Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:dres-dev/DRES into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sauterl committed Sep 22, 2023
2 parents d7d04fe + 2686783 commit e98dbb3
Show file tree
Hide file tree
Showing 27 changed files with 6,991 additions and 7,065 deletions.
2 changes: 1 addition & 1 deletion backend/src/main/kotlin/dev/dres/DRES.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 */)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)


}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -47,16 +47,16 @@ import java.util.*
class GetTaskHintHandler(private val store: TransientEntityStore, private val cache: CacheManager) :
AbstractEvaluationViewerHandler(), GetRestHandler<ApiHintContent> {

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)]),
Expand All @@ -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<InteractiveRunManager>()
if (!manager.runProperties.participantCanView && ctx.isParticipant()) {
throw ErrorStatusException(403, "Access Denied", ctx)
}
val manager = ctx.eligibleManagerForId<RunManager>()

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) {
Expand All @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -39,16 +39,16 @@ import java.util.*
*/
class GetTaskTargetHandler(private val store: TransientEntityStore, private val cache: CacheManager) : AbstractEvaluationViewerHandler(), GetRestHandler<ApiTargetContent> {

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)]),
Expand All @@ -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<InteractiveRunManager>()
if (!manager.runProperties.participantCanView && ctx.isParticipant()) {
throw ErrorStatusException(403, "Access Denied", ctx)
}
val manager = ctx.eligibleManagerForId<RunManager>()

/* 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) {
Expand All @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Long, Long>? = when {
map.containsKey(PARAMETER_NAME_SHOT) && item.type == DbMediaType.VIDEO -> {
val shot = map[PARAMETER_NAME_SHOT]?.first()!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit e98dbb3

Please sign in to comment.