diff --git a/app/controllers/AuthenticationController.scala b/app/controllers/AuthenticationController.scala index b0da06d0b47..71f3e7bc693 100755 --- a/app/controllers/AuthenticationController.scala +++ b/app/controllers/AuthenticationController.scala @@ -215,7 +215,7 @@ class AuthenticationController @Inject()( } yield result def accessibleBySwitching(datasetId: Option[ObjectId], - annotationId: Option[String], + annotationId: Option[ObjectId], workflowHash: Option[String]): Action[AnyContent] = sil.SecuredAction.async { implicit request => for { diff --git a/app/controllers/WKRemoteTracingStoreController.scala b/app/controllers/WKRemoteTracingStoreController.scala index d01693aaa2f..ae8c65c1481 100644 --- a/app/controllers/WKRemoteTracingStoreController.scala +++ b/app/controllers/WKRemoteTracingStoreController.scala @@ -82,8 +82,7 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore tracingStoreService.validateAccess(name, key) { _ => val report = request.body for { - annotationId <- ObjectId.fromString(report.annotationId) - annotation <- annotationDAO.findOne(annotationId) + annotation <- annotationDAO.findOne(report.annotationId) _ <- ensureAnnotationNotFinished(annotation) _ <- annotationDAO.updateModified(annotation._id, Instant.now) _ = report.statistics.map(statistics => annotationService.updateStatistics(annotation._id, statistics)) diff --git a/app/models/annotation/AnnotationLayerPrecedence.scala b/app/models/annotation/AnnotationLayerPrecedence.scala index c9eb0b0524c..5a660f64bb9 100644 --- a/app/models/annotation/AnnotationLayerPrecedence.scala +++ b/app/models/annotation/AnnotationLayerPrecedence.scala @@ -29,7 +29,7 @@ case class RedundantTracingProperties( zoomLevel: Double, userBoundingBoxes: Seq[NamedBoundingBoxProto], editPositionAdditionalCoordinates: Seq[AdditionalCoordinateProto], - userStateBoundingBoxVisibilities: Map[String, Seq[Id32WithBool]] // UserId → Seq(bboxId, bboxIsVisible) + userStateBoundingBoxVisibilities: Map[ObjectId, Seq[Id32WithBool]] // UserId → Seq(bboxId, bboxIsVisible) ) trait AnnotationLayerPrecedence extends FoxImplicits { @@ -79,7 +79,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits { userStates: Seq[SkeletonUserStateProto], oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[SkeletonUserStateProto] = { val adaptedExistingUserStates = userStates.map { userState => - val userId = userState.userId + val userId = ObjectId(userState.userId) oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match { case None => userState case Some(precedenceBboxVisibilities) => @@ -88,9 +88,9 @@ trait AnnotationLayerPrecedence extends FoxImplicits { } // 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)) + !userStates.exists(_.userId == tuple._1.toString)) val newUserStates = newUserPrecedenceProperties.map { - case (userId: String, boundingBoxVisibilities: Seq[Id32WithBool]) => + case (userId: ObjectId, boundingBoxVisibilities: Seq[Id32WithBool]) => SkeletonTracingDefaults .emptyUserState(userId) .copy( @@ -104,7 +104,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits { userStates: Seq[VolumeUserStateProto], oldPrecedenceLayerProperties: RedundantTracingProperties): Seq[VolumeUserStateProto] = { val adaptedExistingUserStates = userStates.map { userState => - val userId = userState.userId + val userId = ObjectId(userState.userId) oldPrecedenceLayerProperties.userStateBoundingBoxVisibilities.get(userId) match { case None => userState case Some(precedenceBboxVisibilities) => @@ -113,9 +113,9 @@ trait AnnotationLayerPrecedence extends FoxImplicits { } // 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)) + !userStates.exists(_.userId == tuple._1.toString)) val newUserStates = newUserPrecedenceProperties.map { - case (userId: String, boundingBoxVisibilities: Seq[Id32WithBool]) => + case (userId: ObjectId, boundingBoxVisibilities: Seq[Id32WithBool]) => VolumeTracingDefaults .emptyUserState(userId) .copy( @@ -194,7 +194,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits { s.userBoundingBoxes ++ s.userBoundingBox.map( com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)), s.editPositionAdditionalCoordinates, - s.userStates.map(userState => (userState.userId, userState.boundingBoxVisibilities)).toMap + s.userStates.map(userState => (ObjectId(userState.userId), userState.boundingBoxVisibilities)).toMap ) case Right(v) => RedundantTracingProperties( @@ -204,7 +204,7 @@ trait AnnotationLayerPrecedence extends FoxImplicits { v.userBoundingBoxes ++ v.userBoundingBox.map( com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)), v.editPositionAdditionalCoordinates, - v.userStates.map(userState => (userState.userId, userState.boundingBoxVisibilities)).toMap + v.userStates.map(userState => (ObjectId(userState.userId), userState.boundingBoxVisibilities)).toMap ) } } diff --git a/app/models/annotation/AnnotationMerger.scala b/app/models/annotation/AnnotationMerger.scala index c4f75941be2..1bdd1149e50 100644 --- a/app/models/annotation/AnnotationMerger.scala +++ b/app/models/annotation/AnnotationMerger.scala @@ -74,7 +74,7 @@ class AnnotationMerger @Inject()(datasetDAO: DatasetDAO, tracingStoreService: Tr for { dataset <- datasetDAO.findOne(datasetId) tracingStoreClient: WKRemoteTracingStoreClient <- tracingStoreService.clientFor(dataset) - mergedAnnotationProto <- tracingStoreClient.mergeAnnotationsByIds(annotations.map(_.id), + mergedAnnotationProto <- tracingStoreClient.mergeAnnotationsByIds(annotations.map(_._id), annotations.map(_._user), newAnnotationId, toTemporaryStore, diff --git a/app/models/annotation/AnnotationService.scala b/app/models/annotation/AnnotationService.scala index a4300bef5b3..1d1cce9343e 100755 --- a/app/models/annotation/AnnotationService.scala +++ b/app/models/annotation/AnnotationService.scala @@ -801,7 +801,7 @@ class AnnotationService @Inject()( dataStore <- dataStoreDAO.findOneByName(dataset._dataStore.trim) ?~> "datastore.notFound" tracingStore <- tracingStoreDAO.findFirst annotationSource = AnnotationSource( - id = annotation.id, + id = annotation._id, annotationLayers = annotation.annotationLayers, datasetDirectoryName = dataset.directoryName, organizationId = organization._id, diff --git a/app/models/annotation/WKRemoteTracingStoreClient.scala b/app/models/annotation/WKRemoteTracingStoreClient.scala index fc97e218e7c..d2f432aef5a 100644 --- a/app/models/annotation/WKRemoteTracingStoreClient.scala +++ b/app/models/annotation/WKRemoteTracingStoreClient.scala @@ -172,7 +172,7 @@ class WKRemoteTracingStoreClient( .postEmpty() } - def mergeAnnotationsByIds(annotationIds: List[String], + def mergeAnnotationsByIds(annotationIds: List[ObjectId], ownerIds: List[ObjectId], newAnnotationId: ObjectId, toTemporaryStore: Boolean, @@ -183,8 +183,8 @@ class WKRemoteTracingStoreClient( .addQueryString("toTemporaryStore" -> toTemporaryStore.toString) .addQueryString("newAnnotationId" -> newAnnotationId.toString) .addQueryString("requestingUserId" -> requestingUserId.toString) - .postJsonWithProtoResponse[MergedFromIdsRequest, AnnotationProto]( - MergedFromIdsRequest(annotationIds, ownerIds.map(_.toString)))(AnnotationProto) + .postJsonWithProtoResponse[MergedFromIdsRequest, AnnotationProto](MergedFromIdsRequest(annotationIds, ownerIds))( + AnnotationProto) } def mergeSkeletonTracingsByContents(newTracingId: String, tracings: SkeletonTracings): Fox[Unit] = { diff --git a/app/security/AccessibleBySwitchingService.scala b/app/security/AccessibleBySwitchingService.scala index babcffdf5df..4aa6db15626 100644 --- a/app/security/AccessibleBySwitchingService.scala +++ b/app/security/AccessibleBySwitchingService.scala @@ -30,7 +30,7 @@ class AccessibleBySwitchingService @Inject()( def getOrganizationToSwitchTo(user: User, datasetId: Option[ObjectId], - annotationId: Option[String], + annotationId: Option[ObjectId], workflowHash: Option[String])(implicit ctx: DBAccessContext): Fox[Organization] = for { isSuperUser <- multiUserDAO.findOne(user._multiUser).map(_.isSuperUser) @@ -42,7 +42,7 @@ class AccessibleBySwitchingService @Inject()( } yield selectedOrganization private def accessibleBySwitchingForSuperUser(datasetIdOpt: Option[ObjectId], - annotationIdOpt: Option[String], + annotationIdOpt: Option[ObjectId], workflowHashOpt: Option[String]): Fox[Organization] = { implicit val ctx: DBAccessContext = GlobalAccessContext (datasetIdOpt, annotationIdOpt, workflowHashOpt) match { @@ -53,8 +53,7 @@ class AccessibleBySwitchingService @Inject()( } yield organization case (None, Some(annotationId), None) => for { - annotationObjectId <- ObjectId.fromString(annotationId) - annotation <- annotationDAO.findOne(annotationObjectId) // Note: this does not work for compound annotations. + annotation <- annotationDAO.findOne(annotationId) // Note: this does not work for compound annotations. user <- userDAO.findOne(annotation._user) organization <- organizationDAO.findOne(user._organization) } yield organization @@ -69,7 +68,7 @@ class AccessibleBySwitchingService @Inject()( private def accessibleBySwitchingForMultiUser(multiUserId: ObjectId, datasetIdOpt: Option[ObjectId], - annotationIdOpt: Option[String], + annotationIdOpt: Option[ObjectId], workflowHashOpt: Option[String]): Fox[Organization] = for { identities <- userDAO.findAllByMultiUser(multiUserId) @@ -80,7 +79,7 @@ class AccessibleBySwitchingService @Inject()( private def canAccessDatasetOrAnnotationOrWorkflow(user: User, datasetIdOpt: Option[ObjectId], - annotationIdOpt: Option[String], + annotationIdOpt: Option[ObjectId], workflowHashOpt: Option[String]): Fox[Boolean] = { val ctx = AuthorizedAccessContext(user) (datasetIdOpt, annotationIdOpt, workflowHashOpt) match { @@ -99,12 +98,11 @@ class AccessibleBySwitchingService @Inject()( foundFox.shiftBox.map(_.isDefined) } - private def canAccessAnnotation(user: User, ctx: DBAccessContext, annotationId: String): Fox[Boolean] = { + private def canAccessAnnotation(user: User, ctx: DBAccessContext, annotationId: ObjectId): Fox[Boolean] = { val foundFox = for { - annotationIdParsed <- ObjectId.fromString(annotationId) - annotation <- annotationDAO.findOne(annotationIdParsed)(GlobalAccessContext) + annotation <- annotationDAO.findOne(annotationId)(GlobalAccessContext) _ <- Fox.fromBool(annotation.state != Cancelled) - restrictions <- annotationProvider.restrictionsFor(AnnotationIdentifier(annotation.typ, annotationIdParsed))(ctx) + restrictions <- annotationProvider.restrictionsFor(AnnotationIdentifier(annotation.typ, annotationId))(ctx) _ <- restrictions.allowAccess(user) } yield () foundFox.shiftBox.map(_.isDefined) diff --git a/build.sbt b/build.sbt index 18bf21caa65..4eb1bc89d72 100644 --- a/build.sbt +++ b/build.sbt @@ -34,13 +34,10 @@ Compile / console / scalacOptions -= "-Xlint:unused" scapegoatIgnoredFiles := Seq(".*/Tables.scala", ".*/Routes.scala", ".*/.*mail.*template\\.scala") scapegoatDisabledInspections := Seq("FinalModifierOnCaseClass", "UnusedMethodParameter", "UnsafeTraversableMethods") -// Allow path binding for ObjectId -routesImport += "com.scalableminds.util.objectid.ObjectId" - lazy val commonSettings = Seq( resolvers ++= DependencyResolvers.dependencyResolvers, Compile / doc / sources := Seq.empty, - Compile / packageDoc / publishArtifact := false + Compile / packageDoc / publishArtifact := false, ) lazy val protocolBufferSettings = Seq( @@ -92,6 +89,7 @@ lazy val webknossosDatastore = (project in file("webknossos-datastore")) } ((libs +++ subs +++ targets) ** "*.jar").classpath }, + routesImport += "com.scalableminds.util.objectid.ObjectId", copyMessagesFilesSetting ) @@ -102,11 +100,12 @@ lazy val webknossosTracingstore = (project in file("webknossos-tracingstore")) .settings( name := "webknossos-tracingstore", commonSettings, + routesImport += "com.scalableminds.util.objectid.ObjectId", generateReverseRouter := false, BuildInfoSettings.webknossosTracingstoreBuildInfoSettings, libraryDependencies ++= Dependencies.webknossosTracingstoreDependencies, dependencyOverrides ++= Dependencies.dependencyOverrides, - copyMessagesFilesSetting + copyMessagesFilesSetting, ) lazy val webknossos = (project in file(".")) @@ -116,6 +115,7 @@ lazy val webknossos = (project in file(".")) .settings( name := "webknossos", commonSettings, + routesImport += "com.scalableminds.util.objectid.ObjectId", generateReverseRouter := false, AssetCompilation.settings, BuildInfoSettings.webknossosBuildInfoSettings, diff --git a/conf/webknossos.latest.routes b/conf/webknossos.latest.routes index b5cdecaa007..628d92ec71f 100644 --- a/conf/webknossos.latest.routes +++ b/conf/webknossos.latest.routes @@ -28,7 +28,7 @@ GET /auth/token DELETE /auth/token controllers.AuthenticationController.deleteToken() GET /auth/switch controllers.AuthenticationController.switchMultiUser(to: String) POST /auth/switchOrganization/:organizationId controllers.AuthenticationController.switchOrganization(organizationId: String) -GET /auth/accessibleBySwitching controllers.AuthenticationController.accessibleBySwitching(datasetId: Option[ObjectId], annotationId: Option[String], workflowHash: Option[String]) +GET /auth/accessibleBySwitching controllers.AuthenticationController.accessibleBySwitching(datasetId: Option[ObjectId], annotationId: Option[ObjectId], workflowHash: Option[String]) POST /auth/sendInvites controllers.AuthenticationController.sendInvites() POST /auth/startResetPassword controllers.AuthenticationController.handleStartResetPassword() POST /auth/changePassword controllers.AuthenticationController.changePassword() diff --git a/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts b/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts index 11d1953c88d..31b4532a504 100644 --- a/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts +++ b/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts @@ -199,6 +199,8 @@ describe("Annotation API (E2E)", () => { [UpdateActions.updateCameraAnnotation([2, 3, 4], null, [1, 2, 3], 2)], ], 123456789, + null, + true, ), 0, ); @@ -246,6 +248,8 @@ describe("Annotation API (E2E)", () => { createSaveQueueFromUpdateActions( [createTreesUpdateActions, [updateTreeGroupsUpdateAction]], 123456789, + null, + true, ), 0, ); @@ -284,7 +288,12 @@ describe("Annotation API (E2E)", () => { const updateTreeAction = UpdateActions.updateTree(trees.getOrThrow(1), tracingId); const [saveQueue] = addVersionNumbers( - createSaveQueueFromUpdateActions([createTreesUpdateActions, [updateTreeAction]], 123456789), + createSaveQueueFromUpdateActions( + [createTreesUpdateActions, [updateTreeAction]], + 123456789, + null, + true, + ), 0, ); @@ -301,6 +310,8 @@ describe("Annotation API (E2E)", () => { createSaveQueueFromUpdateActions( [[UpdateActions.updateMetadataOfAnnotation(newDescription)]], 123456789, + null, + true, ), 0, ); diff --git a/frontend/javascripts/test/e2e-setup.ts b/frontend/javascripts/test/e2e-setup.ts index 2e671b77bac..6606337ff41 100644 --- a/frontend/javascripts/test/e2e-setup.ts +++ b/frontend/javascripts/test/e2e-setup.ts @@ -34,6 +34,8 @@ const tokenUserFInOrgaX = let currentUserAuthToken = tokenUserA; +const idUserA = "570b9f4d2a7c0e4d008da6ef"; + function setUserAuthToken(token: string) { currentUserAuthToken = token; } @@ -163,4 +165,5 @@ export { tokenUserFInOrgaX, tokenUserFInOrgaY, setUserAuthToken, + idUserA, }; diff --git a/frontend/javascripts/test/helpers/saveHelpers.ts b/frontend/javascripts/test/helpers/saveHelpers.ts index 68be879397d..3ad197c7a23 100644 --- a/frontend/javascripts/test/helpers/saveHelpers.ts +++ b/frontend/javascripts/test/helpers/saveHelpers.ts @@ -1,12 +1,14 @@ import type { TracingStats } from "viewer/model/accessors/annotation_accessor"; import type { UpdateActionWithoutIsolationRequirement } from "viewer/model/sagas/update_actions"; import type { SaveQueueEntry } from "viewer/store"; +import { idUserA } from "test/e2e-setup"; import dummyUser from "test/fixtures/dummy_user"; export function createSaveQueueFromUpdateActions( updateActions: UpdateActionWithoutIsolationRequirement[][], timestamp: number, stats: TracingStats | null = null, + useE2eAuthorId: boolean = false, ): SaveQueueEntry[] { return updateActions.map((ua) => ({ version: -1, @@ -15,7 +17,7 @@ export function createSaveQueueFromUpdateActions( actions: ua, info: "[]", transactionGroupCount: 1, - authorId: dummyUser.id, + authorId: useE2eAuthorId ? idUserA : dummyUser.id, transactionGroupIndex: 0, transactionId: "dummyRequestId", })); diff --git a/test/backend/AnnotationUserStateTestSuite.scala b/test/backend/AnnotationUserStateTestSuite.scala index 1e33127aa34..a9c5e355810 100644 --- a/test/backend/AnnotationUserStateTestSuite.scala +++ b/test/backend/AnnotationUserStateTestSuite.scala @@ -1,5 +1,6 @@ package backend +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.datastore.IdWithBool.{Id32WithBool, Id64WithBool} import com.scalableminds.webknossos.datastore.SkeletonTracing import com.scalableminds.webknossos.tracingstore.tracings.AnnotationUserStateUtils @@ -8,6 +9,10 @@ import org.scalatestplus.play.PlaySpec class AnnotationUserStateTestSuite extends PlaySpec with AnnotationUserStateUtils { + private lazy val userAId = ObjectId("userA") + private lazy val userBId = ObjectId("userB") + private lazy val userCId = ObjectId("userC") + private lazy val dummySkeletonWithUserState = Dummies.skeletonTracing.copy( userStates = Seq( SkeletonTracing.SkeletonUserStateProto( @@ -28,7 +33,7 @@ class AnnotationUserStateTestSuite extends PlaySpec with AnnotationUserStateUtil "Skeleton user state" should { "be rendered into new skeleton user state correctly for userA (sparse user state present for them)" in { val renderedUserState = - renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, "userA", "userB") + renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, userAId, userBId) assert(renderedUserState.treeVisibilities == Seq(Id32WithBool(1, false), Id32WithBool(2, true))) assert(renderedUserState.activeNodeId == Some(5)) assert(renderedUserState.treeGroupExpandedStates == Seq(Id32WithBool(1, true))) @@ -36,14 +41,14 @@ class AnnotationUserStateTestSuite extends PlaySpec with AnnotationUserStateUtil "be rendered into new skeleton user state correctly for userB (owner)" in { val renderedUserState = - renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, "userB", "userB") + renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, userBId, userBId) assert(renderedUserState.treeVisibilities == Seq(Id32WithBool(1, true), Id32WithBool(2, true))) assert(renderedUserState.treeGroupExpandedStates == Seq.empty) } "be rendered into new skeleton user state correctly for userC (no user state present for them)" in { val renderedUserState = - renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, "userC", "userB") + renderSkeletonUserStateIntoUserState(dummySkeletonWithUserState, userCId, userBId) assert(renderedUserState.treeVisibilities == Seq(Id32WithBool(1, true), Id32WithBool(2, true))) assert(renderedUserState.activeNodeId == Some(2)) assert(renderedUserState.treeGroupExpandedStates == Seq.empty) @@ -55,14 +60,14 @@ class AnnotationUserStateTestSuite extends PlaySpec with AnnotationUserStateUtil "respect id mapping" in { val tracingAUserStates = Seq( VolumeTracingDefaults - .emptyUserState("userA") + .emptyUserState(userAId) .copy( segmentVisibilities = Seq(Id64WithBool(1L, true)), segmentGroupExpandedStates = Seq(Id32WithBool(1, true)) )) val tracingBUserStates = Seq( VolumeTracingDefaults - .emptyUserState("userA") + .emptyUserState(userAId) .copy( segmentVisibilities = Seq(Id64WithBool(1L, false)), segmentGroupExpandedStates = Seq(Id32WithBool(1, false)) @@ -78,7 +83,7 @@ class AnnotationUserStateTestSuite extends PlaySpec with AnnotationUserStateUtil assert( mergedUserStates == Seq( VolumeTracingDefaults - .emptyUserState("userA") + .emptyUserState(userAId) .copy(segmentVisibilities = Seq(Id64WithBool(1, true), Id64WithBool(2L, false)), segmentGroupExpandedStates = Seq(Id32WithBool(6, true), Id32WithBool(1, false))) )) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/SkeletonElementDefaults.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/SkeletonElementDefaults.scala index ee1e65f7faa..41802477ec4 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/SkeletonElementDefaults.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/SkeletonElementDefaults.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.datastore.helpers import com.scalableminds.util.geometry.{Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.datastore.SkeletonTracing.{Node, SkeletonTracing, SkeletonUserStateProto} object SkeletonTracingDefaults extends ProtoGeometryImplicits { @@ -27,9 +28,9 @@ object SkeletonTracingDefaults extends ProtoGeometryImplicits { version, userBoundingBox) - def emptyUserState(userId: String): SkeletonUserStateProto = + def emptyUserState(userId: ObjectId): SkeletonUserStateProto = SkeletonUserStateProto( - userId = userId, + userId = userId.toString, activeNodeId = None, treeGroupExpandedStates = Seq.empty, boundingBoxVisibilities = Seq.empty, diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/annotation/AnnotationSource.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/annotation/AnnotationSource.scala index cfd8e69180b..7d60eec44f3 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/annotation/AnnotationSource.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/annotation/AnnotationSource.scala @@ -1,8 +1,9 @@ package com.scalableminds.webknossos.datastore.models.annotation +import com.scalableminds.util.objectid.ObjectId import play.api.libs.json.{Json, OFormat} -case class AnnotationSource(id: String, +case class AnnotationSource(id: ObjectId, annotationLayers: List[AnnotationLayer], datasetDirectoryName: String, organizationId: String, diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AccessTokenService.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AccessTokenService.scala index 941df655b7d..6c209d2ceda 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AccessTokenService.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/AccessTokenService.scala @@ -4,6 +4,7 @@ import com.google.inject.Inject import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.enumeration.ExtendedEnumeration +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId import play.api.libs.json.{Json, OFormat} @@ -47,11 +48,11 @@ object UserAccessRequest { def writeTracing(tracingId: String): UserAccessRequest = UserAccessRequest(DataSourceId(tracingId, ""), AccessResourceType.tracing, AccessMode.write) - def readAnnotation(annotationId: String): UserAccessRequest = - UserAccessRequest(DataSourceId(annotationId, ""), AccessResourceType.annotation, AccessMode.read) + def readAnnotation(annotationId: ObjectId): UserAccessRequest = + UserAccessRequest(DataSourceId(annotationId.toString, ""), AccessResourceType.annotation, AccessMode.read) - def writeAnnotation(annotationId: String): UserAccessRequest = - UserAccessRequest(DataSourceId(annotationId, ""), AccessResourceType.annotation, AccessMode.write) + def writeAnnotation(annotationId: ObjectId): UserAccessRequest = + UserAccessRequest(DataSourceId(annotationId.toString, ""), AccessResourceType.annotation, AccessMode.write) def downloadJobExport(jobId: String): UserAccessRequest = UserAccessRequest(DataSourceId(jobId, ""), AccessResourceType.jobExport, AccessMode.read) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteDatastoreClient.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteDatastoreClient.scala index 5a5b2f064f4..20eee18c328 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteDatastoreClient.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteDatastoreClient.scala @@ -4,6 +4,7 @@ import com.google.inject.Inject import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.Vec3Int +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.AgglomerateGraph.AgglomerateGraph import com.scalableminds.webknossos.datastore.ListOfLong.ListOfLong @@ -38,7 +39,7 @@ class TSRemoteDatastoreClient @Inject()( with MissingBucketHeaders { private lazy val dataStoreUriCache: AlfuCache[(String, String), String] = AlfuCache() - private lazy val voxelSizeCache: AlfuCache[String, VoxelSize] = AlfuCache(timeToLive = 10 minutes) + private lazy val voxelSizeCache: AlfuCache[ObjectId, VoxelSize] = AlfuCache(timeToLive = 10 minutes) private lazy val largestAgglomerateIdCache: AlfuCache[(RemoteFallbackLayer, String, Option[String]), Long] = AlfuCache(timeToLive = 10 minutes) @@ -160,10 +161,10 @@ class TSRemoteDatastoreClient @Inject()( .postJsonWithBytesResponse(fullMeshRequest) } yield result - def voxelSizeForAnnotationWithCache(annotationId: String)(implicit tc: TokenContext): Fox[VoxelSize] = + def voxelSizeForAnnotationWithCache(annotationId: ObjectId)(implicit tc: TokenContext): Fox[VoxelSize] = voxelSizeCache.getOrLoad(annotationId, aId => voxelSizeForAnnotation(aId)) - private def voxelSizeForAnnotation(annotationId: String)(implicit tc: TokenContext): Fox[VoxelSize] = + private def voxelSizeForAnnotation(annotationId: ObjectId)(implicit tc: TokenContext): Fox[VoxelSize] = for { dataSourceId <- remoteWebknossosClient.getDataSourceIdForAnnotation(annotationId) dataStoreUri <- dataStoreUriWithCache(dataSourceId.organizationId, dataSourceId.directoryName) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteWebknossosClient.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteWebknossosClient.scala index eb845c08128..109926be669 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteWebknossosClient.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/TSRemoteWebknossosClient.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.tracingstore import com.google.inject.Inject import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.Annotation.AnnotationProto @@ -27,7 +28,7 @@ import play.api.libs.ws.WSResponse import scala.concurrent.ExecutionContext import scala.concurrent.duration.DurationInt -case class AnnotationUpdatesReport(annotationId: String, +case class AnnotationUpdatesReport(annotationId: ObjectId, timestamps: List[Instant], statistics: Option[JsObject], significantChangesCount: Int, @@ -49,8 +50,8 @@ class TSRemoteWebknossosClient @Inject()( private val webknossosUri: String = config.Tracingstore.WebKnossos.uri - private lazy val dataSourceIdByAnnotationIdCache: AlfuCache[String, DataSourceId] = AlfuCache() - private lazy val annotationIdByTracingIdCache: AlfuCache[String, String] = + private lazy val dataSourceIdByAnnotationIdCache: AlfuCache[ObjectId, DataSourceId] = AlfuCache() + private lazy val annotationIdByTracingIdCache: AlfuCache[String, ObjectId] = AlfuCache(maxCapacity = 10000, timeToLive = 5 minutes) def reportAnnotationUpdates(tracingUpdatesReport: AnnotationUpdatesReport): Fox[WSResponse] = @@ -59,9 +60,9 @@ class TSRemoteWebknossosClient @Inject()( .silent .postJson(Json.toJson(tracingUpdatesReport)) - def getDataSourceForAnnotation(annotationId: String)(implicit tc: TokenContext): Fox[DataSourceLike] = + def getDataSourceForAnnotation(annotationId: ObjectId)(implicit tc: TokenContext): Fox[DataSourceLike] = rpc(s"$webknossosUri/api/tracingstores/$tracingStoreName/dataSource") - .addQueryString("annotationId" -> annotationId) + .addQueryString("annotationId" -> annotationId.toString) .addQueryString("key" -> tracingStoreKey) .withTokenFromContext .silent @@ -74,18 +75,18 @@ class TSRemoteWebknossosClient @Inject()( .silent .getWithJsonResponse[String] - def getDataSourceIdForAnnotation(annotationId: String)(implicit ec: ExecutionContext): Fox[DataSourceId] = + def getDataSourceIdForAnnotation(annotationId: ObjectId)(implicit ec: ExecutionContext): Fox[DataSourceId] = dataSourceIdByAnnotationIdCache.getOrLoad( annotationId, aId => rpc(s"$webknossosUri/api/tracingstores/$tracingStoreName/dataSourceId") - .addQueryString("annotationId" -> aId) + .addQueryString("annotationId" -> aId.toString) .addQueryString("key" -> tracingStoreKey) .silent .getWithJsonResponse[DataSourceId] ) - def getAnnotationIdForTracing(tracingId: String)(implicit ec: ExecutionContext): Fox[String] = + def getAnnotationIdForTracing(tracingId: String)(implicit ec: ExecutionContext): Fox[ObjectId] = annotationIdByTracingIdCache.getOrLoad( tracingId, tracingId => @@ -93,21 +94,21 @@ class TSRemoteWebknossosClient @Inject()( .addQueryString("tracingId" -> tracingId) .addQueryString("key" -> tracingStoreKey) .silent - .getWithJsonResponse[String] + .getWithJsonResponse[ObjectId] ) ?~> "annotation.idForTracing.failed" - def updateAnnotation(annotationId: String, annotationProto: AnnotationProto): Fox[Unit] = + def updateAnnotation(annotationId: ObjectId, annotationProto: AnnotationProto): Fox[Unit] = rpc(s"$webknossosUri/api/tracingstores/$tracingStoreName/updateAnnotation") - .addQueryString("annotationId" -> annotationId) + .addQueryString("annotationId" -> annotationId.toString) .addQueryString("key" -> tracingStoreKey) .silent .postProto(annotationProto) - def createTracingFor(annotationId: String, + def createTracingFor(annotationId: ObjectId, layerParameters: AnnotationLayerParameters, previousVersion: Long): Fox[Either[SkeletonTracingWithUpdatedTreeIds, VolumeTracing]] = { val req = rpc(s"$webknossosUri/api/tracingstores/$tracingStoreName/createTracing") - .addQueryString("annotationId" -> annotationId) + .addQueryString("annotationId" -> annotationId.toString) .addQueryString("previousVersion" -> previousVersion.toString) // used for fetching old precedence layers .addQueryString("key" -> tracingStoreKey) layerParameters.typ match { diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationReversion.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationReversion.scala index 09b740318f2..457681d69df 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationReversion.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationReversion.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.annotation import com.scalableminds.util.accesscontext.TokenContext +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.EditableMappingInfo.EditableMappingInfo import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing @@ -20,14 +21,14 @@ trait AnnotationReversion extends FoxImplicits { version: Option[Long]): Fox[VersionedKeyValuePair[EditableMappingInfo]] protected def editableMappingUpdaterFor( - annotationId: String, + annotationId: ObjectId, tracingId: String, volumeTracing: VolumeTracing, editableMappingInfo: EditableMappingInfo, currentMaterializedVersion: Long, targetVersion: Long)(implicit tc: TokenContext, ec: ExecutionContext): Fox[EditableMappingUpdater] - def revertDistributedElements(annotationId: String, + def revertDistributedElements(annotationId: ObjectId, currentAnnotationWithTracings: AnnotationWithTracings, sourceAnnotationWithTracings: AnnotationWithTracings, sourceVersion: Long, @@ -60,7 +61,7 @@ trait AnnotationReversion extends FoxImplicits { } yield () private def revertEditableMappingFields( - annotationId: String, + annotationId: ObjectId, currentAnnotationWithTracings: AnnotationWithTracings, tracingBeforeRevert: VolumeTracing, sourceVersion: Long, @@ -75,7 +76,7 @@ trait AnnotationReversion extends FoxImplicits { } yield () // If source annotation doesn’t have this editable mapping, use the last existing one as a “before point” for the reversion - private def editableMappingReversionUpdater(annotationId: String, + private def editableMappingReversionUpdater(annotationId: ObjectId, tracingId: String, tracingBeforeRevert: VolumeTracing, targetVersion: Long)(implicit ec: ExecutionContext, tc: TokenContext) = diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationTransactionService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationTransactionService.scala index c1bc0f06b14..673bb83214c 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationTransactionService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationTransactionService.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.annotation import com.scalableminds.util.accesscontext.TokenContext +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, JsonHelper} import com.scalableminds.webknossos.tracingstore.tracings.volume.{ @@ -34,19 +35,22 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe private val transactionGroupExpiry: FiniteDuration = 24 hours private val handledGroupCacheExpiry: FiniteDuration = 24 hours - private def transactionGroupKey(annotationId: String, + private def transactionGroupKey(annotationId: ObjectId, transactionId: String, transactionGroupIndex: Int, version: Long) = s"transactionGroup___${annotationId}___${transactionId}___${transactionGroupIndex}___$version" - private def handledGroupKey(annotationId: String, transactionId: String, version: Long, transactionGroupIndex: Int) = + private def handledGroupKey(annotationId: ObjectId, + transactionId: String, + version: Long, + transactionGroupIndex: Int) = s"handledGroup___${annotationId}___${transactionId}___${version}___$transactionGroupIndex" - private def patternFor(annotationId: String, transactionId: String) = + private def patternFor(annotationId: ObjectId, transactionId: String) = s"transactionGroup___${annotationId}___${transactionId}___*" - private def saveUncommitted(annotationId: String, + private def saveUncommitted(annotationId: ObjectId, transactionId: String, transactionGroupIndex: Int, version: Long, @@ -67,7 +71,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe } yield () private def handleUpdateGroupOfTransaction( - annotationId: String, + annotationId: ObjectId, previousVersionFox: Fox[Long], updateGroup: UpdateActionGroup)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Long] = for { @@ -97,8 +101,8 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe // For an update group (that is the last of a transaction), fetch all previous uncommitted for the same transaction // and commit them all. - private def commitWithPending(annotationId: String, updateGroup: UpdateActionGroup)(implicit ec: ExecutionContext, - tc: TokenContext): Fox[Long] = + private def commitWithPending(annotationId: ObjectId, updateGroup: UpdateActionGroup)(implicit ec: ExecutionContext, + tc: TokenContext): Fox[Long] = for { previousActionGroupsToCommit <- getAllUncommittedFor(annotationId, updateGroup.transactionId) _ <- Fox.fromBool(previousActionGroupsToCommit @@ -108,17 +112,17 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe _ <- removeAllUncommittedFor(annotationId, updateGroup.transactionId) } yield commitResult - private def removeAllUncommittedFor(tracingId: String, transactionId: String): Fox[Unit] = - uncommittedUpdatesStore.removeAllConditional(patternFor(tracingId, transactionId)) + private def removeAllUncommittedFor(annotationId: ObjectId, transactionId: String): Fox[Unit] = + uncommittedUpdatesStore.removeAllConditional(patternFor(annotationId, transactionId)) - private def getAllUncommittedFor(annotationId: String, transactionId: String): Fox[List[UpdateActionGroup]] = + private def getAllUncommittedFor(annotationId: ObjectId, transactionId: String): Fox[List[UpdateActionGroup]] = for { raw: Seq[String] <- uncommittedUpdatesStore.findAllConditional(patternFor(annotationId, transactionId)) parsed: Seq[UpdateActionGroup] = raw.flatMap(itemAsString => JsonHelper.parseAs[UpdateActionGroup](itemAsString).toOption) } yield parsed.toList.sortBy(_.transactionGroupIndex) - private def saveToHandledGroupIdStore(annotationId: String, + private def saveToHandledGroupIdStore(annotationId: ObjectId, transactionId: String, version: Long, transactionGroupIndex: Int): Fox[Unit] = { @@ -126,7 +130,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe handledGroupIdStore.insert(key, "()", Some(handledGroupCacheExpiry)) } - private def handledGroupIdStoreContains(annotationId: String, + private def handledGroupIdStoreContains(annotationId: ObjectId, transactionId: String, version: Long, transactionGroupIndex: Int): Fox[Boolean] = @@ -150,7 +154,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe ) } - def handleSingleUpdateAction(annotationId: String, currentVersion: Long, updateAction: UpdateAction)( + def handleSingleUpdateAction(annotationId: ObjectId, currentVersion: Long, updateAction: UpdateAction)( implicit ec: ExecutionContext, tc: TokenContext): Fox[Long] = { val wrapped = List( @@ -168,8 +172,8 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe handleUpdateGroups(annotationId, wrapped) } - def handleUpdateGroups(annotationId: String, updateGroups: List[UpdateActionGroup])(implicit ec: ExecutionContext, - tc: TokenContext): Fox[Long] = + def handleUpdateGroups(annotationId: ObjectId, updateGroups: List[UpdateActionGroup])(implicit ec: ExecutionContext, + tc: TokenContext): Fox[Long] = if (updateGroups.forall(_.transactionGroupCount == 1)) { commitUpdates(annotationId, updateGroups) } else { @@ -180,8 +184,9 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe } // Perform version check and commit the passed updates - private def commitUpdates(annotationId: String, updateGroups: List[UpdateActionGroup])(implicit ec: ExecutionContext, - tc: TokenContext): Fox[Long] = + private def commitUpdates(annotationId: ObjectId, updateGroups: List[UpdateActionGroup])( + implicit ec: ExecutionContext, + tc: TokenContext): Fox[Long] = for { _ <- reportUpdates(annotationId, updateGroups) currentCommittedVersion: Fox[Long] = annotationService.currentMaterializableVersion(annotationId) @@ -201,7 +206,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe _ <- applyImmediatelyIfNeeded(annotationId, updateGroups.flatMap(_.actions), newVersion) } yield newVersion - private def applyImmediatelyIfNeeded(annotationId: String, updates: List[UpdateAction], newVersion: Long)( + private def applyImmediatelyIfNeeded(annotationId: ObjectId, updates: List[UpdateAction], newVersion: Long)( implicit ec: ExecutionContext, tc: TokenContext): Fox[Unit] = if (containsApplyImmediatelyUpdateActions(updates)) { @@ -213,12 +218,12 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe case _ => false } - private def handleUpdateGroup(annotationId: String, updateActionGroup: UpdateActionGroup)( + private def handleUpdateGroup(annotationId: ObjectId, updateActionGroup: UpdateActionGroup)( implicit ec: ExecutionContext, tc: TokenContext): Fox[Unit] = for { updateActionsJson <- Fox.successful(Json.toJson(preprocessActionsForStorage(updateActionGroup))) - _ <- tracingDataStore.annotationUpdates.put(annotationId, updateActionGroup.version, updateActionsJson) + _ <- tracingDataStore.annotationUpdates.put(annotationId.toString, updateActionGroup.version, updateActionsJson) bucketMutatingActions = findBucketMutatingActions(updateActionGroup) actionsGrouped: Map[String, List[BucketMutatingVolumeUpdateAction]] = bucketMutatingActions.groupBy( _.actionTracingId) @@ -258,7 +263,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe * ignore it silently. This is in case the frontend sends a retry if it believes a save to be unsuccessful * despite the backend receiving it just fine. */ - private def failUnlessAlreadyHandled(updateGroup: UpdateActionGroup, annotationId: String, previousVersion: Long)( + private def failUnlessAlreadyHandled(updateGroup: UpdateActionGroup, annotationId: ObjectId, previousVersion: Long)( implicit ec: ExecutionContext): Fox[Long] = { val errorMessage = s"Incorrect version. Expected: ${previousVersion + 1}; Got: ${updateGroup.version}" for { @@ -270,7 +275,7 @@ class AnnotationTransactionService @Inject()(handledGroupIdStore: TracingStoreRe } yield updateGroup.version } - private def reportUpdates(annotationId: String, updateGroups: List[UpdateActionGroup])( + private def reportUpdates(annotationId: ObjectId, updateGroups: List[UpdateActionGroup])( implicit tc: TokenContext): Fox[Unit] = for { _ <- remoteWebknossosClient.reportAnnotationUpdates( diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationUpdateActions.scala index 53027993906..1587152d3c5 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationUpdateActions.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.annotation import com.scalableminds.util.geometry.{Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.datastore.models.AdditionalCoordinate import com.scalableminds.webknossos.datastore.models.annotation.AnnotationLayer import com.scalableminds.webknossos.datastore.models.annotation.AnnotationLayerType.AnnotationLayerType @@ -28,14 +29,14 @@ trait AnnotationUpdateAction extends UpdateAction case class AddLayerAnnotationAction(layerParameters: AnnotationLayerParameters, tracingId: Option[String] = None, // filled in by backend eagerly on save actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } @@ -43,80 +44,80 @@ case class DeleteLayerAnnotationAction(tracingId: String, layerName: String, // Just stored for nicer-looking history typ: AnnotationLayerType, // Just stored for nicer-looking history actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } case class UpdateLayerMetadataAnnotationAction(tracingId: String, layerName: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } case class UpdateMetadataAnnotationAction( description: Option[String], // None means do not change description. Empty string means set to empty actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } case class RevertToVersionAnnotationAction(sourceVersion: Long, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } // Used only in tasks by admin to undo the work done of the annotator case class ResetToBaseAnnotationAction(actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with ApplyImmediatelyUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) } case class UpdateTdCameraAnnotationAction(actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction { override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def isViewOnlyChange: Boolean = true @@ -127,7 +128,7 @@ case class UpdateCameraAnnotationAction(editPosition: Vec3Int, zoomLevel: Double, editPositionAdditionalCoordinates: Option[Seq[AdditionalCoordinate]] = None, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends AnnotationUpdateAction with UserStateUpdateAction { @@ -135,7 +136,7 @@ case class UpdateCameraAnnotationAction(editPosition: Vec3Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def isViewOnlyChange: Boolean = true } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationWithTracings.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationWithTracings.scala index e7ad66d7077..0d72b96a712 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationWithTracings.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/AnnotationWithTracings.scala @@ -134,12 +134,12 @@ case class AnnotationWithTracings( a.actionAuthorId match { case None => this case Some(actionUserId) => - val userStateAlreadyPresent = annotation.userStates.exists(state => actionUserId == state.userId) + val userStateAlreadyPresent = annotation.userStates.exists(state => actionUserId.toString == state.userId) if (userStateAlreadyPresent) { this.copy(annotation = annotation.copy(userStates = annotation.userStates.map { userState => - if (actionUserId == userState.userId) + if (actionUserId.toString == userState.userId) userState.copy( - userId = actionUserId, + userId = actionUserId.toString, editPosition = a.editPosition, editRotation = a.editRotation, zoomLevel = a.zoomLevel, @@ -150,7 +150,7 @@ case class AnnotationWithTracings( } else this.copy( annotation = annotation.copy(userStates = annotation.userStates :+ AnnotationUserStateProto( - userId = actionUserId, + userId = actionUserId.toString, editPosition = a.editPosition, editRotation = a.editRotation, zoomLevel = a.zoomLevel, diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/TSAnnotationService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/TSAnnotationService.scala index e048ffb0d1f..9e6c2ce45a0 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/TSAnnotationService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/TSAnnotationService.scala @@ -4,6 +4,7 @@ import collections.SequenceUtils import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.Annotation.{ @@ -57,13 +58,13 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss // two-level caching: outer key: annotation id; inner key version // This way we cache at most two versions of the same annotation, and at most 1000 different annotations private lazy val materializedAnnotationWithTracingCache = - AlfuCache[String, AlfuCache[Long, AnnotationWithTracings]](maxCapacity = 1000) + AlfuCache[ObjectId, AlfuCache[Long, AnnotationWithTracings]](maxCapacity = 1000) private def newInnerCache(implicit ec: ExecutionContext): Fox[AlfuCache[Long, AnnotationWithTracings]] = Fox.successful(AlfuCache[Long, AnnotationWithTracings](maxCapacity = 2)) - def get(annotationId: String, version: Option[Long])(implicit ec: ExecutionContext, - tc: TokenContext): Fox[AnnotationProto] = + def get(annotationId: ObjectId, version: Option[Long])(implicit ec: ExecutionContext, + tc: TokenContext): Fox[AnnotationProto] = for { isTemporaryAnnotation <- temporaryTracingService.isTemporaryAnnotation(annotationId) annotation <- if (isTemporaryAnnotation) temporaryTracingService.getAnnotation(annotationId) @@ -73,13 +74,13 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield withTracings.annotation } yield annotation - def getMultiple(annotationIds: Seq[String])(implicit ec: ExecutionContext, - tc: TokenContext): Fox[Seq[AnnotationProto]] = + def getMultiple(annotationIds: Seq[ObjectId])(implicit ec: ExecutionContext, + tc: TokenContext): Fox[Seq[AnnotationProto]] = Fox.serialCombined(annotationIds) { annotationId => get(annotationId, None) } - private def getWithTracings(annotationId: String, version: Option[Long])( + private def getWithTracings(annotationId: ObjectId, version: Option[Long])( implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = for { @@ -96,7 +97,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss ) } yield updatedAnnotation - private def getWithTracingsVersioned(annotationId: String, targetVersion: Long, reportChangesToWk: Boolean)( + private def getWithTracingsVersioned(annotationId: ObjectId, targetVersion: Long, reportChangesToWk: Boolean)( implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = for { @@ -111,11 +112,13 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss updated <- applyPendingUpdates(annotationWithTracingsAndMappings, annotationId, targetVersion, reportChangesToWk) ?~> "applyUpdates.failed" } yield updated - def currentMaterializableVersion(annotationId: String): Fox[Long] = - tracingDataStore.annotationUpdates.getVersion(annotationId, mayBeEmpty = Some(true), emptyFallback = Some(0L)) + def currentMaterializableVersion(annotationId: ObjectId): Fox[Long] = + tracingDataStore.annotationUpdates.getVersion(annotationId.toString, + mayBeEmpty = Some(true), + emptyFallback = Some(0L)) - def currentMaterializedVersion(annotationId: String): Fox[Long] = - tracingDataStore.annotations.getVersion(annotationId, mayBeEmpty = Some(true), emptyFallback = Some(0L)) + def currentMaterializedVersion(annotationId: ObjectId): Fox[Long] = + tracingDataStore.annotations.getVersion(annotationId.toString, mayBeEmpty = Some(true), emptyFallback = Some(0L)) private def newestMatchingMaterializedSkeletonVersion(tracingId: String, targetVersion: Long): Fox[Long] = tracingDataStore.skeletons.getVersion(tracingId, @@ -129,17 +132,17 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss mayBeEmpty = Some(true), emptyFallback = Some(0L)) - private def getNewestMatchingMaterializedAnnotation(annotationId: String, + private def getNewestMatchingMaterializedAnnotation(annotationId: ObjectId, version: Option[Long]): Fox[AnnotationProto] = for { keyValuePair <- tracingDataStore.annotations.get[AnnotationProto]( - annotationId, + annotationId.toString, mayBeEmpty = Some(true), version = version)(fromProtoBytes[AnnotationProto]) ?~> "getAnnotation.failed" } yield keyValuePair.value private def applyUpdate( - annotationId: String, + annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, updateAction: UpdateAction, targetVersion: Long // Note: this is not the target version of this one update, but of all pending @@ -177,7 +180,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss case _ => Fox.failure(s"Received unsupported AnnotationUpdateAction action ${Json.toJson(updateAction)}") } - private def addLayer(annotationId: String, + private def addLayer(annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, action: AddLayerAnnotationAction, targetVersion: Long)(implicit ec: ExecutionContext): Fox[AnnotationWithTracings] = @@ -196,7 +199,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield updated private def revertToVersion( - annotationId: String, + annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, revertAction: RevertToVersionAnnotationAction, newVersion: Long)(implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = @@ -215,7 +218,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss s"Reverting annotation $annotationId from v${annotationWithTracings.version} to v${revertAction.sourceVersion}") } yield sourceAnnotation.markAllTreeBodiesAsChanged - private def resetToBase(annotationId: String, annotationWithTracings: AnnotationWithTracings, newVersion: Long)( + private def resetToBase(annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, newVersion: Long)( implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = { // Note: works only if reset actions are in separate update groups @@ -228,16 +231,16 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield sourceAnnotation.markAllTreeBodiesAsChanged } - def saveAnnotationProto(annotationId: String, + def saveAnnotationProto(annotationId: ObjectId, version: Long, annotationProto: AnnotationProto, toTemporaryStore: Boolean = false): Fox[Unit] = if (toTemporaryStore) temporaryTracingService.saveAnnotationProto(annotationId, annotationProto) else - tracingDataStore.annotations.put(annotationId, version, annotationProto) + tracingDataStore.annotations.put(annotationId.toString, version, annotationProto) - def updateActionLog(annotationId: String, newestVersion: Long, oldestVersion: Long)( + def updateActionLog(annotationId: ObjectId, newestVersion: Long, oldestVersion: Long)( implicit ec: ExecutionContext): Fox[JsValue] = { def versionedTupleToJson(tuple: (Long, List[UpdateAction])): JsObject = Json.obj( @@ -251,14 +254,14 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss val batchFrom = batchRange._1 val batchTo = batchRange._2 tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple( - annotationId, + annotationId.toString, Some(batchTo), Some(batchFrom))(fromJsonBytes[List[UpdateAction]]) } } yield Json.toJson(updateActionBatches.flatten.map(versionedTupleToJson)) } - def findEditableMappingInfo(annotationId: String, tracingId: String, version: Option[Long] = None)( + def findEditableMappingInfo(annotationId: ObjectId, tracingId: String, version: Option[Long] = None)( implicit ec: ExecutionContext, tc: TokenContext): Fox[EditableMappingInfo] = for { @@ -267,7 +270,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield tracing private def addEditableMapping( - annotationId: String, + annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, action: UpdateMappingNameVolumeAction, targetVersion: Long)(implicit tc: TokenContext, ec: ExecutionContext): Fox[AnnotationWithTracings] = @@ -290,7 +293,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss private def applyPendingUpdates( annotationWithTracingsAndMappings: AnnotationWithTracings, - annotationId: String, + annotationId: ObjectId, targetVersion: Long, reportChangesToWk: Boolean)(implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = for { @@ -303,7 +306,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield updated.withVersion(targetVersion) // set version again, because extraSkeleton update filtering may skip latest version - private def findPendingUpdates(annotationId: String, annotation: AnnotationWithTracings, desiredVersion: Long)( + private def findPendingUpdates(annotationId: ObjectId, annotation: AnnotationWithTracings, desiredVersion: Long)( implicit ec: ExecutionContext): Fox[List[(Long, List[UpdateAction])]] = for { extraSkeletonUpdates <- findExtraSkeletonUpdates(annotationId, annotation, desiredVersion) @@ -312,7 +315,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss pendingAnnotationUpdates <- if (desiredVersion == existingVersion) Fox.successful(List.empty) else { tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple( - annotationId, + annotationId.toString, Some(desiredVersion), Some(existingVersion + 1))(fromJsonBytes[List[UpdateAction]]) } @@ -327,7 +330,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss * we may fetch skeleton updates *older* than it, in order to fully construct the state of that version. * Only annotations from before that migration have this skeletonMayHavePendingUpdates=Some(true). */ - private def findExtraSkeletonUpdates(annotationId: String, annotation: AnnotationWithTracings, targetVersion: Long)( + private def findExtraSkeletonUpdates(annotationId: ObjectId, annotation: AnnotationWithTracings, targetVersion: Long)( implicit ec: ExecutionContext): Fox[List[(Long, List[UpdateAction])]] = if (annotation.annotation.skeletonMayHavePendingUpdates.getOrElse(false)) { annotation.getSkeletonId.map { skeletonId => @@ -335,7 +338,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss materializedSkeletonVersion <- newestMatchingMaterializedSkeletonVersion(skeletonId, targetVersion) extraUpdates <- if (materializedSkeletonVersion < annotation.version) { tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple( - annotationId, + annotationId.toString, Some(annotation.version), Some(materializedSkeletonVersion + 1))(fromJsonBytes[List[UpdateAction]]) } else Fox.successful(List.empty) @@ -361,7 +364,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss // Note that the EditableMappingUpdaters are passed only the “oldVersion” that is the materialized annotation version // not the actual materialized editableMapping version, but that should yield the same data when loading from fossil. private def findExtraEditableMappingUpdates( - annotationId: String, + annotationId: ObjectId, annotation: AnnotationWithTracings, targetVersion: Long)(implicit ec: ExecutionContext): Fox[List[(Long, List[UpdateAction])]] = if (annotation.annotation.editableMappingsMayHavePendingUpdates.getOrElse(false)) { @@ -372,7 +375,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss targetVersion) extraUpdates <- if (materializedEditableMappingVersion < annotation.version) { tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple( - annotationId, + annotationId.toString, Some(annotation.version), Some(materializedEditableMappingVersion + 1))(fromJsonBytes[List[UpdateAction]]) } else Fox.successful(List.empty) @@ -418,7 +421,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } private def findEditableMappingsForAnnotation( - annotationId: String, + annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings, currentMaterializedVersion: Long, targetVersion: Long)(implicit ec: ExecutionContext, tc: TokenContext) = { @@ -443,7 +446,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss version: Option[Long]): Fox[VersionedKeyValuePair[EditableMappingInfo]] = tracingDataStore.editableMappingsInfo.get(volumeTracingId, version = version)(fromProtoBytes[EditableMappingInfo]) - private def editableMappingUpdaterFor(annotationId: String, + private def editableMappingUpdaterFor(annotationId: ObjectId, tracingId: String, remoteFallbackLayer: RemoteFallbackLayer, editableMappingInfo: EditableMappingInfo, @@ -463,7 +466,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss ) protected def editableMappingUpdaterFor( - annotationId: String, + annotationId: ObjectId, tracingId: String, volumeTracing: VolumeTracing, editableMappingInfo: EditableMappingInfo, @@ -481,7 +484,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss private def applyUpdatesGrouped( annotation: AnnotationWithTracings, - annotationId: String, + annotationId: ObjectId, updateGroups: List[(Long, List[UpdateAction])], reportChangesToWk: Boolean )(implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = { @@ -505,7 +508,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss private def applyUpdates( annotationWithTracings: AnnotationWithTracings, - annotationId: String, + annotationId: ObjectId, updates: List[UpdateAction], targetVersion: Long, reportChangesToWk: Boolean)(implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationWithTracings] = { @@ -592,10 +595,10 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss flushOnlyTheseTreeIds = Some(updatedTreeIds)) } yield () - private def flushAnnotationInfo(annotationId: String, annotationWithTracings: AnnotationWithTracings) = + private def flushAnnotationInfo(annotationId: ObjectId, annotationWithTracings: AnnotationWithTracings) = saveAnnotationProto(annotationId, annotationWithTracings.version, annotationWithTracings.annotation) - private def determineTargetVersion(annotationId: String, + private def determineTargetVersion(annotationId: ObjectId, newestMaterializedAnnotation: AnnotationProto, requestedVersionOpt: Option[Long]): Fox[Long] = /* @@ -604,7 +607,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss * hence the emptyFallbck newestMaterializedAnnotation.version) */ for { - newestUpdateVersion <- tracingDataStore.annotationUpdates.getVersion(annotationId, + newestUpdateVersion <- tracingDataStore.annotationUpdates.getVersion(annotationId.toString, mayBeEmpty = Some(true), emptyFallback = Some(newestMaterializedAnnotation.version)) @@ -616,7 +619,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } } yield targetVersion - def editableMappingLayer(annotationId: String, tracingId: String, tracing: VolumeTracing): EditableMappingLayer = + def editableMappingLayer(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing): EditableMappingLayer = EditableMappingLayer( name = tracingId, tracing.boundingBox, @@ -629,7 +632,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss editableMappingService = editableMappingService ) - def baseMappingName(annotationId: String, tracingId: String, tracing: VolumeTracing)( + def baseMappingName(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing)( implicit ec: ExecutionContext, tc: TokenContext): Fox[Option[String]] = if (tracing.getHasEditableMapping) @@ -665,7 +668,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss tracingDataStore.skeletons .get[SkeletonTracing](tracingId, version, mayBeEmpty = Some(true))(fromProtoBytes[SkeletonTracing]) - def findVolume(annotationId: String, tracingId: String, version: Option[Long] = None)( + def findVolume(annotationId: ObjectId, tracingId: String, version: Option[Long] = None)( implicit tc: TokenContext, ec: ExecutionContext): Fox[VolumeTracing] = for { @@ -680,7 +683,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield tracing def findSkeleton( - annotationId: String, + annotationId: ObjectId, tracingId: String, version: Option[Long] = None )(implicit tc: TokenContext, ec: ExecutionContext): Fox[SkeletonTracing] = @@ -728,10 +731,10 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } def duplicate( - annotationId: String, - newAnnotationId: String, - ownerId: String, - requestingUserId: String, + annotationId: ObjectId, + newAnnotationId: ObjectId, + ownerId: ObjectId, + requestingUserId: ObjectId, version: Option[Long], isFromTask: Boolean, datasetBoundingBox: Option[BoundingBox])(implicit ec: ExecutionContext, tc: TokenContext): Fox[AnnotationProto] = @@ -784,8 +787,8 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield duplicatedAnnotation - private def duplicateUpdates(annotationId: String, - newAnnotationId: String, + private def duplicateUpdates(annotationId: ObjectId, + newAnnotationId: ObjectId, v0TracingIds: Seq[String], newestVersion: Long)(implicit ec: ExecutionContext): Fox[Map[String, String]] = { val tracingIdMapMutable = scala.collection.mutable.Map[String, String]() @@ -799,7 +802,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss for { updateLists: Seq[(Long, List[UpdateAction])] <- tracingDataStore.annotationUpdates .getMultipleVersionsAsVersionValueTuple( - annotationId, + annotationId.toString, oldestVersion = Some(batchRange._1), newestVersion = Some(batchRange._2))(fromJsonBytes[List[UpdateAction]]) _ <- Fox.serialCombined(updateLists.reverse) { // we reverse (order asc by version) so that addLayer comes before the layer’s updates @@ -826,7 +829,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss case a: UpdateAction => Fox.successful(a) } - _ <- updatesPutBuffer.put(newAnnotationId, version, Json.toJson(updateListAdapted)) + _ <- updatesPutBuffer.put(newAnnotationId.toString, version, Json.toJson(updateListAdapted)) } yield () } } yield () @@ -835,10 +838,10 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss .map(_ => tracingIdMapMutable.toMap) } - private def duplicateLayer(annotationId: String, - newAnnotationId: String, - ownerId: String, - requestingUserId: String, + private def duplicateLayer(annotationId: ObjectId, + newAnnotationId: ObjectId, + ownerId: ObjectId, + requestingUserId: ObjectId, layer: AnnotationLayerProto, tracingIdMap: Map[String, String], version: Long, @@ -882,14 +885,14 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield layer.copy(tracingId = newTracingId) def duplicateVolumeTracing( - sourceAnnotationId: String, + sourceAnnotationId: ObjectId, sourceTracingId: String, sourceVersion: Long, - newAnnotationId: String, + newAnnotationId: ObjectId, newTracingId: String, newVersion: Long, - ownerId: String, - requestingUserId: String, + ownerId: ObjectId, + requestingUserId: ObjectId, isFromTask: Boolean, boundingBox: Option[BoundingBox], datasetBoundingBox: Option[BoundingBox], @@ -924,7 +927,7 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss duplicateEditableMapping(sourceAnnotationId, sourceTracingId, newTracingId, sourceVersion, newVersion)) } yield newTracingId - private def duplicateEditableMapping(sourceAnnotationId: String, + private def duplicateEditableMapping(sourceAnnotationId: ObjectId, sourceTracingId: String, newTracingId: String, sourceVersion: Long, @@ -940,13 +943,13 @@ class TSAnnotationService @Inject()(val remoteWebknossosClient: TSRemoteWebknoss } yield () def duplicateSkeletonTracing( - sourceAnnotationId: String, + sourceAnnotationId: ObjectId, sourceTracingId: String, sourceVersion: Long, newTracingId: String, newVersion: Long, - ownerId: String, - requestingUserId: String, + ownerId: ObjectId, + requestingUserId: ObjectId, isFromTask: Boolean, editPosition: Option[Vec3Int], editRotation: Option[Vec3Double], diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala index 6cb675b53a4..c4b156787b7 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala @@ -1,5 +1,6 @@ package com.scalableminds.webknossos.tracingstore.annotation +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.tracingstore.tracings.IdWithBoolUtils import com.scalableminds.webknossos.tracingstore.tracings.editablemapping.{ MergeAgglomerateUpdateAction, @@ -16,7 +17,7 @@ trait UpdateAction { def addInfo(info: Option[String]): UpdateAction - def addAuthorId(authorId: Option[String]): UpdateAction + def addAuthorId(authorId: Option[ObjectId]): UpdateAction def isViewOnlyChange: Boolean = false } @@ -266,7 +267,7 @@ object UpdateAction { case class UpdateActionGroup(version: Long, timestamp: Long, - authorId: Option[String], + authorId: Option[ObjectId], actions: List[UpdateAction], stats: Option[JsObject], info: Option[String], diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/EditableMappingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/EditableMappingController.scala index cf8db771d07..4e83b333082 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/EditableMappingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/EditableMappingController.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.controllers import com.google.inject.Inject +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.AgglomerateGraph.AgglomerateGraph import com.scalableminds.webknossos.datastore.ListOfLong.ListOfLong @@ -28,7 +29,7 @@ class EditableMappingController @Inject()( editableMappingService: EditableMappingService)(implicit ec: ExecutionContext, bodyParsers: PlayBodyParsers) extends Controller { - def editableMappingInfo(tracingId: String, annotationId: String, version: Option[Long]): Action[AnyContent] = + def editableMappingInfo(tracingId: String, annotationId: ObjectId, version: Option[Long]): Action[AnyContent] = Action.async { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { @@ -64,7 +65,7 @@ class EditableMappingController @Inject()( } } - def agglomerateIdsForSegments(tracingId: String, annotationId: String, version: Option[Long]): Action[ListOfLong] = + def agglomerateIdsForSegments(tracingId: String, annotationId: ObjectId, version: Option[Long]): Action[ListOfLong] = Action.async(validateProto[ListOfLong]) { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala index 7fb6589a658..ebe39e60fa7 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.controllers import com.google.inject.Inject import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.util.tools.JsonHelper.{boxFormat, optionFormat} import com.scalableminds.webknossos.datastore.SkeletonTracing.{ @@ -77,7 +78,7 @@ class SkeletonTracingController @Inject()(skeletonTracingService: SkeletonTracin } } - def get(tracingId: String, annotationId: String, version: Option[Long]): Action[AnyContent] = + def get(tracingId: String, annotationId: ObjectId, version: Option[Long]): Action[AnyContent] = Action.async { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { @@ -118,8 +119,8 @@ class SkeletonTracingController @Inject()(skeletonTracingService: SkeletonTracin // Used in task creation. History is dropped. Caller is responsible to create and save a matching AnnotationProto object def duplicate(tracingId: String, newTracingId: String, - ownerId: String, - requestingUserId: String, + ownerId: ObjectId, + requestingUserId: ObjectId, editPosition: Option[String], editRotation: Option[String], boundingBox: Option[String]): Action[AnyContent] = diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TSAnnotationController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TSAnnotationController.scala index d7a6bc8b90b..e1515957aff 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TSAnnotationController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TSAnnotationController.scala @@ -4,6 +4,7 @@ import collections.SequenceUtils import com.google.inject.Inject import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.geometry.BoundingBox +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.Annotation.{ AnnotationLayerProto, @@ -34,7 +35,7 @@ import play.api.mvc.{Action, AnyContent, PlayBodyParsers} import scala.concurrent.ExecutionContext -case class MergedFromIdsRequest(annotationIds: Seq[String], ownerIds: Seq[String]) +case class MergedFromIdsRequest(annotationIds: Seq[ObjectId], ownerIds: Seq[ObjectId]) object MergedFromIdsRequest { implicit val jsonFormat: OFormat[MergedFromIdsRequest] = Json.format[MergedFromIdsRequest] @@ -51,7 +52,7 @@ class TSAnnotationController @Inject()( extends Controller with KeyValueStoreImplicits { - def save(annotationId: String, toTemporaryStore: Boolean = false): Action[AnnotationProto] = + def save(annotationId: ObjectId, toTemporaryStore: Boolean = false): Action[AnnotationProto] = Action.async(validateProto[AnnotationProto]) { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.webknossos) { @@ -62,7 +63,7 @@ class TSAnnotationController @Inject()( } } - def update(annotationId: String): Action[List[UpdateActionGroup]] = + def update(annotationId: ObjectId): Action[List[UpdateActionGroup]] = Action.async(validateJson[List[UpdateActionGroup]]) { implicit request => log() { logTime(slackNotificationService.noticeSlowRequest) { @@ -75,7 +76,7 @@ class TSAnnotationController @Inject()( } } - def updateActionLog(annotationId: String, + def updateActionLog(annotationId: ObjectId, newestVersion: Option[Long] = None, oldestVersion: Option[Long] = None): Action[AnyContent] = Action.async { implicit request => log() { @@ -90,7 +91,7 @@ class TSAnnotationController @Inject()( } } - def newestVersion(annotationId: String): Action[AnyContent] = Action.async { implicit request => + def newestVersion(annotationId: ObjectId): Action[AnyContent] = Action.async { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { for { @@ -100,7 +101,7 @@ class TSAnnotationController @Inject()( } } - def get(annotationId: String, version: Option[Long]): Action[AnyContent] = + def get(annotationId: ObjectId, version: Option[Long]): Action[AnyContent] = Action.async { implicit request => log() { logTime(slackNotificationService.noticeSlowRequest) { @@ -113,10 +114,10 @@ class TSAnnotationController @Inject()( } } - def duplicate(annotationId: String, - newAnnotationId: String, - ownerId: String, - requestingUserId: String, + def duplicate(annotationId: ObjectId, + newAnnotationId: ObjectId, + ownerId: ObjectId, + requestingUserId: ObjectId, version: Option[Long], isFromTask: Boolean, datasetBoundingBox: Option[String]): Action[AnyContent] = @@ -139,7 +140,7 @@ class TSAnnotationController @Inject()( } } - def resetToBase(annotationId: String): Action[AnyContent] = + def resetToBase(annotationId: ObjectId): Action[AnyContent] = Action.async { implicit request => log() { logTime(slackNotificationService.noticeSlowRequest) { @@ -155,8 +156,10 @@ class TSAnnotationController @Inject()( } } - private def findAndAdaptVolumesForAnnotation(annotation: AnnotationProto, requestingUserId: String, ownerId: String)( - implicit tc: TokenContext): Fox[Seq[VolumeTracing]] = { + private def findAndAdaptVolumesForAnnotation( + annotation: AnnotationProto, + requestingUserId: ObjectId, + ownerId: ObjectId)(implicit tc: TokenContext): Fox[Seq[VolumeTracing]] = { val volumeLayersOfAnnotation = annotation.annotationLayers.filter(_.typ == AnnotationLayerTypeProto.Volume) for { volumeTracings <- annotationService @@ -173,8 +176,8 @@ class TSAnnotationController @Inject()( private def findAndAdaptSkeletonsForAnnotation( annotation: AnnotationProto, - requestingUserId: String, - ownerId: String)(implicit tc: TokenContext): Fox[Seq[SkeletonTracing]] = { + requestingUserId: ObjectId, + ownerId: ObjectId)(implicit tc: TokenContext): Fox[Seq[SkeletonTracing]] = { val skeletonLayersOfAnnotation = annotation.annotationLayers.filter(_.typ == AnnotationLayerTypeProto.Skeleton) for { skeletonTracings <- annotationService @@ -190,8 +193,8 @@ class TSAnnotationController @Inject()( } def mergedFromIds(toTemporaryStore: Boolean, - newAnnotationId: String, - requestingUserId: String): Action[MergedFromIdsRequest] = + newAnnotationId: ObjectId, + requestingUserId: ObjectId): Action[MergedFromIdsRequest] = Action.async(validateJson[MergedFromIdsRequest]) { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.webknossos) { diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala index 45419f1f9db..8ba6e734513 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.tracingstore.controllers import collections.SequenceUtils import com.google.inject.Inject import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.ExtendedTypes.ExtendedString import com.scalableminds.util.tools.Fox import com.scalableminds.util.tools.JsonHelper.optionFormat @@ -87,7 +88,7 @@ class VolumeTracingController @Inject()( } } - def get(tracingId: String, annotationId: String, version: Option[Long]): Action[AnyContent] = + def get(tracingId: String, annotationId: ObjectId, version: Option[Long]): Action[AnyContent] = Action.async { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { @@ -111,7 +112,7 @@ class VolumeTracingController @Inject()( } } - def initialData(annotationId: String, + def initialData(annotationId: ObjectId, tracingId: String, minMag: Option[Int], maxMag: Option[Int]): Action[AnyContent] = @@ -154,7 +155,7 @@ class VolumeTracingController @Inject()( } } - def initialDataMultiple(annotationId: String, tracingId: String): Action[AnyContent] = + def initialDataMultiple(annotationId: ObjectId, tracingId: String): Action[AnyContent] = Action.async { implicit request => log() { logTime(slackNotificationService.noticeSlowRequest) { @@ -175,7 +176,7 @@ class VolumeTracingController @Inject()( } def allDataZip(tracingId: String, - annotationId: Option[String], + annotationId: Option[ObjectId], version: Option[Long], volumeDataZipFormat: String, voxelSizeFactor: Option[String], @@ -207,7 +208,7 @@ class VolumeTracingController @Inject()( } } - def data(tracingId: String, annotationId: String): Action[List[WebknossosDataRequest]] = + def data(tracingId: String, annotationId: ObjectId): Action[List[WebknossosDataRequest]] = Action.async(validateJson[List[WebknossosDataRequest]]) { implicit request => log() { accessTokenService.validateAccessFromTokenContext(UserAccessRequest.readAnnotation(annotationId)) { @@ -374,10 +375,10 @@ class VolumeTracingController @Inject()( // Used in task creation. History is dropped. Caller is responsible to create and save a matching AnnotationProto object def duplicate(tracingId: String, - newAnnotationId: String, + newAnnotationId: ObjectId, newTracingId: String, - ownerId: String, - requestingUserId: String, + ownerId: ObjectId, + requestingUserId: ObjectId, minMag: Option[Int], maxMag: Option[Int], editPosition: Option[String], diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala index 9c24db0cb66..8ccd1b009a1 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala @@ -4,6 +4,7 @@ import com.google.inject.Inject import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.geometry.Vec3Int import com.scalableminds.util.mvc.ExtendedController +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing import com.scalableminds.webknossos.datastore.dataformats.MagLocator @@ -329,7 +330,7 @@ class VolumeTracingZarrStreamingController @Inject()( private def getFallbackLayerDataIfEmpty( tracing: VolumeTracing, - annotationId: String, + annotationId: ObjectId, data: Array[Byte], missingBucketIndices: List[Int], mag: Vec3Int, diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/AnnotationUserStateUtils.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/AnnotationUserStateUtils.scala index b6465b3a1b7..0ebcf67ef08 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/AnnotationUserStateUtils.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/AnnotationUserStateUtils.scala @@ -1,5 +1,6 @@ package com.scalableminds.webknossos.tracingstore.tracings +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.datastore.Annotation.{AnnotationProto, AnnotationUserStateProto} import com.scalableminds.webknossos.datastore.IdWithBool.{Id32WithBool, Id64WithBool} import com.scalableminds.webknossos.datastore.SkeletonTracing.{SkeletonTracing, SkeletonUserStateProto} @@ -131,14 +132,14 @@ trait AnnotationUserStateUtils extends BoundingBoxMerger with IdWithBoolUtils { // Since the owner may change in duplicate, we need to render what they would see into a single user state for them def renderSkeletonUserStateIntoUserState(s: SkeletonTracing, - requestingUserId: String, - ownerId: String): SkeletonUserStateProto = { - val ownerUserState = s.userStates.find(_.userId == ownerId).map(_.copy(userId = requestingUserId)) + requestingUserId: ObjectId, + ownerId: ObjectId): SkeletonUserStateProto = { + val ownerUserState = s.userStates.find(_.userId == ownerId.toString).map(_.copy(userId = requestingUserId.toString)) if (requestingUserId == ownerId) ownerUserState.getOrElse(SkeletonTracingDefaults.emptyUserState(requestingUserId)) else { - val requestingUserState = s.userStates.find(_.userId == requestingUserId) + val requestingUserState = s.userStates.find(_.userId == requestingUserId.toString) val requestingUserTreeVisibilityMap = id32WithBoolsToMap(requestingUserState.map(_.treeVisibilities)) val ownerTreeVisibilityMap = id32WithBoolsToMap(ownerUserState.map(_.treeVisibilities)) val mergedTreeVisibilityMap = ownerTreeVisibilityMap ++ requestingUserTreeVisibilityMap @@ -151,7 +152,7 @@ trait AnnotationUserStateUtils extends BoundingBoxMerger with IdWithBoolUtils { val mergedTreeGroupExpandedMap = ownerTreeGroupExpandedMap ++ requestingUserTreeGroupExpandedMap SkeletonUserStateProto( - userId = requestingUserId, + userId = requestingUserId.toString, activeNodeId = requestingUserState.flatMap(_.activeNodeId).orElse(ownerUserState.flatMap(_.activeNodeId)), treeGroupExpandedStates = mapToId32WithBools(mergedTreeGroupExpandedMap), boundingBoxVisibilities = mapToId32WithBools(mergedBoundingBoxVisibilityMap), @@ -162,14 +163,14 @@ trait AnnotationUserStateUtils extends BoundingBoxMerger with IdWithBoolUtils { // Since the owner may change in duplicate, we need to render what they would see into a single user state for them def renderVolumeUserStateIntoUserState(s: VolumeTracing, - requestingUserId: String, - ownerId: String): VolumeUserStateProto = { - val ownerUserState = s.userStates.find(_.userId == ownerId).map(_.copy(userId = requestingUserId)) + requestingUserId: ObjectId, + ownerId: ObjectId): VolumeUserStateProto = { + val ownerUserState = s.userStates.find(_.userId == ownerId.toString).map(_.copy(userId = requestingUserId.toString)) if (requestingUserId == ownerId) ownerUserState.getOrElse(VolumeTracingDefaults.emptyUserState(requestingUserId)) else { - val requestingUserState = s.userStates.find(_.userId == requestingUserId) + val requestingUserState = s.userStates.find(_.userId == requestingUserId.toString) val requestingUserBoundingBoxVisibilityMap = id32WithBoolsToMap( requestingUserState.map(_.boundingBoxVisibilities)) val ownerBoundingBoxVisibilityMap = id32WithBoolsToMap(ownerUserState.map(_.boundingBoxVisibilities)) @@ -182,7 +183,7 @@ trait AnnotationUserStateUtils extends BoundingBoxMerger with IdWithBoolUtils { val mergedSegmentGroupExpandedMap = ownerSegmentGroupExpandedMap ++ requestingUserSegmentGroupExpandedMap val mergedSegmentVisibilityMap = ownerSegmentVisibilityMap ++ requestingUserSegmentVisibilityMap VolumeUserStateProto( - userId = requestingUserId, + userId = requestingUserId.toString, activeSegmentId = requestingUserState.flatMap(_.activeSegmentId).orElse(ownerUserState.flatMap(_.activeSegmentId)), segmentGroupExpandedStates = mapToId32WithBools(mergedSegmentGroupExpandedMap), diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/RemoteFallbackLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/RemoteFallbackLayer.scala index ddf984b3b4d..4250e156bfe 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/RemoteFallbackLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/RemoteFallbackLayer.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing.ElementClassProto @@ -33,7 +34,7 @@ trait FallbackDataHelper extends FoxImplicits { private lazy val fallbackBucketDataCache: AlfuCache[FallbackDataKey, (Array[Byte], List[Int])] = AlfuCache(maxCapacity = 3000) - def remoteFallbackLayerForVolumeTracing(tracing: VolumeTracing, annotationId: String)( + def remoteFallbackLayerForVolumeTracing(tracing: VolumeTracing, annotationId: ObjectId)( implicit ec: ExecutionContext): Fox[RemoteFallbackLayer] = for { layerName <- tracing.fallbackLayer.toFox ?~> "This feature is only defined on volume annotations with fallback segmentation layer." diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TemporaryTracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TemporaryTracingService.scala index fbf64c0eda1..bd01b6cd7bd 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TemporaryTracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TemporaryTracingService.scala @@ -1,5 +1,6 @@ package com.scalableminds.webknossos.tracingstore.tracings +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.Annotation.AnnotationProto import com.scalableminds.webknossos.datastore.SkeletonTracing.SkeletonTracing @@ -37,10 +38,10 @@ class TemporaryTracingService @Inject()(skeletonStore: TemporaryTracingStore[Ske private def temporaryTracingIdKey(tracingId: String) = s"temporaryTracingId___$tracingId" - private def temporaryAnnotationIdKey(tracingId: String) = - s"temporaryTracingId___$tracingId" + private def temporaryAnnotationIdKey(annotationId: ObjectId) = + s"temporaryAnnotationId___$annotationId" - def getAnnotation(annotationId: String): Fox[AnnotationProto] = annotationStore.get(annotationId).toFox + def getAnnotation(annotationId: ObjectId): Fox[AnnotationProto] = annotationStore.get(annotationId.toString).toFox def getVolume(tracingId: String): Fox[VolumeTracing] = volumeStore.get(tracingId).toFox @@ -80,8 +81,8 @@ class TemporaryTracingService @Inject()(skeletonStore: TemporaryTracingStore[Ske Fox.successful(()) } - def saveAnnotationProto(annotationId: String, annotationProto: AnnotationProto): Fox[Unit] = { - annotationStore.insert(annotationId, annotationProto, Some(temporaryStoreTimeout)) + def saveAnnotationProto(annotationId: ObjectId, annotationProto: AnnotationProto): Fox[Unit] = { + annotationStore.insert(annotationId.toString, annotationProto, Some(temporaryStoreTimeout)) registerAnnotationId(annotationId) Fox.successful(()) } @@ -93,7 +94,7 @@ class TemporaryTracingService @Inject()(skeletonStore: TemporaryTracingStore[Ske Fox.successful(()) } - def isTemporaryAnnotation(annotationId: String): Fox[Boolean] = + def isTemporaryAnnotation(annotationId: ObjectId): Fox[Boolean] = temporaryTracingIdStore.contains(temporaryAnnotationIdKey(annotationId)) def isTemporaryTracing(tracingId: String): Fox[Boolean] = @@ -107,7 +108,7 @@ class TemporaryTracingService @Inject()(skeletonStore: TemporaryTracingStore[Ske private def registerTracingId(tracingId: String) = temporaryTracingIdStore.insertKey(temporaryTracingIdKey(tracingId), Some(temporaryIdStoreTimeout)) - private def registerAnnotationId(annotationId: String) = + private def registerAnnotationId(annotationId: ObjectId) = temporaryTracingIdStore.insertKey(temporaryAnnotationIdKey(annotationId), Some(temporaryIdStoreTimeout)) } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala index c245b13dfa5..d822ab2f7e2 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.editablemapping import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.{BoundingBox, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing import com.scalableminds.webknossos.datastore.dataformats.{BucketProvider, MagLocator} @@ -15,9 +16,9 @@ import com.scalableminds.webknossos.datastore.models.datasource.{ DataFormat, DataLayer, DataSourceId, + DatasetLayerAttachments, ElementClass, - SegmentationLayer, - DatasetLayerAttachments + SegmentationLayer } import ucar.ma2.{Array => MultiArray} import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction @@ -77,7 +78,7 @@ case class EditableMappingLayer(name: String, // set to tracing id largestSegmentId: Option[Long], elementClass: ElementClass.Value, tracing: VolumeTracing, - annotationId: String, + annotationId: ObjectId, annotationService: TSAnnotationService, editableMappingService: EditableMappingService) extends SegmentationLayer { diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingMergeService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingMergeService.scala index 36b115c039c..8ae50ef1a2e 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingMergeService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingMergeService.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.editablemapping import collections.SequenceUtils import com.scalableminds.util.accesscontext.TokenContext +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.EditableMappingInfo.EditableMappingInfo @@ -37,9 +38,9 @@ class EditableMappingMergeService @Inject()(val tracingDataStore: TracingDataSto * So that it itself can be merged again. * The earliestAccessibleVersion property ensures that the fully merged annotation is still the earliest accessible one. */ - def mergeEditableMappings(annotationIds: Seq[String], - firstVolumeAnnotationIdOpt: Option[String], - newAnnotationId: String, + def mergeEditableMappings(annotationIds: Seq[ObjectId], + firstVolumeAnnotationIdOpt: Option[ObjectId], + newAnnotationId: ObjectId, newVolumeTracingId: String, tracingsWithIds: Seq[(VolumeTracing, String)], toTemporaryStore: Boolean)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Long] = @@ -68,7 +69,9 @@ class EditableMappingMergeService @Inject()(val tracingDataStore: TracingDataSto Fox .serialCombined(linearizedEditableMappingUpdates) { update: UpdateAction => for { - _ <- annotationUpdatesPutBuffer.put(newAnnotationId, Json.toJson(List(update)), Some(updateVersion)) + _ <- annotationUpdatesPutBuffer.put(newAnnotationId.toString, + Json.toJson(List(update)), + Some(updateVersion)) _ = updateVersion += 1 } yield () } @@ -98,13 +101,13 @@ class EditableMappingMergeService @Inject()(val tracingDataStore: TracingDataSto Fox.failure("Cannot merge annotations with and without editable mappings") } - private def mergeEditableMappingUpdates(annotationIds: Seq[String], newTracingId: String)( + private def mergeEditableMappingUpdates(annotationIds: Seq[ObjectId], newTracingId: String)( implicit ec: ExecutionContext): Fox[List[EditableMappingUpdateAction]] = for { updatesByAnnotation <- Fox.serialCombined(annotationIds) { annotationId => for { - updateGroups <- tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple(annotationId)( - fromJsonBytes[List[UpdateAction]]) + updateGroups <- tracingDataStore.annotationUpdates.getMultipleVersionsAsVersionValueTuple( + annotationId.toString)(fromJsonBytes[List[UpdateAction]]) updatesIroned: Seq[UpdateAction] = ironOutReverts(updateGroups) editableMappingUpdates = updatesIroned.flatMap { case a: EditableMappingUpdateAction => Some(a.withActionTracingId(newTracingId)) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdateActions.scala index 9a8dcf07e74..472de842ad5 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdateActions.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.editablemapping import com.scalableminds.util.geometry.Vec3Int +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.tracingstore.annotation.{LayerUpdateAction, UpdateAction} import play.api.libs.json._ @@ -18,12 +19,12 @@ case class SplitAgglomerateUpdateAction(agglomerateId: Long, // Unused, we now l mag: Vec3Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends EditableMappingUpdateAction { override def addTimestamp(timestamp: Long): EditableMappingUpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): EditableMappingUpdateAction = this.copy(actionTracingId = newTracingId) @@ -44,12 +45,12 @@ case class MergeAgglomerateUpdateAction(agglomerateId1: Long, // Unused, we now mag: Vec3Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends EditableMappingUpdateAction { override def addTimestamp(timestamp: Long): EditableMappingUpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): EditableMappingUpdateAction = this.copy(actionTracingId = newTracingId) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdater.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdater.scala index 5f084769048..792d0c557f4 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdater.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingUpdater.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.editablemapping import com.scalableminds.util.accesscontext.TokenContext +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.AgglomerateGraph.{AgglomerateEdge, AgglomerateGraph} import com.scalableminds.webknossos.datastore.EditableMappingInfo.EditableMappingInfo @@ -32,7 +33,7 @@ import scala.jdk.CollectionConverters.CollectionHasAsScala // this results in only one version increment in the db per update group class EditableMappingUpdater( - annotationId: String, + annotationId: ObjectId, tracingId: String, baseMappingName: String, oldVersion: Long, diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala index bdc1e338063..85ac2b6562b 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.skeleton import com.google.inject.Inject import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.SkeletonTracing.{SkeletonTracing, TreeBody} import com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto @@ -60,8 +61,8 @@ class SkeletonTracingService @Inject()( editRotation: Option[Vec3Double], boundingBox: Option[BoundingBox], newVersion: Long, - ownerId: String, - requestingUserId: String): SkeletonTracing = { + ownerId: ObjectId, + requestingUserId: ObjectId): SkeletonTracing = { val taskBoundingBox = if (fromTask) { tracing.boundingBox.map { bb => val newId = if (tracing.userBoundingBoxes.isEmpty) 1 else tracing.userBoundingBoxes.map(_.id).max + 1 diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala index 8b0e6209cf0..7a6f4da503b 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.skeleton.updating import com.scalableminds.webknossos.tracingstore.tracings._ import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} import com.scalableminds.util.image.Color +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.TristateOptionJsonHelper import com.scalableminds.webknossos.datastore.IdWithBool.Id32WithBool import com.scalableminds.webknossos.datastore.SkeletonTracing.{ @@ -26,18 +27,18 @@ trait SkeletonUpdateAction extends LayerUpdateAction { trait UserStateSkeletonUpdateAction extends SkeletonUpdateAction with UserStateUpdateAction { - def actionAuthorId: Option[String] + def actionAuthorId: Option[ObjectId] def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto override def applyOn(tracing: SkeletonTracing): SkeletonTracing = actionAuthorId match { case None => tracing case Some(actionUserId) => - val userStateAlreadyExists = tracing.userStates.exists(state => actionUserId == state.userId) + val userStateAlreadyExists = tracing.userStates.exists(state => actionUserId.toString == state.userId) if (userStateAlreadyExists) { tracing.copy(userStates = tracing.userStates.map { userState => - if (actionUserId == userState.userId) applyOnUserState(tracing, actionUserId, Some(userState)) + if (actionUserId.toString == userState.userId) applyOnUserState(tracing, actionUserId, Some(userState)) else userState }) } else { @@ -59,7 +60,7 @@ case class CreateTreeSkeletonAction(id: Int, metadata: Option[Seq[MetadataEntry]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -85,7 +86,7 @@ case class CreateTreeSkeletonAction(id: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -96,7 +97,7 @@ case class CreateTreeSkeletonAction(id: Int, case class DeleteTreeSkeletonAction(id: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction { override def applyOn(tracing: SkeletonTracing): SkeletonTracing = @@ -105,7 +106,7 @@ case class DeleteTreeSkeletonAction(id: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -124,7 +125,7 @@ case class UpdateTreeSkeletonAction(id: Int, metadata: Option[Seq[MetadataEntry]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -147,7 +148,7 @@ case class UpdateTreeSkeletonAction(id: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -157,7 +158,7 @@ case class MergeTreeSkeletonAction(sourceId: Int, targetId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -179,7 +180,7 @@ case class MergeTreeSkeletonAction(sourceId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -192,7 +193,7 @@ case class MoveTreeComponentSkeletonAction(nodeIds: List[Int], targetId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -225,7 +226,7 @@ case class MoveTreeComponentSkeletonAction(nodeIds: List[Int], override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -238,7 +239,7 @@ case class CreateEdgeSkeletonAction(source: Int, treeId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -250,7 +251,7 @@ case class CreateEdgeSkeletonAction(source: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -263,7 +264,7 @@ case class DeleteEdgeSkeletonAction(source: Int, treeId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -275,7 +276,7 @@ case class DeleteEdgeSkeletonAction(source: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -296,7 +297,7 @@ case class CreateNodeSkeletonAction(id: Int, additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper @@ -324,7 +325,7 @@ case class CreateNodeSkeletonAction(id: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -345,7 +346,7 @@ case class UpdateNodeSkeletonAction(id: Int, additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper @@ -375,7 +376,7 @@ case class UpdateNodeSkeletonAction(id: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -387,7 +388,7 @@ case class DeleteNodeSkeletonAction(nodeId: Int, treeId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -402,7 +403,7 @@ case class DeleteNodeSkeletonAction(nodeId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -413,7 +414,7 @@ case class DeleteNodeSkeletonAction(nodeId: Int, case class UpdateTreeGroupsSkeletonAction(treeGroups: List[UpdateActionTreeGroup], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -423,7 +424,7 @@ case class UpdateTreeGroupsSkeletonAction(treeGroups: List[UpdateActionTreeGroup override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -433,12 +434,12 @@ case class UpdateTreeGroupsExpandedStateSkeletonAction(groupIds: List[Int], areExpanded: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateSkeletonUpdateAction { override def addTimestamp(timestamp: Long): SkeletonUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): SkeletonUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): SkeletonUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) @@ -447,7 +448,7 @@ case class UpdateTreeGroupsExpandedStateSkeletonAction(groupIds: List[Int], this.copy(actionTracingId = newTracingId) def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto = existingUserStateOpt.map { existingUserState => val expandedStateMapMutable = id32WithBoolsToMutableMap(existingUserState.treeGroupExpandedStates) @@ -471,7 +472,7 @@ case class UpdateTracingSkeletonAction(activeNode: Option[Int], userBoundingBox: Option[com.scalableminds.util.geometry.BoundingBox], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None, editPositionAdditionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends SkeletonUpdateAction @@ -489,7 +490,7 @@ case class UpdateTracingSkeletonAction(activeNode: Option[Int], override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -500,18 +501,18 @@ case class UpdateTracingSkeletonAction(activeNode: Option[Int], case class UpdateActiveNodeSkeletonAction(activeNode: Option[Int], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateSkeletonUpdateAction { override def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto = existingUserStateOpt.getOrElse(SkeletonTracingDefaults.emptyUserState(actionUserId)).copy(activeNodeId = activeNode) override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -523,12 +524,12 @@ case class UpdateTreeVisibilitySkeletonAction(treeId: Int, isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateSkeletonUpdateAction with SkeletonUpdateActionHelper { override def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto = existingUserStateOpt.map { existingUserState => val visibilityMap = id32WithBoolsToMutableMap(existingUserState.treeVisibilities) @@ -547,7 +548,7 @@ case class UpdateTreeVisibilitySkeletonAction(treeId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -559,13 +560,13 @@ case class UpdateTreeGroupVisibilitySkeletonAction(treeGroupId: Option[Int], // isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateSkeletonUpdateAction with SkeletonUpdateActionHelper { override def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto = { val treeIdsToUpdate: Seq[Int] = treeGroupId match { case None => tracing.trees.map(tree => tree.treeId) @@ -594,7 +595,7 @@ case class UpdateTreeGroupVisibilitySkeletonAction(treeGroupId: Option[Int], // override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -606,7 +607,7 @@ case class UpdateTreeEdgesVisibilitySkeletonAction(treeId: Int, edgesAreVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with SkeletonUpdateActionHelper { @@ -620,7 +621,7 @@ case class UpdateTreeEdgesVisibilitySkeletonAction(treeId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -631,7 +632,7 @@ case class UpdateTreeEdgesVisibilitySkeletonAction(treeId: Int, case class UpdateUserBoundingBoxesSkeletonAction(boundingBoxes: List[NamedBoundingBox], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction { override def applyOn(tracing: SkeletonTracing): SkeletonTracing = @@ -640,7 +641,7 @@ case class UpdateUserBoundingBoxesSkeletonAction(boundingBoxes: List[NamedBoundi override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -649,7 +650,7 @@ case class UpdateUserBoundingBoxesSkeletonAction(boundingBoxes: List[NamedBoundi case class AddUserBoundingBoxSkeletonAction(boundingBox: NamedBoundingBox, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction { override def applyOn(tracing: SkeletonTracing): SkeletonTracing = @@ -658,7 +659,7 @@ case class AddUserBoundingBoxSkeletonAction(boundingBox: NamedBoundingBox, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -667,7 +668,7 @@ case class AddUserBoundingBoxSkeletonAction(boundingBox: NamedBoundingBox, case class DeleteUserBoundingBoxSkeletonAction(boundingBoxId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction { override def applyOn(tracing: SkeletonTracing): SkeletonTracing = @@ -676,7 +677,7 @@ case class DeleteUserBoundingBoxSkeletonAction(boundingBoxId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -688,7 +689,7 @@ case class UpdateUserBoundingBoxSkeletonAction(boundingBoxId: Int, boundingBox: Option[Option[BoundingBox]], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends SkeletonUpdateAction with ProtoGeometryImplicits { @@ -713,7 +714,7 @@ case class UpdateUserBoundingBoxSkeletonAction(boundingBoxId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -723,12 +724,12 @@ case class UpdateUserBoundingBoxVisibilitySkeletonAction(boundingBoxId: Option[I isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateSkeletonUpdateAction { override def applyOnUserState(tracing: SkeletonTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[SkeletonUserStateProto]): SkeletonUserStateProto = { val bboxIdsToUpdate = boundingBoxId.map(Seq(_)).getOrElse(tracing.userBoundingBoxes.map(_.id)) existingUserStateOpt.map { existingUserState => @@ -746,7 +747,7 @@ case class UpdateUserBoundingBoxVisibilitySkeletonAction(boundingBoxId: Option[I override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/TSFullMeshService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/TSFullMeshService.scala index 317df386a24..2e737502d84 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/TSFullMeshService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/TSFullMeshService.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.geometry.Vec3Int +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing @@ -36,7 +37,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, with FoxImplicits with LazyLogging { - def loadFor(annotationId: String, tracingId: String, fullMeshRequest: FullMeshRequest)( + def loadFor(annotationId: ObjectId, tracingId: String, fullMeshRequest: FullMeshRequest)( implicit ec: ExecutionContext, tc: TokenContext): Fox[Array[Byte]] = for { @@ -47,7 +48,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, } yield data private def loadFullMeshFromMeshfile( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, fullMeshRequest: FullMeshRequest)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Array[Byte]] = @@ -63,7 +64,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, } yield array private def loadFullMeshFromAdHoc( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, fullMeshRequest: FullMeshRequest)(implicit ec: ExecutionContext, tc: TokenContext): Fox[Array[Byte]] = @@ -91,7 +92,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, } yield array private def getAllAdHocChunksWithSegmentIndex( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, mag: Vec3Int, @@ -133,7 +134,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, allVertices = vertexChunksWithNeighbors.map(_._1) } yield allVertices - private def getAllAdHocChunksWithNeighborLogic(annotationId: String, + private def getAllAdHocChunksWithNeighborLogic(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, mag: Vec3Int, @@ -176,7 +177,7 @@ class TSFullMeshService @Inject()(volumeTracingService: VolumeTracingService, } yield allVertices private def loadMeshChunkFromAdHoc( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, adHocMeshRequest: WebknossosAdHocMeshRequest)(implicit tc: TokenContext): Fox[(Array[Float], List[Int])] = diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala index 8cd2cde738c..7f447221b83 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.geometry.{BoundingBox, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.geometry.Vec3IntProto import com.scalableminds.webknossos.datastore.helpers.{NativeBucketScanner, ProtoGeometryImplicits, SegmentStatistics} @@ -26,7 +27,7 @@ class VolumeSegmentStatisticsService @Inject()(volumeTracingService: VolumeTraci protected lazy val bucketScanner = new NativeBucketScanner() // Returns the segment volume (=number of voxels) in the target mag - def getSegmentVolume(annotationId: String, + def getSegmentVolume(annotationId: ObjectId, tracingId: String, segmentId: Long, mag: Vec3Int, @@ -41,7 +42,7 @@ class VolumeSegmentStatisticsService @Inject()(volumeTracingService: VolumeTraci getDataForBucketPositions(annotationId, tracingId) ) - def getSegmentBoundingBox(annotationId: String, + def getSegmentBoundingBox(annotationId: ObjectId, tracingId: String, segmentId: Long, mag: Vec3Int, @@ -57,7 +58,7 @@ class VolumeSegmentStatisticsService @Inject()(volumeTracingService: VolumeTraci getDataForBucketPositions(annotationId, tracingId) ) - private def getDataForBucketPositions(annotationId: String, tracingId: String)( + private def getDataForBucketPositions(annotationId: ObjectId, tracingId: String)( bucketPositions: Seq[Vec3Int], mag: Vec3Int, additionalCoordinates: Option[Seq[AdditionalCoordinate]])( @@ -87,7 +88,7 @@ class VolumeSegmentStatisticsService @Inject()(volumeTracingService: VolumeTraci includeFallbackDataIfAvailable = true) } yield (bucketDataBoxes, elementClassFromProto(tracing.elementClass)) - private def getBucketPositions(annotationId: String, + private def getBucketPositions(annotationId: ObjectId, tracingId: String, mappingName: Option[String], additionalCoordinates: Option[Seq[AdditionalCoordinate]])( diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDefaults.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDefaults.scala index 89982d0b1ad..28d09f57cc4 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDefaults.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDefaults.scala @@ -1,5 +1,6 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeUserStateProto import com.scalableminds.webknossos.datastore.models.datasource.ElementClass import com.scalableminds.webknossos.datastore.geometry.Vec3DoubleProto @@ -15,8 +16,8 @@ object VolumeTracingDefaults { val zoomLevel = 1.0 - def emptyUserState(userId: String): VolumeUserStateProto = VolumeUserStateProto( - userId = userId, + def emptyUserState(userId: ObjectId): VolumeUserStateProto = VolumeUserStateProto( + userId = userId.toString, activeSegmentId = None, segmentGroupExpandedStates = Seq.empty, boundingBoxVisibilities = Seq.empty, diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala index eb1bb302c9a..67a4c8528e6 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume import com.scalableminds.util.accesscontext.TokenContext import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.{BoundingBox, Vec3Int} +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing import com.scalableminds.webknossos.datastore.dataformats.{BucketProvider, MagLocator} @@ -76,7 +77,7 @@ class TemporaryVolumeTracingBucketProvider(layer: VolumeTracingLayer)(implicit v case class VolumeTracingLayer( name: String, - annotationId: String, + annotationId: ObjectId, volumeTracingService: VolumeTracingService, temporaryTracingService: TemporaryTracingService, isTemporaryTracing: Boolean = false, diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala index 29a4ee06424..8aefa6210fc 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala @@ -6,6 +6,7 @@ import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} import com.scalableminds.util.io.{NamedStream, ZipIO} import com.scalableminds.util.mvc.Formatter +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing @@ -76,8 +77,8 @@ class VolumeTracingService @Inject()( adHocMeshServiceHolder.tracingStoreAdHocMeshConfig = (binaryDataService, 30 seconds, 1) val adHocMeshService: AdHocMeshService = adHocMeshServiceHolder.tracingStoreAdHocMeshService - // (tracingId, fallbackLayerNameOpt, userTokenOpt) → remoteFallbackLayerOpt - private val fallbackLayerCache: AlfuCache[(String, Option[String], Option[String]), Option[RemoteFallbackLayer]] = + // (annotationId, fallbackLayerNameOpt, userTokenOpt) → remoteFallbackLayerOpt + private val fallbackLayerCache: AlfuCache[(ObjectId, Option[String], Option[String]), Option[RemoteFallbackLayer]] = AlfuCache(maxCapacity = 100) def saveVolume(tracingId: String, @@ -103,7 +104,7 @@ class VolumeTracingService @Inject()( editableMappingTracingId) ?~> "volumeSegmentIndex.update.failed" def applyBucketMutatingActions(tracingId: String, - annotationId: String, + annotationId: ObjectId, tracing: VolumeTracing, updateActions: List[BucketMutatingVolumeUpdateAction], newVersion: Long)(implicit tc: TokenContext): Fox[Unit] = @@ -191,7 +192,7 @@ class VolumeTracingService @Inject()( else Fox.successful(tracing.mappingName) private def deleteSegmentData(tracingId: String, - annotationId: String, + annotationId: ObjectId, volumeTracing: VolumeTracing, a: DeleteSegmentDataVolumeAction, segmentIndexBuffer: VolumeSegmentIndexBuffer, @@ -258,7 +259,7 @@ class VolumeTracingService @Inject()( } def revertVolumeData(tracingId: String, - annotationId: String, + annotationId: ObjectId, sourceVersion: Long, sourceTracing: VolumeTracing, newVersion: Long, @@ -326,7 +327,7 @@ class VolumeTracingService @Inject()( } yield () } - def initializeWithDataMultiple(annotationId: String, tracingId: String, tracing: VolumeTracing, initialData: File)( + def initializeWithDataMultiple(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, initialData: File)( implicit mp: MessagesProvider, tc: TokenContext): Fox[Set[Vec3Int]] = if (tracing.version != 0L) @@ -397,7 +398,7 @@ class VolumeTracingService @Inject()( } yield mags } - def initializeWithData(annotationId: String, + def initializeWithData(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, initialData: File, @@ -449,7 +450,7 @@ class VolumeTracingService @Inject()( } } - def allDataZip(annotationId: String, + def allDataZip(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, volumeDataZipFormat: VolumeDataZipFormat, @@ -459,7 +460,7 @@ class VolumeTracingService @Inject()( allDataToOutputStream(annotationId, tracingId, tracing, volumeDataZipFormat, voxelSize, os).map(_ => zipped) } - private def allDataToOutputStream(annotationId: String, + private def allDataToOutputStream(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, volumeDataZipFormmat: VolumeDataZipFormat, @@ -489,7 +490,7 @@ class VolumeTracingService @Inject()( zipResult } - def data(annotationId: String, + def data(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, dataRequests: DataRequestCollection, @@ -507,7 +508,7 @@ class VolumeTracingService @Inject()( } yield data def dataBucketBoxes( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, dataRequests: DataRequestCollection, @@ -525,7 +526,7 @@ class VolumeTracingService @Inject()( } yield data def adaptVolumeForDuplicate( - sourceAnnotationId: String, + sourceAnnotationId: ObjectId, newTracingId: String, sourceTracing: VolumeTracing, isFromTask: Boolean, @@ -535,8 +536,8 @@ class VolumeTracingService @Inject()( editPosition: Option[Vec3Int], editRotation: Option[Vec3Double], newVersion: Long, - ownerId: String, - requestingUserId: String)(implicit ec: ExecutionContext, tc: TokenContext): Fox[VolumeTracing] = { + ownerId: ObjectId, + requestingUserId: ObjectId)(implicit ec: ExecutionContext, tc: TokenContext): Fox[VolumeTracing] = { val tracingWithBB = addBoundingBoxFromTaskIfRequired(sourceTracing, isFromTask, datasetBoundingBox) val tracingWithMagRestrictions = VolumeTracingMags.restrictMagList(tracingWithBB, magRestrictions) for { @@ -577,10 +578,10 @@ class VolumeTracingService @Inject()( case _ => tracing } - def duplicateVolumeData(sourceAnnotationId: String, + def duplicateVolumeData(sourceAnnotationId: ObjectId, sourceTracingId: String, sourceTracing: VolumeTracing, - newAnnotationId: String, + newAnnotationId: ObjectId, newTracingId: String, newTracing: VolumeTracing)(implicit tc: TokenContext): Fox[Unit] = { var bucketCount = 0 @@ -638,7 +639,7 @@ class VolumeTracingService @Inject()( } private def volumeTracingLayer( - annotationId: String, + annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, isTemporaryTracing: Boolean = false, @@ -666,7 +667,7 @@ class VolumeTracingService @Inject()( def volumeBucketsAreEmpty(tracingId: String): Boolean = volumeDataStore.getMultipleKeys(None, Some(tracingId), limit = Some(1))(toBox).isEmpty - def createAdHocMesh(annotationId: String, + def createAdHocMesh(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, request: WebknossosAdHocMeshRequest)(implicit tc: TokenContext): Fox[(Array[Float], List[Int])] = @@ -692,7 +693,7 @@ class VolumeTracingService @Inject()( result <- adHocMeshService.requestAdHocMeshViaActor(adHocMeshRequest) } yield result - def findData(annotationId: String, tracingId: String, tracing: VolumeTracing)( + def findData(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing)( implicit tc: TokenContext): Fox[Option[Vec3Int]] = for { _ <- Fox.successful(()) @@ -793,7 +794,7 @@ class VolumeTracingService @Inject()( } def mergeVolumeData( - firstVolumeAnnotationIdOpt: Option[String], + firstVolumeAnnotationIdOpt: Option[ObjectId], volumeTracingIds: Seq[String], volumeTracings: Seq[VolumeTracing], newVolumeTracingId: String, @@ -801,7 +802,7 @@ class VolumeTracingService @Inject()( toTemporaryStore: Boolean)(implicit mp: MessagesProvider, tc: TokenContext): Fox[MergedVolumeStats] = { val before = Instant.now val volumeLayers = volumeTracingIds.zip(volumeTracings).map { - case (tracingId, tracing) => volumeTracingLayer("annotationIdUnusedInThisContext", tracingId, tracing) + case (tracingId, tracing) => volumeTracingLayer(ObjectId("annotationIdUnusedInThisContext"), tracingId, tracing) } val elementClassProto = volumeLayers.headOption.map(_.tracing.elementClass).getOrElse(ElementClassProto.uint8) @@ -895,7 +896,7 @@ class VolumeTracingService @Inject()( } } - def importVolumeData(annotationId: String, + def importVolumeData(annotationId: ObjectId, tracingId: String, tracing: VolumeTracing, zipFile: File, @@ -961,12 +962,12 @@ class VolumeTracingService @Inject()( } } - def getFallbackLayer(annotationId: String, tracing: VolumeTracing)( + def getFallbackLayer(annotationId: ObjectId, tracing: VolumeTracing)( implicit tc: TokenContext): Fox[Option[RemoteFallbackLayer]] = fallbackLayerCache.getOrLoad((annotationId, tracing.fallbackLayer, tc.userTokenOpt), t => getFallbackLayerFromWebknossos(t._1, t._2)) - private def getFallbackLayerFromWebknossos(annotationId: String, fallbackLayerName: Option[String])( + private def getFallbackLayerFromWebknossos(annotationId: ObjectId, fallbackLayerName: Option[String])( implicit tc: TokenContext): Fox[Option[RemoteFallbackLayer]] = for { dataSource <- remoteWebknossosClient.getDataSourceForAnnotation(annotationId) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala index e9e4fb879b5..ab19ff81b43 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala @@ -2,6 +2,7 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} import com.scalableminds.util.image.Color +import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.tools.TristateOptionJsonHelper import com.scalableminds.webknossos.datastore.IdWithBool.{Id32WithBool, Id64WithBool} import com.scalableminds.webknossos.datastore.VolumeTracing.{Segment, SegmentGroup, VolumeTracing, VolumeUserStateProto} @@ -39,19 +40,20 @@ trait BucketMutatingVolumeUpdateAction extends ApplyableVolumeUpdateAction { } trait UserStateVolumeUpdateAction extends ApplyableVolumeUpdateAction with UserStateUpdateAction { - def actionAuthorId: Option[String] + def actionAuthorId: Option[ObjectId] def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto override def applyOn(tracing: VolumeTracing): VolumeTracing = actionAuthorId match { case None => tracing case Some(actionUserId) => - val userStateAlreadyExists = tracing.userStates.exists(state => actionUserId == state.userId) + val userStateAlreadyExists = tracing.userStates.exists(state => actionUserId.toString == state.userId) if (userStateAlreadyExists) { tracing.copy(userStates = tracing.userStates.map { - case userState if actionUserId == userState.userId => applyOnUserState(tracing, actionUserId, Some(userState)) - case userState => userState + case userState if actionUserId.toString == userState.userId => + applyOnUserState(tracing, actionUserId, Some(userState)) + case userState => userState }) } else { tracing.copy(userStates = tracing.userStates :+ applyOnUserState(tracing, actionUserId, None)) @@ -66,12 +68,12 @@ case class UpdateBucketVolumeAction(position: Vec3Int, additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends BucketMutatingVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -99,12 +101,12 @@ case class UpdateTracingVolumeAction( hideUnregisteredSegments: Option[Boolean] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None ) extends ApplyableVolumeUpdateAction with ProtoGeometryImplicits { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -127,11 +129,11 @@ case class UpdateTracingVolumeAction( case class UpdateActiveSegmentIdVolumeAction(activeSegmentId: Long, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -140,7 +142,7 @@ case class UpdateActiveSegmentIdVolumeAction(activeSegmentId: Long, override def isViewOnlyChange: Boolean = true override def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto = existingUserStateOpt .getOrElse(VolumeTracingDefaults.emptyUserState(actionUserId)) @@ -150,11 +152,11 @@ case class UpdateActiveSegmentIdVolumeAction(activeSegmentId: Long, case class UpdateLargestSegmentIdVolumeAction(largestSegmentId: Long, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -167,12 +169,12 @@ case class UpdateLargestSegmentIdVolumeAction(largestSegmentId: Long, case class UpdateUserBoundingBoxesVolumeAction(boundingBoxes: List[NamedBoundingBox], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -185,7 +187,7 @@ case class UpdateUserBoundingBoxesVolumeAction(boundingBoxes: List[NamedBounding case class AddUserBoundingBoxVolumeAction(boundingBox: NamedBoundingBox, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def applyOn(tracing: VolumeTracing): VolumeTracing = @@ -194,7 +196,7 @@ case class AddUserBoundingBoxVolumeAction(boundingBox: NamedBoundingBox, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -203,7 +205,7 @@ case class AddUserBoundingBoxVolumeAction(boundingBox: NamedBoundingBox, case class DeleteUserBoundingBoxVolumeAction(boundingBoxId: Int, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def applyOn(tracing: VolumeTracing): VolumeTracing = @@ -212,7 +214,7 @@ case class DeleteUserBoundingBoxVolumeAction(boundingBoxId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -224,7 +226,7 @@ case class UpdateUserBoundingBoxVolumeAction(boundingBoxId: Int, boundingBox: Option[Option[BoundingBox]], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction with ProtoGeometryImplicits { @@ -249,7 +251,7 @@ case class UpdateUserBoundingBoxVolumeAction(boundingBoxId: Int, override def addTimestamp(timestamp: Long): UpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) - override def addAuthorId(authorId: Option[String]): UpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): UpdateAction = this.copy(actionAuthorId = authorId) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) @@ -259,18 +261,18 @@ case class UpdateUserBoundingBoxVisibilityVolumeAction(boundingBoxId: Option[Int isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) override def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto = { val bboxIdsToUpdate = boundingBoxId.map(Seq(_)).getOrElse(tracing.userBoundingBoxes.map(_.id)) existingUserStateOpt.map { existingUserState => @@ -290,11 +292,11 @@ case class UpdateUserBoundingBoxVisibilityVolumeAction(boundingBoxId: Option[Int case class RemoveFallbackLayerVolumeAction(actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -307,11 +309,11 @@ case class RemoveFallbackLayerVolumeAction(actionTracingId: String, case class ImportVolumeDataVolumeAction(actionTracingId: String, largestSegmentId: Option[Long], actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -324,11 +326,11 @@ case class ImportVolumeDataVolumeAction(actionTracingId: String, // The current code no longer creates these actions, but they are in the history of some volume annotations. case class AddSegmentIndexVolumeAction(actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -349,14 +351,14 @@ case class CreateSegmentVolumeAction(id: Long, metadata: Option[Seq[MetadataEntry]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction with ProtoGeometryImplicits { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -388,7 +390,7 @@ case class UpdateSegmentVolumeAction(id: Long, metadata: Option[Seq[MetadataEntry]] = None, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction with ProtoGeometryImplicits @@ -396,7 +398,7 @@ case class UpdateSegmentVolumeAction(id: Long, override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -420,13 +422,13 @@ case class UpdateSegmentVolumeAction(id: Long, case class DeleteSegmentVolumeAction(id: Long, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -440,11 +442,11 @@ case class DeleteSegmentVolumeAction(id: Long, case class DeleteSegmentDataVolumeAction(id: Long, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends BucketMutatingVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -456,11 +458,11 @@ case class UpdateMappingNameVolumeAction(mappingName: Option[String], isLocked: Option[Boolean], actionTracingId: String, actionTimestamp: Option[Long], - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -477,7 +479,7 @@ case class UpdateMappingNameVolumeAction(mappingName: Option[String], case class UpdateSegmentGroupsVolumeAction(segmentGroups: List[UpdateActionSegmentGroup], actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends ApplyableVolumeUpdateAction with VolumeUpdateActionHelper { @@ -485,7 +487,7 @@ case class UpdateSegmentGroupsVolumeAction(segmentGroups: List[UpdateActionSegme tracing.withSegmentGroups(segmentGroups.map(convertSegmentGroup)) override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -496,18 +498,18 @@ case class UpdateSegmentGroupsExpandedStateVolumeAction(groupIds: List[Int], areExpanded: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateVolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = this.copy(actionTracingId = newTracingId) override def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto = existingUserStateOpt.map { existingUserState => val expandedStateMapMutable = id32WithBoolsToMutableMap(existingUserState.segmentGroupExpandedStates) @@ -526,13 +528,13 @@ case class UpdateSegmentVisibilityVolumeAction(id: Long, isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateVolumeUpdateAction with VolumeUpdateActionHelper { def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto = existingUserStateOpt.map { existingUserState => val visibilityMap = id64WithBoolsToMutableMap(existingUserState.segmentVisibilities) @@ -549,7 +551,7 @@ case class UpdateSegmentVisibilityVolumeAction(id: Long, ) override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -560,13 +562,13 @@ case class UpdateSegmentGroupVisibilityVolumeAction(groupId: Option[Long], // No isVisible: Boolean, actionTracingId: String, actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, info: Option[String] = None) extends UserStateVolumeUpdateAction with VolumeUpdateActionHelper { override def applyOnUserState(tracing: VolumeTracing, - actionUserId: String, + actionUserId: ObjectId, existingUserStateOpt: Option[VolumeUserStateProto]): VolumeUserStateProto = { val segmentIdsToUpdate: Seq[Long] = groupId match { case None => tracing.segments.map(segment => segment.segmentId) @@ -593,7 +595,7 @@ case class UpdateSegmentGroupVisibilityVolumeAction(groupId: Option[Long], // No } override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this.copy(info = info) override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -606,11 +608,11 @@ case class UpdateSegmentGroupVisibilityVolumeAction(groupId: Option[Long], // No case class CompactVolumeUpdateAction(name: String, actionTracingId: String, actionTimestamp: Option[Long], - actionAuthorId: Option[String] = None, + actionAuthorId: Option[ObjectId] = None, value: JsObject) extends VolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) - override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = + override def addAuthorId(authorId: Option[ObjectId]): VolumeUpdateAction = this.copy(actionAuthorId = authorId) override def addInfo(info: Option[String]): UpdateAction = this override def withActionTracingId(newTracingId: String): LayerUpdateAction = @@ -624,7 +626,7 @@ object CompactVolumeUpdateAction { name <- (json \ "name").validate[String] actionTracingId <- (json \ "value" \ "actionTracingId").validate[String] actionTimestamp <- (json \ "value" \ "actionTimestamp").validateOpt[Long] - actionAuthorId <- (json \ "value" \ "actionAuthorId").validateOpt[String] + actionAuthorId <- (json \ "value" \ "actionAuthorId").validateOpt[ObjectId] value <- (json \ "value").validate[JsObject].map(_ - "actionTracingId" - "actionTimestamp" - "actionAuthorId") } yield CompactVolumeUpdateAction(name, actionTracingId, actionTimestamp, actionAuthorId, value) diff --git a/webknossos-tracingstore/conf/com.scalableminds.webknossos.tracingstore.routes b/webknossos-tracingstore/conf/com.scalableminds.webknossos.tracingstore.routes index 56237761bec..10d6a12646b 100644 --- a/webknossos-tracingstore/conf/com.scalableminds.webknossos.tracingstore.routes +++ b/webknossos-tracingstore/conf/com.scalableminds.webknossos.tracingstore.routes @@ -5,25 +5,25 @@ GET /health @com.scalableminds.webknossos.tracingstore.controllers.Application.health # Annotations (concerns AnnotationProto, not annotation info as stored in postgres) -POST /annotation/save @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.save(annotationId: String) -GET /annotation/:annotationId @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.get(annotationId: String, version: Option[Long]) -POST /annotation/:annotationId/update @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.update(annotationId: String) -GET /annotation/:annotationId/updateActionLog @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.updateActionLog(annotationId: String, newestVersion: Option[Long], oldestVersion: Option[Long]) -GET /annotation/:annotationId/newestVersion @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.newestVersion(annotationId: String) -POST /annotation/:annotationId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.duplicate(annotationId: String, newAnnotationId: String, ownerId: String, requestingUserId: String, version: Option[Long], isFromTask: Boolean, datasetBoundingBox: Option[String]) -POST /annotation/:annotationId/resetToBase @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.resetToBase(annotationId: String) -POST /annotation/mergedFromIds @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.mergedFromIds(toTemporaryStore: Boolean, newAnnotationId: String, requestingUserId: String) +POST /annotation/save @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.save(annotationId: ObjectId) +GET /annotation/:annotationId @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.get(annotationId: ObjectId, version: Option[Long]) +POST /annotation/:annotationId/update @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.update(annotationId: ObjectId) +GET /annotation/:annotationId/updateActionLog @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.updateActionLog(annotationId: ObjectId, newestVersion: Option[Long], oldestVersion: Option[Long]) +GET /annotation/:annotationId/newestVersion @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.newestVersion(annotationId: ObjectId) +POST /annotation/:annotationId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.duplicate(annotationId: ObjectId, newAnnotationId: ObjectId, ownerId: ObjectId, requestingUserId: ObjectId, version: Option[Long], isFromTask: Boolean, datasetBoundingBox: Option[String]) +POST /annotation/:annotationId/resetToBase @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.resetToBase(annotationId: ObjectId) +POST /annotation/mergedFromIds @com.scalableminds.webknossos.tracingstore.controllers.TSAnnotationController.mergedFromIds(toTemporaryStore: Boolean, newAnnotationId: ObjectId, requestingUserId: ObjectId) # Volume tracings POST /volume/save @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.save(newTracingId: String) -POST /volume/:tracingId/initialData @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.initialData(annotationId: String, tracingId: String, minMag: Option[Int], maxMag: Option[Int]) -POST /volume/:tracingId/initialDataMultiple @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.initialDataMultiple(annotationId: String, tracingId: String) -GET /volume/:tracingId @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.get(tracingId: String, annotationId: String, version: Option[Long]) -GET /volume/:tracingId/allDataZip @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.allDataZip(tracingId: String, annotationId: Option[String], version: Option[Long], volumeDataZipFormat: String, voxelSize: Option[String], voxelSizeUnit: Option[String]) -POST /volume/:tracingId/data @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.data(tracingId: String, annotationId: String) +POST /volume/:tracingId/initialData @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.initialData(annotationId: ObjectId, tracingId: String, minMag: Option[Int], maxMag: Option[Int]) +POST /volume/:tracingId/initialDataMultiple @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.initialDataMultiple(annotationId: ObjectId, tracingId: String) +GET /volume/:tracingId @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.get(tracingId: String, annotationId: ObjectId, version: Option[Long]) +GET /volume/:tracingId/allDataZip @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.allDataZip(tracingId: String, annotationId: Option[ObjectId], version: Option[Long], volumeDataZipFormat: String, voxelSize: Option[String], voxelSizeUnit: Option[String]) +POST /volume/:tracingId/data @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.data(tracingId: String, annotationId: ObjectId) POST /volume/:tracingId/adHocMesh @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.requestAdHocMesh(tracingId: String) POST /volume/:tracingId/fullMesh.stl @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.loadFullMeshStl(tracingId: String) -POST /volume/:tracingId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.duplicate(tracingId: String, newAnnotationId: String, newTracingId: String, ownerId: String, requestingUserId: String, minMag: Option[Int], maxMag: Option[Int], editPosition: Option[String], editRotation: Option[String], boundingBox: Option[String]) +POST /volume/:tracingId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.duplicate(tracingId: String, newAnnotationId: ObjectId, newTracingId: String, ownerId: ObjectId, requestingUserId: ObjectId, minMag: Option[Int], maxMag: Option[Int], editPosition: Option[String], editRotation: Option[String], boundingBox: Option[String]) POST /volume/:tracingId/segmentIndex/:segmentId @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.getSegmentIndex(tracingId: String, segmentId: Long) POST /volume/:tracingId/importVolumeData @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.importVolumeData(tracingId: String) GET /volume/:tracingId/findData @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.findData(tracingId: String) @@ -33,9 +33,9 @@ POST /volume/getMultiple POST /volume/mergedFromContents @com.scalableminds.webknossos.tracingstore.controllers.VolumeTracingController.mergedFromContents(newTracingId: String) # Editable Mappings -GET /mapping/:tracingId/info @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.editableMappingInfo(tracingId: String, annotationId: String, version: Option[Long]) +GET /mapping/:tracingId/info @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.editableMappingInfo(tracingId: String, annotationId: ObjectId, version: Option[Long]) GET /mapping/:tracingId/segmentsForAgglomerate @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.segmentIdsForAgglomerate(tracingId: String, agglomerateId: Long) -POST /mapping/:tracingId/agglomeratesForSegments @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.agglomerateIdsForSegments(tracingId: String, annotationId: String, version: Option[Long]) +POST /mapping/:tracingId/agglomeratesForSegments @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.agglomerateIdsForSegments(tracingId: String, annotationId: ObjectId, version: Option[Long]) POST /mapping/:tracingId/agglomerateGraphMinCut @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.agglomerateGraphMinCut(tracingId: String) POST /mapping/:tracingId/agglomerateGraphNeighbors @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.agglomerateGraphNeighbors(tracingId: String) GET /mapping/:tracingId/agglomerateSkeleton/:agglomerateId @com.scalableminds.webknossos.tracingstore.controllers.EditableMappingController.agglomerateSkeleton(tracingId: String, agglomerateId: Long) @@ -70,6 +70,6 @@ GET /volume/zarr3_experimental/:tracingId/:mag/:coordinates POST /skeleton/save @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.save(newTracingId: String) POST /skeleton/saveMultiple @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.saveMultiple() POST /skeleton/mergedFromContents @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.mergedFromContents(newTracingId: String) -GET /skeleton/:tracingId @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.get(tracingId: String, annotationId: String, version: Option[Long]) -POST /skeleton/:tracingId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.duplicate(tracingId: String, newTracingId: String, ownerId: String, requestingUserId: String, editPosition: Option[String], editRotation: Option[String], boundingBox: Option[String]) +GET /skeleton/:tracingId @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.get(tracingId: String, annotationId: ObjectId, version: Option[Long]) +POST /skeleton/:tracingId/duplicate @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.duplicate(tracingId: String, newTracingId: String, ownerId: ObjectId, requestingUserId: ObjectId, editPosition: Option[String], editRotation: Option[String], boundingBox: Option[String]) POST /skeleton/getMultiple @com.scalableminds.webknossos.tracingstore.controllers.SkeletonTracingController.getMultiple