Skip to content

Per-User State in Annotations: Camera, Visibility Toggles #8542

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 113 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
c74a9d0
WIP: Per-User State in Annotations: Camera, Visibility Toggles
fm3 Apr 17, 2025
b782542
add new update actions to backend (not all correctly applied yet)
fm3 Apr 22, 2025
4cbfe89
Merge branch 'master' into annotation-user-state
fm3 Apr 22, 2025
6ae0f66
include userState in skeleton+volume proto
fm3 Apr 22, 2025
399f5d0
wip apply on user state
fm3 Apr 22, 2025
d014207
renamings
fm3 Apr 22, 2025
e8d0b80
snapshots
fm3 Apr 22, 2025
06c8f00
Merge branch 'master' into annotation-user-state
fm3 Apr 24, 2025
b3e5f7a
rename userState to userStates in proto; implement TreeGroupsExpanded…
fm3 Apr 24, 2025
208eab9
more user state update actions
fm3 Apr 24, 2025
c564f1d
remaining actions
fm3 Apr 24, 2025
66acb6b
Merge branch 'master' into annotation-user-state
fm3 Apr 28, 2025
4c5d4c2
wip render user state in annotation download
fm3 Apr 28, 2025
a1f83a3
apply user state first, then write to nml
fm3 Apr 28, 2025
1371226
fix rendering userstate
fm3 Apr 29, 2025
9f29cc6
refresh snapshots
fm3 Apr 29, 2025
860a90a
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto May 8, 2025
f9c6978
wip: integrate updateUserStateSkeleton update action
philippotto May 8, 2025
bf36dde
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto May 9, 2025
955f291
fix type errors
philippotto May 9, 2025
9d9a568
remove actionTracingId from UpdateCameraAnnotationAction
fm3 May 12, 2025
f09c969
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 12, 2025
d366aec
integrate UpdateUserStateSkeletonAction and UpdateCameraAnnotationAct…
philippotto May 12, 2025
753a53d
add new updateActiveSegmentId volumeAction
fm3 May 12, 2025
b1f971d
fix tests (mostly)
philippotto May 12, 2025
01c863f
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 12, 2025
e831332
Revert "add new updateActiveSegmentId volumeAction"
fm3 May 13, 2025
121010e
replace updateVolumeTracing with updateActiveSegmentId and updateUser…
philippotto May 13, 2025
93f78b8
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 13, 2025
938a721
fix type errors
philippotto May 13, 2025
ed1ba5d
fix test
philippotto May 13, 2025
b5c2170
more fixes (compaction, tests) and mark legacy update actions
philippotto May 13, 2025
42c3c87
unify UpdateUserStateInSkeletonTracingUpdateAction name
philippotto May 13, 2025
361100b
integrate updateSegmentGroupsExpandedState
philippotto May 13, 2025
656c671
generalize mapGroupsDeep and move to utils
philippotto May 13, 2025
a2e5ab6
also integrate updateTreeGroupsExpandedState and refactor
philippotto May 13, 2025
d8a66db
read isVisible for trees from user state
philippotto May 13, 2025
2522bf2
integrate updateUserBoundingBoxVisibilityInSkeletonTracing etc; refactor
philippotto May 14, 2025
d6758a8
Merge branch 'master' into annotation-user-state
fm3 May 15, 2025
171244d
rename actions to updateActiveNode, updateActiveSegmentId
fm3 May 15, 2025
b560d09
fix applying bbox visibility actions on user state
fm3 May 15, 2025
89ee6b7
WIP handle user states on layer precedence change
fm3 May 15, 2025
599e24c
fix layer precedence logic, cleanup
fm3 May 15, 2025
84213e2
rename case class
fm3 May 15, 2025
d2312ee
adapt to new updateActiveSegmentId and updateActiveNode actions
philippotto May 15, 2025
dd065b8
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 15, 2025
0482611
improve legacy actions
philippotto May 15, 2025
007fd2e
properly select the correct user state instead of always choosing the…
philippotto May 15, 2025
a918da4
fix that camera was always updated if skeleton was changed
philippotto May 15, 2025
56918dd
cherry-pick 28bdc0e3c2891fc8abfbe91eb2cdcc30b9e2837c and tear down pu…
philippotto May 15, 2025
f84d3a3
improve isolation between integration tests
philippotto May 15, 2025
ef8b683
remove empty user states etc from fixtures
philippotto May 15, 2025
130eb94
fix that camera related update actions where also being sent when all…
philippotto May 15, 2025
7fd9bd9
also snapshot explorational
philippotto May 19, 2025
6c34abd
fix e2e test
philippotto May 20, 2025
b302b55
update snapshot
philippotto May 20, 2025
48bce73
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto May 20, 2025
fb61463
resolve backend merge conflicts
fm3 May 20, 2025
5498c3c
format
philippotto May 20, 2025
b1bb98f
add segment visibilities to user state, update it accordingly
fm3 May 20, 2025
3f3775a
stub for merging user states
fm3 May 20, 2025
b47df57
re-add obsolete properties for backwards compatibility and properly u…
philippotto May 20, 2025
404972a
integrate segment visibilities from user state
philippotto May 20, 2025
153f6bd
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 20, 2025
171038d
fix unused import
philippotto May 20, 2025
e620ef9
WIP merge user states when merging annotations
fm3 May 21, 2025
6611e51
map bbox ids too. WIP merge volume user state
fm3 May 21, 2025
7de24b3
re-enable merging user state for volume; skip labelMaps if there aren…
fm3 May 21, 2025
da4ebe2
pass user info to duplicate / merge routes in tracingstore for adapti…
fm3 May 22, 2025
8cebd27
Merge branch 'master' into annotation-user-state
fm3 May 22, 2025
b3e11e3
adapt user states in duplicate
fm3 May 22, 2025
a03611d
clean up todo comments
fm3 May 22, 2025
e0d2d93
fix lookup, unify naming A/B vs source/target
fm3 May 22, 2025
2c9bfbd
pass user ids to duplicate annotatio route
fm3 May 22, 2025
27077eb
remove unued default, make segment list distinct by id after merge
fm3 May 22, 2025
6ff8900
render user states before merging annotations
fm3 May 26, 2025
78582c7
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto May 26, 2025
a569b08
pass requesting user id + owner id in all duplicate cases
fm3 May 26, 2025
f7ad672
fix up some merge related issues
philippotto May 26, 2025
0d3a78c
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
philippotto May 26, 2025
6ed6092
add lookup defaults for user state id maps; resolve todos
fm3 May 26, 2025
bfac846
Merge branch 'annotation-user-state' of github.com:scalableminds/webk…
fm3 May 26, 2025
4d66b11
extract into trait; wip unit tests
fm3 May 26, 2025
e1d3fab
some more tests
fm3 May 26, 2025
995834c
move more functions to trait, add more unit tests
fm3 May 26, 2025
9f72f5b
changelog
fm3 May 26, 2025
cd504ba
implement coderabbit feedback
fm3 May 26, 2025
59a9800
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto May 27, 2025
0bb0ced
incorporate new bbox into user state aware diffing
philippotto May 27, 2025
e16ac25
fix incorrect skeleton update actions in volume case
philippotto May 27, 2025
62a26a4
misc improvements in version restore view
philippotto May 27, 2025
0f613fd
fix incorrect boolean[] declaration
philippotto May 27, 2025
261ef4a
add more legacy comments
philippotto May 27, 2025
7ea2fd3
implement and use a safeZipObject util function
philippotto May 27, 2025
02aa7c9
linting
philippotto May 27, 2025
a835b38
incorporate feedback
philippotto May 30, 2025
17c31bf
incorporate feedback
philippotto May 30, 2025
6171b50
Implement backend PR feedback part 1
fm3 Jun 2, 2025
24ab140
Merge branch 'master' into annotation-user-state
fm3 Jun 2, 2025
531a950
WIP: save id-to-bool per element
fm3 Jun 2, 2025
d8da42f
iterate on making the id-to-bools element-wise
fm3 Jun 2, 2025
161e548
also migrate volume update actions
fm3 Jun 2, 2025
537cf11
add comment
fm3 Jun 2, 2025
7faf719
Merge branch 'master' into annotation-user-state
fm3 Jun 2, 2025
95c140e
adapt backend unit tests
fm3 Jun 2, 2025
5b83825
naming things
fm3 Jun 2, 2025
ab64ba3
fix imports in test
fm3 Jun 2, 2025
5a5a6ef
adapt front-end user states to new proto format
philippotto Jun 4, 2025
c16382f
fix incorrectly assumed proto format
philippotto Jun 4, 2025
c5812aa
test Set().difference in browser_feature_check.tsx
philippotto Jun 4, 2025
b2310fe
mention exposed user list in sharing modal
philippotto Jun 4, 2025
af847b0
Merge branch 'master' of github.com:scalableminds/webknossos into ann…
philippotto Jun 4, 2025
e756def
fix segment group ids when merging volume tracings
fm3 Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Added the possibility for super users to retry manually cancelled jobs from the jobs list. [#8629](https://github.com/scalableminds/webknossos/pull/8629)
- Added checkboxes to the segments tab that allow to show/hide individual segments. The visibility of segments that are not listed in the segments list can be controlled with a new "Hide unlisted segments" toggle in the layer settings. Additionally, you can right-click a segment in a data viewport and select "Only show this segment" (and similar functionality). [#8546](https://github.com/scalableminds/webknossos/pull/8546)
- Instead of pasting a dataset position from the clipboard to the position input box, you can simply paste it without focussing the position input first. Furthermore, you can also paste a "hash string", such as `#1406,1794,1560,0,0.234,186`, as it can be found in WK URLs. Pasting such a string will also set the encoded zoom, rotation, viewport etc. Note that the `#` has to be included in the pasted text. You can also copy and paste an entire link, but note that the dataset or annotation id in the link will be ignored. [#8652](https://github.com/scalableminds/webknossos/pull/8652)
- In shared annotations with multiple authors, some changes are now stored per user. This means that other users won’t see all those changes if their own diverge. This includes the current position and zoom, visibilities of trees, bounding boxes, and segments (as specified with the checkboxes in the lists), as well as which groups are expanded in the lists. The annotation owner’s user state is used as a fallback for users who haven’t explicitly changed these values themselves. [#8542](https://github.com/scalableminds/webknossos/pull/8542)

### Changed
- Remove `data.maybe` dependency and replaced with regular Typescript types. [#8563](https://github.com/scalableminds/webknossos/pull/8563)
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/AnnotationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ class AnnotationController @Inject()(
newAnnotationProto <- tracingStoreClient.duplicateAnnotation(
annotation._id,
newAnnotationId,
annotation._user,
user._id,
version = None,
isFromTask = annotation._task.isDefined,
datasetBoundingBox = dataSource.map(_.boundingBox)
Expand Down
20 changes: 13 additions & 7 deletions app/controllers/AnnotationIOController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ class AnnotationIOController @Inject()(

private def downloadExplorational(annotationId: ObjectId,
typ: String,
issuingUser: Option[User],
requestingUser: Option[User],
version: Option[Long],
skipVolumeData: Boolean,
volumeDataZipFormat: VolumeDataZipFormat)(implicit ctx: DBAccessContext) = {
Expand All @@ -425,10 +425,12 @@ class AnnotationIOController @Inject()(
tracingStoreClient <- tracingStoreService.clientFor(dataset)
fetchedAnnotationLayers <- Fox.serialCombined(annotation.skeletonAnnotationLayers)(
tracingStoreClient.getSkeletonTracing(annotation._id, _, version))
annotationProto <- tracingStoreClient.getAnnotationProto(annotation._id, version)
user <- userService.findOneCached(annotation._user)(GlobalAccessContext)
taskOpt <- Fox.runOptional(annotation._task)(taskDAO.findOne)
nmlStream = nmlWriter.toNmlStream(
"temp",
annotationProto,
fetchedAnnotationLayers,
Some(annotation),
dataset.voxelSize,
Expand All @@ -437,10 +439,11 @@ class AnnotationIOController @Inject()(
conf.Http.uri,
dataset.name,
dataset._id,
Some(user),
user,
taskOpt,
skipVolumeData,
volumeDataZipFormat
volumeDataZipFormat,
requestingUser
)
nmlTemporaryFile = tempFileService.create()
temporaryFileStream = new BufferedOutputStream(new FileOutputStream(new File(nmlTemporaryFile.toString)))
Expand Down Expand Up @@ -469,8 +472,10 @@ class AnnotationIOController @Inject()(
} ?~> "annotation.download.fetchSkeletonLayer.failed"
user <- userService.findOneCached(annotation._user)(GlobalAccessContext) ?~> "annotation.download.findUser.failed"
taskOpt <- Fox.runOptional(annotation._task)(taskDAO.findOne(_)(GlobalAccessContext)) ?~> "task.notFound"
annotationProto <- tracingStoreClient.getAnnotationProto(annotation._id, version)
nmlStream = nmlWriter.toNmlStream(
name,
annotationProto,
fetchedSkeletonLayers ::: fetchedVolumeLayers,
Some(annotation),
dataset.voxelSize,
Expand All @@ -479,10 +484,11 @@ class AnnotationIOController @Inject()(
conf.Http.uri,
dataset.name,
dataset._id,
Some(user),
user,
taskOpt,
skipVolumeData,
volumeDataZipFormat
volumeDataZipFormat,
requestingUser
)
temporaryFile = tempFileService.create()
zipper = ZipIO.startZip(new BufferedOutputStream(new FileOutputStream(new File(temporaryFile.toString))))
Expand Down Expand Up @@ -520,13 +526,13 @@ class AnnotationIOController @Inject()(
zipMimeType

for {
annotation <- provider.provideAnnotation(typ, annotationId, issuingUser) ~> NOT_FOUND
annotation <- provider.provideAnnotation(typ, annotationId, requestingUser) ~> NOT_FOUND
restrictions <- provider.restrictionsFor(typ, annotationId) ?~> "annotation.restrictions.unavailable"
name <- provider.nameFor(annotation) ?~> "annotation.name.impossible"
fileExtension = exportExtensionForAnnotation(annotation)
fileName = name + fileExtension
mimeType = exportMimeTypeForAnnotation(annotation)
_ <- restrictions.allowDownload(issuingUser) ?~> "annotation.download.notAllowed" ~> FORBIDDEN
_ <- restrictions.allowDownload(requestingUser) ?~> "annotation.download.notAllowed" ~> FORBIDDEN
dataset <- datasetDAO.findOne(annotation._dataset)(GlobalAccessContext) ?~> "dataset.notFoundForAnnotation" ~> NOT_FOUND
organization <- organizationDAO.findOne(dataset._organization)(GlobalAccessContext) ?~> "organization.notFound" ~> NOT_FOUND
temporaryFile <- annotationToTemporaryFile(dataset, annotation, name, organization._id) ?~> "annotation.writeTemporaryFile.failed"
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/TaskController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class TaskController @Inject()(taskCreationService: TaskCreationService,
taskParametersFull <- taskCreationService.createTracingsFromBaseAnnotations(taskParametersWithIds,
taskType,
dataset,
dataSource)
dataSource,
request.identity._id)
skeletonBaseOpts: List[Option[SkeletonTracing]] = taskCreationService.createTaskSkeletonTracingBases(
taskParametersFull)
volumeBaseOpts: List[Option[(VolumeTracing, Option[File])]] <- taskCreationService.createTaskVolumeTracingBases(
Expand Down
74 changes: 68 additions & 6 deletions app/models/annotation/AnnotationLayerPrecedence.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package models.annotation

import com.scalableminds.util.objectid.ObjectId
import com.scalableminds.util.tools.{Fox, FoxImplicits}
import com.scalableminds.webknossos.datastore.SkeletonTracing.SkeletonTracing
import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing
import com.scalableminds.webknossos.datastore.SkeletonTracing.{SkeletonTracing, SkeletonUserStateProto}
import com.scalableminds.webknossos.datastore.VolumeTracing.{VolumeTracing, VolumeUserStateProto}
import com.scalableminds.webknossos.datastore.geometry.{
AdditionalCoordinateProto,
NamedBoundingBoxProto,
Vec3DoubleProto,
Vec3IntProto
}
import com.scalableminds.webknossos.datastore.helpers.SkeletonTracingDefaults
import com.scalableminds.webknossos.datastore.models.annotation.{
AnnotationLayer,
AnnotationLayerType,
Expand All @@ -27,6 +28,7 @@ case class RedundantTracingProperties(
zoomLevel: Double,
userBoundingBoxes: Seq[NamedBoundingBoxProto],
editPositionAdditionalCoordinates: Seq[AdditionalCoordinateProto],
userStateBoundingBoxVisibilities: Map[String, (Seq[Int], Seq[Boolean])] // UserId → (bboxIds bboxVisibleStates)
)

trait AnnotationLayerPrecedence extends FoxImplicits {
Expand Down Expand Up @@ -54,7 +56,8 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
editRotation = p.editRotation,
zoomLevel = p.zoomLevel,
userBoundingBoxes = p.userBoundingBoxes,
editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates
editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates,
userStates = adaptSkeletonUserStates(skeletonTracing.userStates, p)
)
}.getOrElse(skeletonTracing)

Expand All @@ -66,10 +69,63 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
editRotation = p.editRotation,
zoomLevel = p.zoomLevel,
userBoundingBoxes = p.userBoundingBoxes,
editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates
editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates,
userStates = adaptVolumeUserStates(volumeTracing.userStates, p)
)
}.getOrElse(volumeTracing)

private def adaptSkeletonUserStates(
userStates: Seq[SkeletonUserStateProto],
oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[SkeletonUserStateProto] = {
val adaptedExistingUserStates = userStates.map { userState =>
val userId = userState.userId
oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match {
case None => userState
case Some((precedenceBboxIds, precedenceBboxVisibilities)) =>
userState.copy(boundingBoxIds = precedenceBboxIds, boundingBoxVisibilities = precedenceBboxVisibilities)
}
}
// We also have to create new user states for the users the old precedence layer has, but the new precedence layer is missing.
val newUserPrecedenceProperties = oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.filter(tuple =>
!userStates.exists(_.userId == tuple._1))
val newUserStates = newUserPrecedenceProperties.map {
case (userId: String, (boundingBoxIds: Seq[Int], boundingBoxVisibilities: Seq[Boolean])) =>
SkeletonTracingDefaults
.emptyUserState(userId)
.copy(
boundingBoxIds = boundingBoxIds,
boundingBoxVisibilities = boundingBoxVisibilities
)
}
adaptedExistingUserStates ++ newUserStates
}

private def adaptVolumeUserStates(
userStates: Seq[VolumeUserStateProto],
oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[VolumeUserStateProto] = {
val adaptedExistingUserStates = userStates.map { userState =>
val userId = userState.userId
oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match {
case None => userState
case Some((precedenceBboxIds, precedenceBboxVisibilities)) =>
userState.copy(boundingBoxIds = precedenceBboxIds, boundingBoxVisibilities = precedenceBboxVisibilities)
}
}
// We also have to create new user states for the users the old precedence layer has, but the new precedence layer is missing.
val newUserPrecedenceProperties = oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.filter(tuple =>
!userStates.exists(_.userId == tuple._1))
val newUserStates = newUserPrecedenceProperties.map {
case (userId: String, (boundingBoxIds: Seq[Int], boundingBoxVisibilities: Seq[Boolean])) =>
VolumeTracingDefaults
.emptyUserState(userId)
.copy(
boundingBoxIds = boundingBoxIds,
boundingBoxVisibilities = boundingBoxVisibilities
)
}
adaptedExistingUserStates ++ newUserStates
}

protected def getOldPrecedenceLayerProperties(existingAnnotationId: Option[ObjectId],
existingAnnotationLayers: List[AnnotationLayer],
previousVersion: Option[Long],
Expand Down Expand Up @@ -138,7 +194,10 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
s.zoomLevel,
s.userBoundingBoxes ++ s.userBoundingBox.map(
com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)),
s.editPositionAdditionalCoordinates
s.editPositionAdditionalCoordinates,
s.userStates
.map(userState => (userState.userId, (userState.boundingBoxIds, userState.boundingBoxVisibilities)))
.toMap
)
case Right(v) =>
RedundantTracingProperties(
Expand All @@ -147,7 +206,10 @@ trait AnnotationLayerPrecedence extends FoxImplicits {
v.zoomLevel,
v.userBoundingBoxes ++ v.userBoundingBox.map(
com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)),
v.editPositionAdditionalCoordinates
v.editPositionAdditionalCoordinates,
v.userStates
.map(userState => (userState.userId, (userState.boundingBoxIds, userState.boundingBoxVisibilities)))
.toMap
)
}
}
12 changes: 10 additions & 2 deletions app/models/annotation/AnnotationMerger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ class AnnotationMerger @Inject()(datasetDAO: DatasetDAO, tracingStoreService: Tr
Fox.empty
else {
for {
mergedAnnotationLayers <- mergeAnnotationsInTracingstore(annotations, datasetId, newId, toTemporaryStore) ?~> "Failed to merge annotations in tracingstore."
mergedAnnotationLayers <- mergeAnnotationsInTracingstore(
annotations,
datasetId,
newId,
userId,
toTemporaryStore) ?~> "Failed to merge annotations in tracingstore."
} yield {
Annotation(
newId,
Expand All @@ -64,13 +69,16 @@ class AnnotationMerger @Inject()(datasetDAO: DatasetDAO, tracingStoreService: Tr
annotations: List[Annotation],
datasetId: ObjectId,
newAnnotationId: ObjectId,
requestingUserId: ObjectId,
toTemporaryStore: Boolean)(implicit ctx: DBAccessContext): Fox[List[AnnotationLayer]] =
for {
dataset <- datasetDAO.findOne(datasetId)
tracingStoreClient: WKRemoteTracingStoreClient <- tracingStoreService.clientFor(dataset)
mergedAnnotationProto <- tracingStoreClient.mergeAnnotationsByIds(annotations.map(_.id),
annotations.map(_._user),
newAnnotationId,
toTemporaryStore)
toTemporaryStore,
requestingUserId)
layers = mergedAnnotationProto.annotationLayers.map(AnnotationLayer.fromProto)
} yield layers.toList

Expand Down
10 changes: 8 additions & 2 deletions app/models/annotation/AnnotationService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ class AnnotationService @Inject()(
duplicatedAnnotationProto <- tracingStoreClient.duplicateAnnotation(
annotationBaseId,
initializingAnnotationId,
annotationBase._user,
user._id,
version = None,
isFromTask = false, // isFromTask is when duplicate is called on a task annotation, not when a task is assigned
datasetBoundingBox = None
Expand Down Expand Up @@ -531,8 +533,11 @@ class AnnotationService @Inject()(
volumeTracingIdOpt,
skeletonTracingOpt,
volumeTracingOpt)
// user state is not used in compound download, so the annotationProto can be a dummy one.
annotationProto = AnnotationProto("", 0L, Seq.empty, 0L)
nml = nmlWriter.toNmlStream(
name,
annotationProto,
fetchedAnnotationLayersForAnnotation,
Some(annotation),
voxelSizeOpt,
Expand All @@ -541,10 +546,11 @@ class AnnotationService @Inject()(
conf.Http.uri,
datasetName,
datasetId,
Some(user),
user,
taskOpt,
skipVolumeData,
volumeDataZipFormat
volumeDataZipFormat,
requestingUser = None
)
} yield (nml, volumeDataOpt)
}
Expand Down
Loading
Loading