Skip to content

Commit 14dbb8a

Browse files
authored
Fix duplicating compound volume annotations (#8743)
The tracingstore needs to be able to get the datasource for an annotation in order to duplicate it. This is for fallback layer handling and has been introduced in its current form in #8405 The wk side answers this with either postgres (stored annotations) or a special temporary store (for temporary annotations). This PR includes compound annotations in this handling for temporary annotations. ### URL of deployed dev instance (used for testing): - https://fixduplicatecompound.webknossos.xyz ### Steps to test: - Create volume task type, project, tasks - Trace some - View compound annotation - hit duplicate - Should result in a usable explorative annotation. ### Issues: - fixes #8736 ------ - [x] Added changelog entry (create a `$PR_NUMBER.md` file in `unreleased_changes` or use `./tools/create-changelog-entry.py`) - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) - [x] Needs datastore update after deployment
1 parent 7b90571 commit 14dbb8a

8 files changed

+65
-25
lines changed

app/models/annotation/AnnotationDataSourceTemporaryStore.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import scala.concurrent.duration.DurationInt
1111
/**
1212
* Used to store a mapping from annotation id to datasource. This makes it possible for WK to answer a
1313
* /tracingstores/:name/dataSource request before an annotation is created. This happens when uploading an annotation.
14+
* It also provides a mapping from temporary/compound annotation id (e.g. taskTypeId, projectId) to datasource
1415
*/
1516
class AnnotationDataSourceTemporaryStore @Inject()(temporaryStore: TemporaryStore[ObjectId, DataSourceLike]) {
1617

app/models/annotation/handler/AnnotationInformationHandler.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package models.annotation.handler
22

3-
import com.scalableminds.util.accesscontext.DBAccessContext
3+
import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
44
import com.scalableminds.util.tools.{Fox, FoxImplicits}
5+
56
import javax.inject.Inject
67
import models.annotation.AnnotationType.AnnotationType
78
import models.annotation._
89
import models.user.User
910
import com.scalableminds.util.objectid.ObjectId
11+
import models.dataset.{DatasetDAO, DatasetService}
1012

1113
import scala.annotation.{nowarn, tailrec}
1214
import scala.concurrent.ExecutionContext
@@ -24,6 +26,10 @@ class AnnotationInformationHandlerSelector @Inject()(projectInformationHandler:
2426

2527
trait AnnotationInformationHandler extends FoxImplicits {
2628

29+
def datasetDAO: DatasetDAO
30+
def datasetService: DatasetService
31+
def annotationDataSourceTemporaryStore: AnnotationDataSourceTemporaryStore
32+
2733
implicit val ec: ExecutionContext
2834

2935
def cache: Boolean = true
@@ -43,6 +49,7 @@ trait AnnotationInformationHandler extends FoxImplicits {
4349
case List() => true
4450
case head :: tail => head._dataset == datasetId && allOnSameDatasetIter(tail, datasetId)
4551
}
52+
4653
annotations match {
4754
case List() => Fox.successful(true)
4855
case head :: _ =>
@@ -56,4 +63,11 @@ trait AnnotationInformationHandler extends FoxImplicits {
5663
def assertNonEmpty[T](seq: List[T]): Fox[Unit] =
5764
if (seq.isEmpty) Fox.failure("no annotations")
5865
else Fox.successful(())
66+
67+
protected def registerDataSourceInTemporaryStore(temporaryAnnotationId: ObjectId, datasetId: ObjectId): Fox[Unit] =
68+
for {
69+
dataset <- datasetDAO.findOne(datasetId)(GlobalAccessContext) ?~> "dataset.notFoundForAnnotation"
70+
dataSource <- datasetService.dataSourceFor(dataset).flatMap(_.toUsable.toFox) ?~> "dataset.dataSource.notUsable"
71+
_ = annotationDataSourceTemporaryStore.store(temporaryAnnotationId, dataSource)
72+
} yield ()
5973
}

app/models/annotation/handler/ProjectInformationHandler.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@ import models.annotation._
88
import models.project.ProjectDAO
99
import models.user.{User, UserService}
1010
import com.scalableminds.util.objectid.ObjectId
11+
import models.dataset.{DatasetDAO, DatasetService}
1112
import models.task.TaskDAO
1213

1314
import scala.concurrent.ExecutionContext
1415

15-
class ProjectInformationHandler @Inject()(annotationDAO: AnnotationDAO,
16-
projectDAO: ProjectDAO,
17-
taskDAO: TaskDAO,
18-
userService: UserService,
19-
annotationMerger: AnnotationMerger)(implicit val ec: ExecutionContext)
16+
class ProjectInformationHandler @Inject()(
17+
annotationDAO: AnnotationDAO,
18+
projectDAO: ProjectDAO,
19+
userService: UserService,
20+
annotationMerger: AnnotationMerger,
21+
taskDAO: TaskDAO,
22+
val datasetService: DatasetService,
23+
val datasetDAO: DatasetDAO,
24+
val annotationDataSourceTemporaryStore: AnnotationDataSourceTemporaryStore)(implicit val ec: ExecutionContext)
2025
extends AnnotationInformationHandler
2126
with FoxImplicits {
2227

@@ -30,6 +35,7 @@ class ProjectInformationHandler @Inject()(annotationDAO: AnnotationDAO,
3035
_ <- assertAllOnSameDataset(annotations)
3136
_ <- assertNonEmpty(annotations) ?~> "project.noAnnotations"
3237
datasetId <- annotations.headOption.map(_._dataset).toFox
38+
_ <- registerDataSourceInTemporaryStore(projectId, datasetId)
3339
taskBoundingBoxes <- taskDAO.findTaskBoundingBoxesByAnnotationIds(annotations.map(_._id))
3440
mergedAnnotation <- annotationMerger.mergeN(projectId,
3541
toTemporaryStore = true,

app/models/annotation/handler/SavedTracingInformationHandler.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@ import com.scalableminds.util.tools.{Fox, FoxImplicits}
77

88
import javax.inject.Inject
99
import models.annotation._
10-
import models.dataset.DatasetDAO
10+
import models.dataset.{DatasetDAO, DatasetService}
1111
import models.user.{User, UserService}
1212
import com.scalableminds.util.objectid.ObjectId
1313

1414
import scala.concurrent.ExecutionContext
1515

16-
class SavedTracingInformationHandler @Inject()(annotationDAO: AnnotationDAO,
17-
datasetDAO: DatasetDAO,
18-
annotationRestrictionDefults: AnnotationRestrictionDefaults,
19-
userService: UserService)(implicit val ec: ExecutionContext)
16+
class SavedTracingInformationHandler @Inject()(
17+
annotationDAO: AnnotationDAO,
18+
annotationRestrictionDefults: AnnotationRestrictionDefaults,
19+
userService: UserService,
20+
val datasetService: DatasetService,
21+
val datasetDAO: DatasetDAO,
22+
val annotationDataSourceTemporaryStore: AnnotationDataSourceTemporaryStore)(implicit val ec: ExecutionContext)
2023
extends AnnotationInformationHandler
2124
with Formatter
2225
with FoxImplicits {

app/models/annotation/handler/TaskInformationHandler.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,27 @@ package models.annotation.handler
22

33
import com.scalableminds.util.accesscontext.DBAccessContext
44
import com.scalableminds.util.tools.{Fox, FoxImplicits}
5+
56
import javax.inject.Inject
67
import models.annotation._
78
import models.task.TaskDAO
89
import models.user.{User, UserService}
910
import models.annotation.AnnotationState._
1011
import models.project.ProjectDAO
1112
import com.scalableminds.util.objectid.ObjectId
13+
import models.dataset.{DatasetDAO, DatasetService}
1214

1315
import scala.concurrent.ExecutionContext
1416

15-
class TaskInformationHandler @Inject()(taskDAO: TaskDAO,
16-
annotationDAO: AnnotationDAO,
17-
userService: UserService,
18-
annotationMerger: AnnotationMerger,
19-
projectDAO: ProjectDAO)(implicit val ec: ExecutionContext)
17+
class TaskInformationHandler @Inject()(
18+
taskDAO: TaskDAO,
19+
annotationDAO: AnnotationDAO,
20+
userService: UserService,
21+
annotationMerger: AnnotationMerger,
22+
projectDAO: ProjectDAO,
23+
val datasetService: DatasetService,
24+
val datasetDAO: DatasetDAO,
25+
val annotationDataSourceTemporaryStore: AnnotationDataSourceTemporaryStore)(implicit val ec: ExecutionContext)
2026
extends AnnotationInformationHandler
2127
with FoxImplicits {
2228

@@ -31,6 +37,7 @@ class TaskInformationHandler @Inject()(taskDAO: TaskDAO,
3137
user <- userOpt.toFox ?~> "user.notAuthorised"
3238
project <- projectDAO.findOne(task._project)
3339
datasetId <- finishedAnnotations.headOption.map(_._dataset).toFox
40+
_ <- registerDataSourceInTemporaryStore(taskId, datasetId)
3441
taskBoundingBoxes <- taskDAO.findTaskBoundingBoxesByAnnotationIds(annotations.map(_._id))
3542
mergedAnnotation <- annotationMerger.mergeN(task._id,
3643
toTemporaryStore = true,

app/models/annotation/handler/TaskTypeInformationHandler.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,26 @@ package models.annotation.handler
22

33
import com.scalableminds.util.accesscontext.DBAccessContext
44
import com.scalableminds.util.tools.{Fox, FoxImplicits}
5+
56
import javax.inject.Inject
67
import models.annotation._
78
import models.task.{TaskDAO, TaskTypeDAO}
89
import models.user.{User, UserService}
910
import models.annotation.AnnotationState._
1011
import com.scalableminds.util.objectid.ObjectId
12+
import models.dataset.{DatasetDAO, DatasetService}
1113

1214
import scala.concurrent.ExecutionContext
1315

14-
class TaskTypeInformationHandler @Inject()(taskTypeDAO: TaskTypeDAO,
15-
taskDAO: TaskDAO,
16-
userService: UserService,
17-
annotationDAO: AnnotationDAO,
18-
annotationMerger: AnnotationMerger)(implicit val ec: ExecutionContext)
16+
class TaskTypeInformationHandler @Inject()(
17+
taskTypeDAO: TaskTypeDAO,
18+
taskDAO: TaskDAO,
19+
userService: UserService,
20+
annotationDAO: AnnotationDAO,
21+
annotationMerger: AnnotationMerger,
22+
val datasetService: DatasetService,
23+
val datasetDAO: DatasetDAO,
24+
val annotationDataSourceTemporaryStore: AnnotationDataSourceTemporaryStore)(implicit val ec: ExecutionContext)
1925
extends AnnotationInformationHandler
2026
with FoxImplicits {
2127

@@ -32,6 +38,7 @@ class TaskTypeInformationHandler @Inject()(taskTypeDAO: TaskTypeDAO,
3238
_ <- assertNonEmpty(finishedAnnotations) ?~> "taskType.noAnnotations"
3339
user <- userOpt.toFox ?~> "user.notAuthorised"
3440
datasetId <- finishedAnnotations.headOption.map(_._dataset).toFox
41+
_ <- registerDataSourceInTemporaryStore(taskTypeId, datasetId)
3542
taskBoundingBoxes <- taskDAO.findTaskBoundingBoxesByAnnotationIds(annotations.map(_._id))
3643
mergedAnnotation <- annotationMerger.mergeN(taskTypeId,
3744
toTemporaryStore = true,

conf/messages

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ user.configuration.dataset.invalid=Dataset configuration is invalid
6767
user.configuration.dataset.updated=Dataset configuration updated
6868
user.email.invalid=At least one Email is invalid
6969
user.notAuthorised=You are not authorized to view this resource. Please log in.
70-
user.id.notFound=We could not find a user id in the request.
70+
user.id.notFound=Could not find a user id in the request.
7171
user.id.invalid=The provided user id is invalid.
7272
user.creation.failed=Failed to create user
7373
user.team.memberships.failed=Failed to retrieve team memberships for user
@@ -177,7 +177,7 @@ project.remove.success=Project was removed successfully
177177
project.remove.failure=Project could not be removed
178178
project.notFound=Project could not be found
179179
project.update.failed=Project update failed
180-
project.noAnnotations=We could not find annotations for this project
180+
project.noAnnotations=Could not find annotations for this project
181181
project.increaseTaskInstances.negative=Cannot increment task counts by negative number
182182
project.list.failed=Failed to retrieve list of projects.
183183
project.creation.failed=Failed to create project.
@@ -283,7 +283,7 @@ task.notFound=Task could not be found
283283
task.removed=Task was removed
284284
task.remove.failed=Task removal failed
285285
task.editSuccess=Task successfully edited
286-
task.noAnnotations=We could not find finished annotations for this task
286+
task.noAnnotations=Could not find finished annotations for this task
287287
task.annotation.failed=Failed to retrieve annotations for this task
288288
task.id.invalid=The provided task id is invalid.
289289

@@ -293,7 +293,7 @@ taskType.editSuccess=Task type successfully edited
293293
taskType.notFound=Selected task type does not exist
294294
taskType.deleteSuccess=Task type “{0}” successfully deleted
295295
taskType.deleteFailure=Task type “{0}” deletion failed
296-
taskType.noAnnotations=We could not find finished annotations for this task type
296+
taskType.noAnnotations=Could not find finished annotations for this task type
297297
taskType.id.invalid=The provided task type id is invalid.
298298
taskType.tracingTypeImmutable=Annotation types of task types are immutable. Consider creating a new task type.
299299
taskType.magRestrictionsImmutable=Mag restrictions of task types are immutable. Consider creating a new task type.

unreleased_changes/8743.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
### Fixed
2+
- Fixed a bug where duplicating a compound annotation would fail if it has volume annotation layers.

0 commit comments

Comments
 (0)