Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

WIP: Unified annotation versioning #7917

Draft
wants to merge 184 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 167 commits
Commits
Show all changes
184 commits
Select commit Hold shift + click to select a range
a955217
WIP: Unified Annotation Versioning
fm3 Jun 18, 2024
a4361fe
initialize route
fm3 Jun 19, 2024
5a60d17
update actions
fm3 Jun 20, 2024
a698f11
error case
fm3 Jun 20, 2024
9113ee1
fix injector
fm3 Jun 20, 2024
332eb19
sparse versions
fm3 Jul 8, 2024
b46af63
WIP: revert actions for editable mappings
fm3 Jul 8, 2024
8f1094d
wip: revert editable mappings
fm3 Jul 8, 2024
4b7d7fb
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 8, 2024
f300feb
iterate on reverting editable mappings
fm3 Jul 9, 2024
9f99feb
revert agglomerateToGraph + segmentToAgglomerate in buffers
fm3 Jul 9, 2024
130e1f7
use right key
fm3 Jul 9, 2024
1325cac
fix version check, fix json serialization of update action
fm3 Jul 9, 2024
2e62832
wip update route
fm3 Jul 9, 2024
950ab98
annotationUpdates
fm3 Jul 9, 2024
64e4e94
WIP: AnnotationTransactionService
fm3 Jul 15, 2024
3b8e39a
finalize transaction service
fm3 Jul 15, 2024
f0dd992
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 15, 2024
615d78c
move skeleton updates to GenericUpdateAction trait
fm3 Jul 15, 2024
9b9f0bc
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 16, 2024
313d791
further move skeleton update actions
fm3 Jul 16, 2024
b6a066d
json for volume update actions
fm3 Jul 16, 2024
398bc63
cleanup
fm3 Jul 16, 2024
08b8bcc
volume action serialization, wip annotation actions
fm3 Jul 17, 2024
c74110c
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 17, 2024
21d1078
further unify update actions, add tracingId to LayerUpdateActions
fm3 Jul 17, 2024
24e8fd2
remove unused stuff, log routes, access checks
fm3 Jul 17, 2024
f09a624
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 23, 2024
2cbadf2
fetch pending updates
fm3 Jul 23, 2024
7f6bd5d
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 29, 2024
e5f128e
TracingCollection for updating
fm3 Jul 29, 2024
201a1c9
AnnotationWithTracings
fm3 Jul 29, 2024
2dec0a2
Merge branch 'master' into unified-annotation-versioning
fm3 Jul 29, 2024
fd5ccd3
applyOn skeleton
fm3 Jul 29, 2024
d8a6ddb
ApplyableVolumeUpdateAction
fm3 Jul 29, 2024
54444aa
make more update actions applyable
fm3 Jul 30, 2024
a9cd852
apply bucketMutating actions
fm3 Jul 30, 2024
5ae6f46
fixes
fm3 Jul 30, 2024
7b3d7c2
small cleanup
fm3 Jul 30, 2024
01587b1
Merge branch 'master' into unified-annotation-versioning
fm3 Aug 21, 2024
62e671d
WIP: also maintain editable mappings when applying updates
fm3 Aug 21, 2024
c4dc957
WIP: createTracing
fm3 Aug 21, 2024
ff579f8
adapt tests, imports
fm3 Aug 22, 2024
43113e2
Merge branch 'master' into unified-annotation-versioning
fm3 Aug 27, 2024
3e9265e
initialize annotationProto. wip: call with annotationId
fm3 Aug 27, 2024
0105e0e
pass annotation id + token to more spots
fm3 Aug 27, 2024
e42c071
use annotationId also in zarr streaming
fm3 Aug 27, 2024
4862804
remove some unused stuff
fm3 Aug 27, 2024
1ce9e83
fix cyclic injection, fix loading skeleton + volume tracing proto
fm3 Aug 27, 2024
1124262
request tracing from new api
fm3 Aug 27, 2024
9975bf2
fix report tracing updates
fm3 Aug 28, 2024
29a2f47
fix ambiguous update action keys
fm3 Aug 28, 2024
b26e3b2
adapt frontend to changed save route (only for skeleton; still buggy)
philippotto Aug 29, 2024
0a6a507
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 12, 2024
44ca612
use annotationUpdates to get materializableVersion
fm3 Sep 12, 2024
821377a
update tracing proto version
fm3 Sep 12, 2024
bc783e3
Merge branch 'master' of github.com:scalableminds/webknossos into uni…
philippotto Sep 13, 2024
9eb077f
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 16, 2024
6ea6d65
fix volume saving (single, layer, brush+move only)
fm3 Sep 16, 2024
8d01f5b
Merge branch 'unified-annotation-versioning' of github.com:scalablemi…
philippotto Sep 16, 2024
fee3368
fix typing
philippotto Sep 16, 2024
4e54e85
only have one save queue for all tracings in an annotation
philippotto Sep 16, 2024
b15405e
also store only one lastSaved and isBusy info for whole annotation
philippotto Sep 16, 2024
3952474
make sendRequestToServer independent of tracing
philippotto Sep 16, 2024
48bf1b7
rename sendRequestToServer
philippotto Sep 16, 2024
cd101bd
fix save_reducer.spec
philippotto Sep 16, 2024
b3fb119
fix save saga spec
philippotto Sep 16, 2024
f5bda6d
fix volumetracing_saga.spec.ts
philippotto Sep 16, 2024
23f64d9
fix skeletontracing saga spec
philippotto Sep 16, 2024
93420bf
fix saga_integration spec
philippotto Sep 16, 2024
d96a001
improve typing in saga integration spec
philippotto Sep 16, 2024
9234507
update action log
fm3 Sep 17, 2024
a42fe47
wip editable mappings. tokenContext.
fm3 Sep 17, 2024
de717c7
use tracingId directly for editableMappings
fm3 Sep 17, 2024
ba68b72
address editable mappings by tracing id
fm3 Sep 18, 2024
0044b29
editableMappingController
fm3 Sep 18, 2024
d705aeb
editable mapping info does not need version, mappingName anymore. rem…
fm3 Sep 18, 2024
ef09528
renamings, construct EditableMappingUpdater in TSAnnotationService
fm3 Sep 18, 2024
8883306
remove token from parameter lists
fm3 Sep 18, 2024
2dc833e
apply editable mapping updates
fm3 Sep 18, 2024
5618b8d
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 18, 2024
87e743b
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 19, 2024
0dda12a
update editable mapping info in AnnotationWithTracings
fm3 Sep 19, 2024
520d54b
iterate on editable mappings
fm3 Sep 19, 2024
e6d6534
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 23, 2024
1020650
add editable mapping to AnnotationWithTracings when it’s created
fm3 Sep 23, 2024
e21bc6e
fix agglomeratesForSegments route
fm3 Sep 23, 2024
f92d933
fix mincut route
fm3 Sep 23, 2024
19f39e5
wip build correct remoteFallbackLayer for updater
fm3 Sep 23, 2024
831bf7a
pass volumetracing info to updater
fm3 Sep 23, 2024
e5cd917
flush editable mapping updater
fm3 Sep 23, 2024
e4825a6
imports
fm3 Sep 23, 2024
09c78c9
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 24, 2024
4cc4647
fix segment stats request uri
fm3 Sep 24, 2024
8c11da2
wip: fix some more functions
fm3 Sep 24, 2024
2a88899
construct editableMappingLayer already in annotationservice
fm3 Sep 25, 2024
55c6ac1
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 25, 2024
d57b419
fix baseMappingName
fm3 Sep 25, 2024
401c7f9
wip: fix segmentsForAgglomerate
fm3 Sep 25, 2024
5f71bfc
flush
fm3 Sep 25, 2024
573c71a
Merge branch 'master' into unified-annotation-versioning
fm3 Sep 26, 2024
7f4a575
look up annotation id by tracing id rather than expanding the routes
fm3 Sep 26, 2024
1ff9b91
resolve some todos, close annotationUpdates grpc handle, remove unuse…
fm3 Sep 26, 2024
a7ee147
batching for update action log
fm3 Sep 26, 2024
65056ec
remove unused stuff from TracingService
fm3 Sep 26, 2024
1d9bb89
handle dummy annotation id + dummy tracing id
fm3 Sep 26, 2024
d092ece
distinguish between updateUserBoundingBoxVisibility in skeleton + vol…
fm3 Sep 26, 2024
2bec9e3
wip: prepare revertToVersion workflow
fm3 Sep 26, 2024
538395f
wip revert to version
fm3 Oct 7, 2024
8b3b3a6
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 7, 2024
8b8b4c0
revert to version format
fm3 Oct 7, 2024
0abf078
fix update action order
fm3 Oct 7, 2024
b9d3eca
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 8, 2024
1127a92
resolve circular dependency between the services
fm3 Oct 8, 2024
163354c
revert volume buckets. TODO: lz4 exception?
fm3 Oct 8, 2024
fe6257c
cache materialized with version
fm3 Oct 9, 2024
2d37640
revert proofreading distributed elements
fm3 Oct 14, 2024
a824f70
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 14, 2024
269e350
fix volume revert logic
fm3 Oct 14, 2024
50d9dc4
wip fix parallel revert
fm3 Oct 14, 2024
e0e4dc4
in bucket data reversion, handle revertedValue
fm3 Oct 14, 2024
c78b037
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 15, 2024
8ea36da
split and isolate function
fm3 Oct 15, 2024
5bdce21
WIP: Unified Annotation Versioning: regroup update groups by revert a…
fm3 Oct 15, 2024
3f48fea
wip apply regrouped updates
fm3 Oct 15, 2024
e98510d
build new updater
fm3 Oct 15, 2024
e098113
fix regroup
fm3 Oct 15, 2024
d5f1a62
delete annotation layer via update action
fm3 Oct 16, 2024
101e561
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 16, 2024
6bd5cee
wip add layer
fm3 Oct 21, 2024
a419b42
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 21, 2024
40b7e43
create tracing proto on wk side
fm3 Oct 21, 2024
6b3dd0f
fix creating annotation layers for fresh annotation
fm3 Oct 21, 2024
344fd01
fetch precedence layer from previous version
fm3 Oct 21, 2024
5f13149
isolate add layer update actions
fm3 Oct 21, 2024
d5b5386
Unified annotation restore (#8136)
MichaelBuessemeyer Oct 21, 2024
8bde3dc
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 21, 2024
90d8e4d
AnnotationDefaults
fm3 Oct 21, 2024
32dbfc8
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 23, 2024
759c2f3
fix after merge. report name + description to wk
fm3 Oct 23, 2024
1b8a3d5
skip reporting changes to postgres when loading old versions
fm3 Oct 23, 2024
ee89ea7
fix param, remove addSegmentIndex route
fm3 Oct 23, 2024
a7794d2
WIP import volume data
fm3 Oct 23, 2024
f150bf2
sort out some update actions
fm3 Oct 23, 2024
5e660b9
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 24, 2024
ecb4b9d
prepare duplicate
fm3 Oct 24, 2024
a007194
iterate on duplicate
fm3 Oct 24, 2024
af0630a
iterate on duplicate
fm3 Oct 24, 2024
2fa24c6
do not add actionTracingId to updateTdCamera and revertToVersion upd…
Oct 25, 2024
2f80f0f
some frontend spec fixes (far more are still needed xD)
Oct 25, 2024
f1a0d01
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 28, 2024
52076c1
iterate on duplicate (volumes, updates, buckets)
fm3 Oct 28, 2024
ce14718
duplicate editable mapping
fm3 Oct 28, 2024
ba47647
do not report layer changes to postgres when loading an older version
fm3 Oct 28, 2024
b3f9a2c
some cleanup
fm3 Oct 28, 2024
8a0d637
use new annotation duplicate in task assignment
fm3 Oct 28, 2024
3d92ae3
move old routes to new update actions
Oct 28, 2024
6498050
Merge branch 'unified-annotation-versioning' of github.com:scalablemi…
Oct 28, 2024
41cada1
spelling of layer type proto
fm3 Oct 28, 2024
aef7d33
Merge branch 'unified-annotation-versioning' of github.com:scalablemi…
fm3 Oct 28, 2024
05b7e40
ensure loading newest annotation version from tracing store and patch…
Oct 28, 2024
8d5be05
send update annotation name and description as update actions
Oct 28, 2024
3155f6c
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 29, 2024
85dece0
WIP task creation
fm3 Oct 29, 2024
a543415
fix task creation without base
fm3 Oct 29, 2024
d8d7fd4
wip task creation from base id
fm3 Oct 29, 2024
41a326c
fix volume task creation with base id
fm3 Oct 29, 2024
9549ad1
Merge branch 'master' into unified-annotation-versioning
fm3 Oct 30, 2024
11cbcd2
rename annotation.proto layers to annotationLayers, simplify duplicat…
fm3 Oct 30, 2024
65f2ebd
wip merge
fm3 Oct 30, 2024
e55768d
merge skeletons
fm3 Oct 30, 2024
63c8e6f
merge volume data
fm3 Oct 30, 2024
74c679b
small renamings
fm3 Oct 30, 2024
1a59e62
always use only data layers sent by tracing store and disregard layer…
Oct 30, 2024
ec8cd74
Merge branch 'master' into unified-annotation-versioning
fm3 Nov 5, 2024
0e9fffc
earliestAccessibleVersion; always fetch all layers; wip resetToBase
fm3 Nov 5, 2024
02f78b7
call resetToBase
fm3 Nov 5, 2024
bc51deb
remove volume tracing downsampling feature (backend)
fm3 Nov 5, 2024
7150c25
also duplicate annotation v0
fm3 Nov 5, 2024
3ffdf3b
use SequenceUtils
fm3 Nov 5, 2024
fb1b595
cleanup todo comments
fm3 Nov 5, 2024
89c4ec2
remap action tracing ids in duplicate
fm3 Nov 5, 2024
b89a7f6
re-add accidentally deleted function
Nov 5, 2024
636a34d
Merge branch 'unified-annotation-versioning' of github.com:scalablemi…
Nov 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 28 additions & 207 deletions app/controllers/AnnotationController.scala

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions app/controllers/AnnotationIOController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class AnnotationIOController @Inject()(
extends Controller
with FoxImplicits
with ProtoGeometryImplicits
with AnnotationLayerPrecedence
with LazyLogging {
implicit val actorSystem: ActorSystem = ActorSystem()

Expand Down Expand Up @@ -145,7 +146,8 @@ class AnnotationIOController @Inject()(
mergedSkeletonLayers ::: mergedVolumeLayers,
AnnotationType.Explorational,
name,
description)
description,
ObjectId.generate)
_ = analyticsService.track(UploadAnnotationEvent(request.identity, annotation))
} yield
JsonOk(
Expand Down Expand Up @@ -332,9 +334,8 @@ class AnnotationIOController @Inject()(
boundingBox = bbox,
elementClass = elementClass,
fallbackLayer = fallbackLayerOpt.map(_.name),
largestSegmentId =
annotationService.combineLargestSegmentIdsByPrecedence(volumeTracing.largestSegmentId,
fallbackLayerOpt.map(_.largestSegmentId)),
largestSegmentId = combineLargestSegmentIdsByPrecedence(volumeTracing.largestSegmentId,
fallbackLayerOpt.map(_.largestSegmentId)),
mags = VolumeTracingDownsampling.magsForVolumeTracing(dataSource, fallbackLayerOpt).map(vec3IntToProto),
hasSegmentIndex = Some(tracingCanHaveSegmentIndex)
)
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/LegacyApiController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import play.silhouette.api.actions.SecuredRequest
import com.scalableminds.util.tools.Fox
import com.scalableminds.webknossos.datastore.models.VoxelSize
import com.scalableminds.webknossos.datastore.models.annotation.{AnnotationLayer, AnnotationLayerType}
import com.scalableminds.webknossos.tracingstore.annotation.AnnotationLayerParameters
import com.scalableminds.webknossos.tracingstore.tracings.volume.MagRestrictions
import models.dataset.DatasetService
import models.organization.OrganizationDAO
Expand Down Expand Up @@ -128,7 +129,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController,
} yield adaptedResult
}

