diff --git a/app/controllers/AnnotationController.scala b/app/controllers/AnnotationController.scala index 806ceeba709..093559e5a4d 100755 --- a/app/controllers/AnnotationController.scala +++ b/app/controllers/AnnotationController.scala @@ -17,7 +17,7 @@ import models.annotation.AnnotationState.Cancelled import models.annotation._ import models.dataset.{DatasetDAO, DatasetService} import models.project.ProjectDAO -import models.task.TaskDAO +import models.task.{TaskDAO, TaskService} import models.team.{TeamDAO, TeamService} import models.user.time._ import models.user.{User, UserDAO, UserService} @@ -52,6 +52,7 @@ class AnnotationController @Inject()( tracingStoreService: TracingStoreService, provider: AnnotationInformationProvider, annotationRestrictionDefaults: AnnotationRestrictionDefaults, + taskService: TaskService, analyticsService: AnalyticsService, slackNotificationService: SlackNotificationService, mailchimpClient: MailchimpClient, @@ -158,6 +159,7 @@ class AnnotationController @Inject()( _ = logger.info( s"Reopening annotation $id, new state will be ${AnnotationState.Active.toString}, access context: ${request.identity.toStringAnonymous}") _ <- annotationDAO.updateState(annotation._id, AnnotationState.Active) ?~> "annotation.invalid" + _ <- Fox.runOptional(annotation._task)(taskService.clearCompoundCache) updatedAnnotation <- provider.provideAnnotation(typ, id, request.identity) ~> NOT_FOUND json <- annotationService.publicWrites(updatedAnnotation, Some(request.identity)) ?~> "annotation.write.failed" } yield JsonOk(json, Messages("annotation.reopened")) @@ -298,13 +300,14 @@ class AnnotationController @Inject()( def cancel(typ: String, id: ObjectId): Action[AnyContent] = sil.SecuredAction.async { implicit request => def tryToCancel(annotation: Annotation) = - annotation match { - case t if t.typ == AnnotationType.Task => + (annotation._task, annotation.typ) match { + case (Some(taskId), AnnotationType.Task) => logger.info( - s"Canceling annotation $id, new state will be ${AnnotationState.Cancelled.toString}, access context: ${request.identity.toStringAnonymous}") - annotationDAO.updateState(annotation._id, Cancelled).map { _ => - JsonOk(Messages("task.finished")) - } + s"Canceling task annotation $id, new state will be ${AnnotationState.Cancelled}, access context: ${request.identity.toStringAnonymous}") + for { + _ <- Fox.runIf(annotation.state == AnnotationState.Finished)(taskService.clearCompoundCache(taskId)) + _ <- annotationDAO.updateState(annotation._id, Cancelled) + } yield JsonOk(Messages("task.finished")) case _ => Fox.successful(JsonOk(Messages("annotation.finished"))) } diff --git a/app/models/annotation/AnnotationService.scala b/app/models/annotation/AnnotationService.scala index 54577752334..b8fd30a12ea 100755 --- a/app/models/annotation/AnnotationService.scala +++ b/app/models/annotation/AnnotationService.scala @@ -313,6 +313,7 @@ class AnnotationService @Inject()( for { _ <- annotationDAO.updateModified(annotation._id, Instant.now) _ <- annotationDAO.updateState(annotation._id, AnnotationState.Finished) + _ <- Fox.runOptional(annotation._task)(taskService.clearCompoundCache) } yield { if (annotation._task.isEmpty) "annotation.finished" diff --git a/app/models/annotation/AnnotationStore.scala b/app/models/annotation/AnnotationStore.scala index 617634b1793..3076ffa5a51 100755 --- a/app/models/annotation/AnnotationStore.scala +++ b/app/models/annotation/AnnotationStore.scala @@ -25,7 +25,7 @@ class AnnotationStore @Inject()( private def requestFromCache(id: AnnotationIdentifier): Option[Fox[Annotation]] = { val handler = annotationInformationHandlerSelector.informationHandlers(id.annotationType) - if (handler.cache) { + if (handler.useCache) { val cached = getFromCache(id) cached } else @@ -37,7 +37,7 @@ class AnnotationStore @Inject()( for { annotation <- handler.provideAnnotation(id.identifier, user) } yield { - if (handler.cache) { + if (handler.useCache) { storeInCache(id, annotation) } annotation @@ -60,4 +60,6 @@ class AnnotationStore @Inject()( case None => Empty } } + + def removeFromCache(id: AnnotationIdentifier): Unit = temporaryAnnotationStore.remove(id.toUniqueString) } diff --git a/app/models/annotation/handler/AnnotationInformationHandler.scala b/app/models/annotation/handler/AnnotationInformationHandler.scala index 8db66842b47..7e5974d70e0 100755 --- a/app/models/annotation/handler/AnnotationInformationHandler.scala +++ b/app/models/annotation/handler/AnnotationInformationHandler.scala @@ -32,7 +32,7 @@ trait AnnotationInformationHandler extends FoxImplicits { implicit val ec: ExecutionContext - def cache: Boolean = true + def useCache: Boolean = true def provideAnnotation(identifier: ObjectId, user: Option[User])(implicit ctx: DBAccessContext): Fox[Annotation] diff --git a/app/models/annotation/handler/SavedTracingInformationHandler.scala b/app/models/annotation/handler/SavedTracingInformationHandler.scala index d7b1f26f363..1bd9b7bfd9a 100755 --- a/app/models/annotation/handler/SavedTracingInformationHandler.scala +++ b/app/models/annotation/handler/SavedTracingInformationHandler.scala @@ -24,7 +24,7 @@ class SavedTracingInformationHandler @Inject()( with Formatter with FoxImplicits { - override val cache = false + override val useCache = false override def nameForAnnotation(annotation: Annotation)(implicit ctx: DBAccessContext): Fox[String] = for { diff --git a/app/models/task/TaskService.scala b/app/models/task/TaskService.scala index dcd0d97b4ba..a500d770be3 100644 --- a/app/models/task/TaskService.scala +++ b/app/models/task/TaskService.scala @@ -5,7 +5,7 @@ import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import javax.inject.Inject -import models.annotation.{Annotation, AnnotationDAO, AnnotationType} +import models.annotation.{Annotation, AnnotationDAO, AnnotationIdentifier, AnnotationStore, AnnotationType} import models.dataset.DatasetDAO import models.project.ProjectDAO import models.team.TeamDAO @@ -19,10 +19,12 @@ import scala.concurrent.ExecutionContext class TaskService @Inject()(conf: WkConf, datasetDAO: DatasetDAO, scriptDAO: ScriptDAO, + annotationStore: AnnotationStore, userService: UserService, annotationDAO: AnnotationDAO, taskTypeDAO: TaskTypeDAO, teamDAO: TeamDAO, + taskDAO: TaskDAO, scriptService: ScriptService, taskTypeService: TaskTypeService, projectDAO: ProjectDAO)(implicit ec: ExecutionContext) @@ -88,4 +90,11 @@ class TaskService @Inject()(conf: WkConf, activeCount <- Fox.fromFuture(annotationDAO.countActiveByTask(task._id, AnnotationType.Task).getOrElse(0)) } yield TaskStatus(task.pendingInstances, activeCount, task.totalInstances - (activeCount + task.pendingInstances)) + def clearCompoundCache(taskId: ObjectId): Fox[Unit] = + for { + task <- taskDAO.findOne(taskId)(GlobalAccessContext) + _ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundTask, task._id)) + _ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundProject, task._project)) + _ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundTaskType, task._taskType)) + } yield () } diff --git a/unreleased_changes/8756.md b/unreleased_changes/8756.md new file mode 100644 index 00000000000..98f24e7437d --- /dev/null +++ b/unreleased_changes/8756.md @@ -0,0 +1,2 @@ +### Fixed +- Fixed that compound (merged) views of tasks, projects and task types would sometimes show outdated annotation data, leaving out the data from recently finished tasks annotations.