def annotationInfoV4(typ: String, id: String, timestamp: Long): Action[AnyContent] = sil.SecuredAction.async {
def annotationInfoV4(typ: String, id: String, timestamp: Option[Long]): Action[AnyContent] = sil.SecuredAction.async {
implicit request =>
for {
_ <- Fox.successful(logVersioned(request))
Expand Down
28 changes: 20 additions & 8 deletions app/controllers/UserTokenController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import com.scalableminds.webknossos.datastore.services.{
UserAccessAnswer,
UserAccessRequest
}
import com.scalableminds.webknossos.tracingstore.tracings.TracingIds
import com.scalableminds.webknossos.tracingstore.tracings.TracingId

import javax.inject.Inject
import models.annotation._
Expand Down Expand Up @@ -39,7 +39,6 @@ object RpcTokenHolder {

class UserTokenController @Inject()(datasetDAO: DatasetDAO,
datasetService: DatasetService,
annotationDAO: AnnotationDAO,
annotationPrivateLinkDAO: AnnotationPrivateLinkDAO,
userService: UserService,
organizationDAO: OrganizationDAO,
Expand Down Expand Up @@ -98,6 +97,8 @@ class UserTokenController @Inject()(datasetDAO: DatasetDAO,
handleDataSourceAccess(accessRequest.resourceId, accessRequest.mode, userBox)(sharingTokenAccessCtx)
case AccessResourceType.tracing =>
handleTracingAccess(accessRequest.resourceId.name, accessRequest.mode, userBox, token)
case AccessResourceType.annotation =>
handleAnnotationAccess(accessRequest.resourceId.name, accessRequest.mode, userBox, token)
case AccessResourceType.jobExport =>
handleJobExportAccess(accessRequest.resourceId.name, accessRequest.mode, userBox)
case _ =>
Expand Down Expand Up @@ -159,7 +160,19 @@ class UserTokenController @Inject()(datasetDAO: DatasetDAO,
private def handleTracingAccess(tracingId: String,
mode: AccessMode,
userBox: Box[User],
token: Option[String]): Fox[UserAccessAnswer] = {
token: Option[String]): Fox[UserAccessAnswer] =
if (tracingId == TracingId.dummy)
Fox.successful(UserAccessAnswer(granted = true))
else
for {
annotation <- annotationInformationProvider.annotationForTracing(tracingId)(GlobalAccessContext) ?~> "annotation.notFound"
result <- handleAnnotationAccess(annotation._id.toString, mode, userBox, token)
} yield result

private def handleAnnotationAccess(annotationId: String,
mode: AccessMode,
userBox: Box[User],
token: Option[String]): Fox[UserAccessAnswer] = {
// Access is explicitly checked by userBox, not by DBAccessContext, as there is no token sharing for annotations
// Optionally, an accessToken can be provided which explicitly looks up the read right the private link table

Expand All @@ -170,16 +183,15 @@ class UserTokenController @Inject()(datasetDAO: DatasetDAO,
case _ => Fox.successful(false)
}

if (tracingId == TracingIds.dummyTracingId)
if (annotationId == ObjectId.dummyId.toString) {
Fox.successful(UserAccessAnswer(granted = true))
else {
} else {
for {
annotation <- annotationInformationProvider.annotationForTracing(tracingId)(GlobalAccessContext) ?~> "annotation.notFound"
annotation <- annotationInformationProvider.provideAnnotation(annotationId, userBox)(GlobalAccessContext) ?~> "annotation.notFound"
annotationAccessByToken <- token
.map(annotationPrivateLinkDAO.findOneByAccessToken)
.getOrElse(Fox.empty)
.futureBox

allowedByToken = annotationAccessByToken.exists(annotation._id == _._annotation)
restrictions <- annotationInformationProvider.restrictionsFor(
AnnotationIdentifier(annotation.typ, annotation._id))(GlobalAccessContext) ?~> "restrictions.notFound"
Expand All @@ -201,7 +213,7 @@ class UserTokenController @Inject()(datasetDAO: DatasetDAO,
jobBox <- jobDAO.findOne(jobIdValidated)(DBAccessContext(userBox)).futureBox
answer = jobBox match {
case Full(_) => UserAccessAnswer(granted = true)
case _ => UserAccessAnswer(granted = false, Some(s"No ${mode} access to job export"))
case _ => UserAccessAnswer(granted = false, Some(s"No $mode access to job export"))
}
} yield answer
}
Expand Down
87 changes: 82 additions & 5 deletions app/controllers/WKRemoteTracingStoreController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@ package controllers
import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
import com.scalableminds.util.time.Instant
import com.scalableminds.util.tools.{Fox, FoxImplicits}
import com.scalableminds.webknossos.datastore.Annotation.AnnotationProto
import com.scalableminds.webknossos.datastore.SkeletonTracing.SkeletonTracing
import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing
import com.scalableminds.webknossos.datastore.models.annotation.AnnotationLayer
import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId
import com.scalableminds.webknossos.tracingstore.TracingUpdatesReport
import com.scalableminds.webknossos.tracingstore.annotation.AnnotationLayerParameters
import com.scalableminds.webknossos.tracingstore.tracings.TracingId

import javax.inject.Inject
import models.analytics.{AnalyticsService, UpdateAnnotationEvent, UpdateAnnotationViewOnlyEvent}
import models.annotation.AnnotationState._
import models.annotation.{
Annotation,
AnnotationDAO,
AnnotationDefaults,
AnnotationInformationProvider,
AnnotationLayerDAO,
AnnotationService,
TracingDataSourceTemporaryStore,
TracingStoreService
}
Expand All @@ -24,8 +32,9 @@ import models.user.time.TimeSpanService
import play.api.i18n.Messages
import play.api.libs.json.Json
import play.api.mvc.{Action, AnyContent, PlayBodyParsers}
import scalapb.GeneratedMessage
import security.{WebknossosBearerTokenAuthenticatorService, WkSilhouetteEnvironment}
import utils.WkConf
import utils.{ObjectId, WkConf}

import scala.concurrent.ExecutionContext

Expand All @@ -37,6 +46,7 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
userDAO: UserDAO,
annotationInformationProvider: AnnotationInformationProvider,
analyticsService: AnalyticsService,
annotationService: AnnotationService,
datasetDAO: DatasetDAO,
annotationDAO: AnnotationDAO,
annotationLayerDAO: AnnotationLayerDAO,
Expand All @@ -50,18 +60,47 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
val bearerTokenService: WebknossosBearerTokenAuthenticatorService =
wkSilhouetteEnvironment.combinedAuthenticatorService.tokenAuthenticatorService

def updateAnnotation(name: String, key: String, annotationId: String): Action[AnnotationProto] =
Action.async(validateProto[AnnotationProto]) { implicit request =>
// tracingstore only sends this request after ensuring write access
implicit val ctx: DBAccessContext = GlobalAccessContext
for {
annotationIdValidated <- ObjectId.fromString(annotationId)
existingLayers <- annotationLayerDAO.findAnnotationLayersFor(annotationIdValidated)
newLayersProto = request.body.layers
existingLayerIds = existingLayers.map(_.tracingId).toSet
newLayerIds = newLayersProto.map(_.tracingId).toSet
layerIdsToDelete = existingLayerIds.diff(newLayerIds)
layerIdsToUpdate = existingLayerIds.intersect(newLayerIds)
layerIdsToInsert = newLayerIds.diff(existingLayerIds)
_ <- Fox.serialCombined(layerIdsToDelete.toList)(
annotationLayerDAO.deleteOneByTracingId(annotationIdValidated, _))
_ <- Fox.serialCombined(newLayersProto.filter(l => layerIdsToInsert.contains(l.tracingId))) { layerProto =>
annotationLayerDAO.insertOne(annotationIdValidated, AnnotationLayer.fromProto(layerProto))
}
_ <- Fox.serialCombined(newLayersProto.filter(l => layerIdsToUpdate.contains(l.tracingId)))(l =>
annotationLayerDAO.updateName(annotationIdValidated, l.tracingId, l.name))
// Layer stats are ignored here, they are sent eagerly when saving updates
_ <- annotationDAO.updateName(annotationIdValidated,
request.body.name.getOrElse(AnnotationDefaults.defaultName))
_ <- annotationDAO.updateDescription(annotationIdValidated,
request.body.description.getOrElse(AnnotationDefaults.defaultDescription))
} yield Ok
}

def handleTracingUpdateReport(name: String, key: String): Action[TracingUpdatesReport] =
Action.async(validateJson[TracingUpdatesReport]) { implicit request =>
implicit val ctx: DBAccessContext = GlobalAccessContext
tracingStoreService.validateAccess(name, key) { _ =>
val report = request.body
for {
annotation <- annotationDAO.findOneByTracingId(report.tracingId)
annotationId <- ObjectId.fromString(report.annotationId)
annotation <- annotationDAO.findOne(annotationId)
_ <- ensureAnnotationNotFinished(annotation)
_ <- annotationDAO.updateModified(annotation._id, Instant.now)
_ <- Fox.runOptional(report.statistics) { statistics =>
annotationLayerDAO.updateStatistics(annotation._id, report.tracingId, statistics)
}
/*_ <- Fox.runOptional(report.statistics) { statistics =>
annotationLayerDAO.updateStatistics(annotation._id, annotationId, statistics)
}*/ // TODO stats per tracing id. note: they might arrive before the layer is created. skip them then.
userBox <- bearerTokenService.userForTokenOpt(report.userToken).futureBox
trackTime = report.significantChangesCount > 0 || !wkConf.WebKnossos.User.timeTrackingOnlyWithSignificantChanges
_ <- Fox.runOptional(userBox)(user =>
Expand Down Expand Up @@ -113,6 +152,20 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
}
}

def annotationIdForTracing(name: String, key: String, tracingId: String): Action[AnyContent] =
Action.async { implicit request =>
tracingStoreService.validateAccess(name, key) { _ =>
implicit val ctx: DBAccessContext = GlobalAccessContext
if (tracingId == TracingId.dummy) {
Fox.successful(Ok(Json.toJson(ObjectId.dummyId)))
} else {
for {
annotation <- annotationInformationProvider.annotationForTracing(tracingId) ?~> s"No annotation for tracing $tracingId"
} yield Ok(Json.toJson(annotation._id))
}
}
}

def dataStoreUriForDataset(name: String,
key: String,
organizationId: Option[String],
Expand All @@ -131,4 +184,28 @@ class WKRemoteTracingStoreController @Inject()(tracingStoreService: TracingStore
} yield Ok(Json.toJson(dataStore.url))
}
}

def createTracing(name: String,
key: String,
annotationId: String,
previousVersion: Long): Action[AnnotationLayerParameters] =
Action.async(validateJson[AnnotationLayerParameters]) { implicit request =>
tracingStoreService.validateAccess(name, key) { _ =>
implicit val ctx: DBAccessContext = GlobalAccessContext
for {
annotationIdValidated <- ObjectId.fromString(annotationId)
annotation <- annotationDAO.findOne(annotationIdValidated) ?~> "annotation.notFound"
dataset <- datasetDAO.findOne(annotation._dataset)
tracingEither <- annotationService.createTracingForExplorational(dataset,
request.body,
annotation.annotationLayers,
Some(previousVersion))
tracing: GeneratedMessage = tracingEither match {
case Left(s: SkeletonTracing) => s
case Right(v: VolumeTracing) => v
}
} yield Ok(tracing.toByteArray).as(protobufMimeType)
}
}

}
1 change: 1 addition & 0 deletions app/models/analytics/AnalyticsService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class AnalyticsService @Inject()(rpc: RPC,
}
val wrappedJson = Json.obj("api_key" -> conf.key, "events" -> List(analyticsEventJson))
rpc(conf.uri).silent.postJson(wrappedJson)
()
}
Fox.successful(())
}
Expand Down
30 changes: 27 additions & 3 deletions app/models/annotation/Annotation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,21 @@ import javax.inject.Inject
import scala.concurrent.ExecutionContext
import scala.concurrent.duration.FiniteDuration

object AnnotationDefaults {
val defaultName: String = ""
val defaultDescription: String = ""
}

case class Annotation(
_id: ObjectId,
_dataset: ObjectId,
_task: Option[ObjectId] = None,
_team: ObjectId,
_user: ObjectId,
annotationLayers: List[AnnotationLayer],
description: String = "",
description: String = AnnotationDefaults.defaultDescription,
visibility: AnnotationVisibility.Value = AnnotationVisibility.Internal,
name: String = "",
name: String = AnnotationDefaults.defaultName,
viewConfiguration: Option[JsObject] = None,
state: AnnotationState.Value = Active,
isLockedByOwner: Boolean = false,
Expand Down Expand Up @@ -140,13 +145,20 @@ class AnnotationLayerDAO @Inject()(SQLClient: SqlClient)(implicit ec: ExecutionC
q"""INSERT INTO webknossos.annotation_layers(_annotation, tracingId, typ, name, statistics)
VALUES($annotationId, ${a.tracingId}, ${a.typ}, ${a.name}, ${a.stats})""".asUpdate

def deleteOne(annotationId: ObjectId, layerName: String): Fox[Unit] =
def deleteOneByName(annotationId: ObjectId, layerName: String): Fox[Unit] =
for {
_ <- run(q"""DELETE FROM webknossos.annotation_layers
WHERE _annotation = $annotationId
AND name = $layerName""".asUpdate)
} yield ()

def deleteOneByTracingId(annotationId: ObjectId, tracingId: String): Fox[Unit] =
for {
_ <- run(q"""DELETE FROM webknossos.annotation_layers
WHERE _annotation = $annotationId
AND tracingId = $tracingId""".asUpdate)
} yield ()

def findAnnotationIdByTracingId(tracingId: String): Fox[ObjectId] =
for {
rList <- run(q"SELECT _annotation FROM webknossos.annotation_layers WHERE tracingId = $tracingId".as[ObjectId])
Expand Down Expand Up @@ -503,6 +515,18 @@ class AnnotationDAO @Inject()(sqlClient: SqlClient, annotationLayerDAO: Annotati
AND a.typ = ${AnnotationType.Task} """.as[ObjectId])
} yield r.toList

def findBaseIdForTask(taskId: ObjectId)(implicit ctx: DBAccessContext): Fox[ObjectId] =
for {
accessQuery <- readAccessQuery
r <- run(q"""SELECT _id
FROM $existingCollectionName
WHERE _task = $taskId
AND typ = ${AnnotationType.TracingBase}
AND state != ${AnnotationState.Cancelled}
AND $accessQuery""".as[ObjectId])
firstRow <- r.headOption
} yield firstRow

def findAllByTaskIdAndType(taskId: ObjectId, typ: AnnotationType)(
implicit ctx: DBAccessContext): Fox[List[Annotation]] =
for {
Expand Down
Loading