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

Unified annotation versioning #7917

Open
wants to merge 483 commits into
base: master
Choose a base branch
from
Open

Unified annotation versioning #7917

wants to merge 483 commits into from

Conversation

fm3
Copy link
Member

@fm3 fm3 commented Jul 8, 2024

Summary

  • Annotation Versioning no longer works per layer, now a single version number is used for the whole annotation
  • This makes EditableMappings/Proofreading annotations revertable
  • EditableMappings are now stored by the tracingId (same as their volume layer)
  • TokenContext was introduced, an implicit context variable to pass around user tokens in tracingstore + datastore more prettily
  • When restoring older versions of annotations, the layer set may change now (if the layer had been added since then)

Steps to test:

WK Testing

Note: Since the FossilDB is opened with a new set of column families, switching to and from this branch requires a fresh FossilDB data dir. I use rm -rf fossildb/data; git co fossildb/data/.gitignore

  • basic annotating, page refresh
  • download
  • reupload
  • proofreading
  • version restore view
    • restore/preview across
      • skeleton changes
      • volume changes
      • proofreading changes
      • layer deletions/additions
    • open the restore view without permissions and download an older version
  • duplicate
  • merge
  • download
    • test download with older version (not sure if the frontend specifies it, but can be tested with curl)
  • task system
    • viewing compound + downloading results
  • test with large annotations, perf should be not much worse than before
  • General testing around
Migration Testing
  • create annotations on master, switch branch, run migration, load again, annotate again
  • should work for different layer combinations
  • should work with pending updates (no page refresh after skeleton changes on master)
  • should work with proofreading annotations

TODOs

Backend
  • Mechanism to Revert Editable Mappings
    • Iterators for SegmentToAgglomerate, AgglomerateToGraph
    • How to encode Reverted chunks, agglomerates? → Single Zero-Byte?
      • Check a single zero byte is not a valid proto message
    • Iterate over current version, fetch old version, rewrite
    • Integrate this in Updater or flush Updater before this happens
    • Test
  • Annotation proto object
  • Design Annotation-wide update actions
    • add layer
      • becomes update action rather than route
      • after applying updates, a summary is sent to wk if postgres-cached properties change
        • could this lead to annotationId lookups before postgres knows about the new layer?
      • assert no duplicate names?
      • assert no more than one skeleton?
      • test add layer as very first update action
    • delete layer
    • update layer metadata
    • update annotation metadata (name+description)
  • Test sandbox annotation
  • Adapt Task creation (save annotationProto object)
  • Duplicate
    • Use in duplicate route (“copy to my account”)
    • Use in task creation from base annotation
    • Use in task assignment
    • What to do with task resetToBase? implement as revert action?
    • actionTracingIds need to be remapped in duplicate (or use layer names after all?)
    • duplicate history?
      • duplicate update actions (needed for merging editable mappings)
      • duplicate v0 in addition to current version?
      • Also in fromTask case? How to mark earliest accessible version? We don’t want users to revert too far, right?
      • Should we also copy intermediate materialized versions? Or just 0 and current?
      • What about intermediate bucket versions? They are not in the updates
      • perf: duplicate api for fossilDB?
  • Unified versioning over layers
    • Route
    • Store Updates
    • Create Annotation
    • Updates need Layer Identifier
    • Apply Updates
      • updates that mutate annotation object
      • updates that mutate tracing objects
      • set individual targetVersions for updater/buffers? or is this already done?
      • updates that mutate other stuff
        • volume buckets
        • proofreading
      • Special updates
        • AddSegmentIndex → functionality removed. only sets bool now.
        • ImportVolumeData
        • makeMappingEditable
        • Merge into current? → does not exist, only ImportVolumeData
        • Downsample? (Maybe remove feature)
        • RevertToVersion
          • Revert volume buckets
          • Revert skeletons
          • Revert annotation-level properties
          • Revert proofreading fields
          • Handle gone layers (either in previous or target version)
          • What if two of those come in the same update batch?
          • split updates batches by RevertToVersion? What are the intermediate versions?
    • Replace or fix MergedFromIds (used for compound, mergeTwo, always creates new annotation)
      • merge skeletons
      • merge volume data
      • merge history?
        • only proofreading layers, new version numbers!
        • iron out reversion folds?
      • merge editable mappings
      • setting version doesn’t seem to happen correctly yet, getting v0 after merge
      • use persist bool in all cases or assert non-supported don’t happen
      • test with skeleton-only, volume-only, proofreading-only
      • test with compound
      • restore segment index
    • Replace or fix MergedFromContents (only used during upload, so everything has v0, no editable mappings)
    • report applied updates to postgres only when requesting newest
    • Version assertions
    • re-connect tracingMigrationService (concerns user bounding boxes)
    • lazy apply for volumes and editable mappings?
      • can volume data still be written directly? can there be conflicts?
      • or ditch lazy apply completely? (might be ok with distributed skeletons etc)
      • When to materialize which layer?
        • store materialized layer only sometimes? count its update actions?
    • Ensure no parallel update applying on the same object (async cache?)
      • But parallel update applying should also not be a problem, except for perf (was it only a problem because version wasn’t specified when loading volume buckets during revert?)
    • Perf: Reduce cache mem overhead by removing older versions when newer are requested (could be done by nested LRU cache, one with small capacity per annotationId)
  • Fix e2e tests
  • AddLayer/Proofreading seems broken, I can’t add a skeleton layer + then proofread (findSkeletonRaw.failed)
  • Proofreading: when can we set relyOnAgglomerateIds=true?
  • Download Annotation route should take single version parameter
  • Annotation Stats
    • on wk side, distribute to layers for postgres
    • In timetracking api, serve stats per layer instead of aggregated
  • Resolve remaining // TODO comments
  • Remove some logging (both RPC requests and logger.info)
  • CI
  • Double-check: does the regroup interfere with the targetVersions?
  • How to deal with old CompactUpdateActions? Don’t have enough info to migrate those. → add isCompacted bool in json, just display those in version log, no apply
  • Follow-Ups (write issues)
  • agglomerateIdsForSegments needs to support a version parameter so that previewing an older version works
  • annotationId for tracingId lookups fail when loading old versions. We probably need to take the annotationId from the frontend in these cases.
  • Fetch extra editable mapping updates in case of annotationProto.editableMappingMayHavePendingUpdates
  • Double-check: do we flush the updated proto only when it changed? If so, doesnt the materialized versions het out of sync? Wont we apply updates twice?
  • Double-check does withVersion set editableMappingMayHavePendingUpdates to None, as well as skeleton?
Frontend
  • Linearized update actions
  • Use new update action route
  • update actions now need actionTracingId
  • importVolumeData seems to send outdated version, does not reload on success?
  • Some update actions now need to be distinguished between skeleton & volume, thus they are now separate and need their own updateName. This goes for:
    • updateUserBoundingBoxes → updateUserBoundingBoxesInSkeletonTracing, updateUserBoundingBoxesInVolumeTracing
    • ~~updateUserBoundingBoxVisibility → updateUserBoundingBoxVisibilityInSkeletonTracing,
    • updateUserBoundingBoxVisibilityInVolumeTracing~~ is used nowhere in the frontend -> remove in backend?
    • updateTracing → updateVolumeTracing, updateSkeletonTracing
  • Some routes are now update actions (add layer, delete layer). makeHybrid was removed.
    • They need to first send an update action to the store and then e.g. reload
  • Version Restore View; See: Unified annotation versioning #7917 (comment)
    • For volume actions show which volume layer was edited (if the layer was deleted, show "unknown layer" / layer id)
    • Revert proofreading preview does not work. version needs to be used when mapping segments to agglomerates in agglomerateIdsForSegments
    • Respect earliestAccessibleVersion (show version history only down to that one)
    • Seems to break if layer set has changed (e.g. if a layer was added since the old version)
  • enforce revert actions to come in separate update group (has its own version number)
  • same for add layer mapping
  • same for initialize editable mapping -> why? code reads like this is not done and it works. A saved state is simply required for proofreading actions
  • Sync layerIndependentActions with backend -> should include only actions which the backend has set to be in a single transaction group these are two different concepts.
  • get rid of "unused-tracing-id"
  • makeMappingEditable route was removed. Sending the updateMappingName action is enough now (but must be sent before proofreading interactions)
  • Action delete layer is used nowhere -> The migration is in process -> An action was created but the code still uses the old backend route deleteAnnotationLayer, which should be removed and replaced with the update action
  • why is newestVersion sometimes called with emptystring annotationId? looks like this happens in dashboard/during navigation?
  • Load the annotation proto object to get correct layer set, name, description for requested version (postgres only knows latest for the dashboard)
    • should be done in model init (both sources should be correctly merged -> tracingstore data completely overwrites postgres data)
  • remove downsample feature for volume annotation layers
  • Now that the editableMappingId is always equal to its volume layer’s tracingId, some code may be simplified
  • What happens to users who keep their annotation tab open during the downtime? → they can’t save their pending updates since the update routes have changed. So they can’t create inconsistent states. Page refresh should work then.
  • Current version to base next save requests on should be maximum of that of all layers and annotationProto
  • Send adapted annotationStats, grouped by tracingId:
{
    "myTracingId": {
        "branchpoints:" 3
        "nodeCount": 5
    },
    "myVolumeTracingId1": {
        "segmentCount": 5
    },
    "myVolumeTracingId2": {
        "segmentCount": 9
    }
}
Migration
  • Double-check that no tracingId is referenced by multiple annotations. → tracingId even has UNIQUE constraint in postgres
  • Linearized Updates
    • How to interleave updates? is by timestamp alone fine?
    • How to deal with reverts? In the new code, a revert can’t be for just one layer
    • How to deal with makeMappingEditable? → can be ignored, we set earliest_accessible_version for all editable mapping annotations anyway
    • How to deal with addSegmentIndex? keep, the backend code no longer creates it, but can still handle it.
    • How to detect when to add which layer? Add all layers already to v0?
    • careful: merged editable mappings have update actions in non-chronological order
      • Maybe just set earliestAccessibleVersion for those to after that?
    • Fix bugs (activeNode wrong? can’t request updateActionLog?)
  • changed update actions
    • several actions were renamed, e.g. updateTracing was renamed → updateVolumeTracing/updateSkeletonTracing
    • update actions now need actionTracingId
  • migrate materialized protos to new version nums (note that their version fields need to change too)
  • Find annotation ids
  • Why is len(annotations) smaller than SELECT COUNT(*)? is the pagination buggy? Nope, some annotations have zero layers, they get thrown out by the JOIN. That’s ok.
  • Shortcut for single-layer annotations (parse proto only if changes needed)
  • Checkpoints/Resumability
  • Can we run a first part in the background?
    • on a second run, (re)do only annotations that in postgres have a modified date newer than when first run started
  • editable mapping update actions and arrays used to be stored by mappingName, now annotationId
  • while we’re migrating everything, could we also do Fix or Remove Morton Order in Volume Data Fossil Keys #3546 ?
    • how does it interact with ND data?
  • take the time to profile
  • parallelize (distribute using thread pool / semaphore)
  • materialize additional versions? or change logic to select pending updates per layer in wk?
  • also mark potentially pending proofreading updates
  • add custom “start time” to handle rsynced fossilDB snapshot
  • On incremental run, do existing ones need to be cleaned up? (In case of reversions since then?)
  • double check that this revert cleanup works and that revert skips are handled correctly. Order seems backwards? Even opening v0 shows nodes.

Issues:


@fm3 fm3 self-assigned this Jul 8, 2024
@fm3 fm3 changed the title Unified annotation versioning WIP: Unified annotation versioning Jul 8, 2024
@fm3
Copy link
Member Author

fm3 commented Aug 28, 2024

@philippotto A first rudimentary version seems to work: I can create an annotation on l4_sample, (note: volume buckets won’t load), take the annotation id and skeleton tracing id, and insert them in here (both in uri and in request body):

await fetch("http://localhost:9000/tracings/annotation/66cf0fc1d10000f6ae2f30bd/update?token=secretSampleUserToken", {
    "credentials": "include",
    "headers": {
        "Accept": "application/json",
        "Accept-Language": "en-US,en;q=0.5",
        "content-type": "application/json",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "Priority": "u=0",
        "Pragma": "no-cache",
        "Cache-Control": "no-cache"
    },
    "referrer": "http://localhost:9000/annotations/66cedcb2e000008402396d82",
    "body": "[{\"version\":1,\"transactionId\":\"lfh7mxycvn\",\"transactionGroupCount\":1,\"transactionGroupIndex\":0,\"timestamp\":1724832956301,\"authorId\":\"66c46740d10000d1005405b6\",\"actions\":[{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"createTree\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"id\":1,\"color\":[0.6784313725490196,0.1411764705882353,0.050980392156862744],\"name\":\"explorative_2024-08-28_Sample_User_001\",\"timestamp\":1724832956293,\"comments\":[],\"branchPoints\":[],\"groupId\":null,\"isVisible\":true,\"type\":\"DEFAULT\",\"edgesAreVisible\":true}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"createNode\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"additionalCoordinates\":[],\"radius\":1,\"rotation\":[0,0,0],\"viewport\":0,\"resolution\":0,\"id\":1,\"timestamp\":1724832956293,\"bitDepth\":8,\"interpolation\":false,\"position\":[3530,3603,1024],\"treeId\":1}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateSkeletonTracing\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"activeNode\":1,\"editPosition\":[3584,3584,1024],\"editPositionAdditionalCoordinates\":[],\"editRotation\":[0,0,0],\"zoomLevel\":1}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateTdCameraSkeleton\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\"}}],\"stats\":{\"treeCount\":1,\"nodeCount\":1,\"edgeCount\":0,\"branchPointCount\":0},\"info\":\"[\\\"UPDATE_LAYER_SETTING\\\",\\\"SET_HISTOGRAM_DATA_FOR_LAYER\\\",\\\"UPDATE_MESH_FILE_LIST\\\",\\\"UPDATE_CURRENT_MESH_FILE\\\",\\\"SET_INPUT_CATCHER_RECTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"SET_STORED_LAYOUTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING * 2\\\",\\\"SET_TOOL\\\",\\\"CREATE_NODE\\\"]\"},{\"version\":2,\"transactionId\":\"bmwafojnac\",\"transactionGroupCount\":1,\"transactionGroupIndex\":0,\"timestamp\":1724832956421,\"authorId\":\"66c46740d10000d1005405b6\",\"actions\":[{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateSkeletonTracing\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"activeNode\":1,\"editPosition\":[3552,3595,1024],\"editPositionAdditionalCoordinates\":[],\"editRotation\":[0,0,0],\"zoomLevel\":1}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateTdCameraSkeleton\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\"}}],\"stats\":{\"treeCount\":1,\"nodeCount\":1,\"edgeCount\":0,\"branchPointCount\":0},\"info\":\"[\\\"UPDATE_MESH_FILE_LIST\\\",\\\"UPDATE_CURRENT_MESH_FILE\\\",\\\"SET_INPUT_CATCHER_RECTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"SET_STORED_LAYOUTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING * 2\\\",\\\"SET_TOOL\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\"]\"},{\"version\":3,\"transactionId\":\"anjed8c2bb\",\"transactionGroupCount\":1,\"transactionGroupIndex\":0,\"timestamp\":1724832956939,\"authorId\":\"66c46740d10000d1005405b6\",\"actions\":[{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"createNode\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"additionalCoordinates\":[],\"radius\":1,\"rotation\":[0,0,0],\"viewport\":0,\"resolution\":0,\"id\":2,\"timestamp\":1724832956939,\"bitDepth\":8,\"interpolation\":false,\"position\":[3514,3571,1024],\"treeId\":1}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"createEdge\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"treeId\":1,\"source\":1,\"target\":2}},{\"name\":\"updateSkeletonTracing\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"activeNode\":2,\"editPosition\":[3530,3603,1024],\"editPositionAdditionalCoordinates\":[],\"editRotation\":[0,0,0],\"zoomLevel\":1}}],\"stats\":{\"treeCount\":1,\"nodeCount\":2,\"edgeCount\":1,\"branchPointCount\":0},\"info\":\"[\\\"UPDATE_CURRENT_MESH_FILE\\\",\\\"SET_INPUT_CATCHER_RECTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"SET_STORED_LAYOUTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING * 2\\\",\\\"SET_TOOL\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\",\\\"CREATE_NODE\\\"]\"},{\"version\":4,\"transactionId\":\"mgb0fqyu0c\",\"transactionGroupCount\":1,\"transactionGroupIndex\":0,\"timestamp\":1724832957039,\"authorId\":\"66c46740d10000d1005405b6\",\"actions\":[{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateSkeletonTracing\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"activeNode\":2,\"editPosition\":[3522,3587,1024],\"editPositionAdditionalCoordinates\":[],\"editRotation\":[0,0,0],\"zoomLevel\":1}},{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateTdCameraSkeleton\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\"}}],\"stats\":{\"treeCount\":1,\"nodeCount\":2,\"edgeCount\":1,\"branchPointCount\":0},\"info\":\"[\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"SET_STORED_LAYOUTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING * 2\\\",\\\"SET_TOOL\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\"]\"},{\"version\":5,\"transactionId\":\"9uu9b6j1qf\",\"transactionGroupCount\":1,\"transactionGroupIndex\":0,\"timestamp\":1724832957149,\"authorId\":\"66c46740d10000d1005405b6\",\"actions\":[{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"name\":\"updateSkeletonTracing\",\"value\":{\"actionTracingId\":\"3c2710c5-3b68-4bfa-ba7c-86553df9d8bf\",\"activeNode\":2,\"editPosition\":[3514,3571,1024],\"editPositionAdditionalCoordinates\":[],\"editRotation\":[0,0,0],\"zoomLevel\":1}}],\"stats\":null,\"info\":\"[\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"SET_STORED_LAYOUTS\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING * 2\\\",\\\"SET_TOOL\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\",\\\"CREATE_NODE\\\",\\\"SET_TD_CAMERA_WITHOUT_TIME_TRACKING\\\",\\\"MOVE_TD_VIEW_BY_VECTOR_WITHOUT_TIME_TRACKING\\\"]\"}]",
    "method": "POST",
    "mode": "cors"
});

Then reload and see two skeleton nodes. Adding/Removing layer, volume annotation, proofreading is all still broken. So are the version assertions. But this can be used as a starting point to adapt the frontend:

  • include actionTracingIds in update actions
  • send updates to new endpoint
  • some update action names have changed (need to distinguish btw skeleton + volume). This may change again, though…
  • adapt tracingstore js client to include annotation id in everything (note: some backend-internal routes still have to be adapted too) I ended up looking up the annotation id by the tracingid instead.

Note: when switching to and from this branch, I clear the fossildb content with rm -rf fossildb/data; git checkout fossildb/data/.gitignore, otherwise the column family change is rejected

@fm3
Copy link
Member Author

fm3 commented Oct 15, 2024

The next part of the frontend could be added now: The Restore Older Version flow. The version list view should no longer feature tabs per layer, but instead only one global GET tracings/annotation/:annotationId/updateActionLog route exists with a single list. (I currently adapted the frotnend so that each tab uses this new route)

Also, the revertToVersion update action is no longer layer specific (I guess this may already be covered by philipps changes).

It is required that the revertToVersion update action is the only action in its update group (so that it gets its own version number). Not sure if the frontend already does that.

Since Philipp is out of office, maybe @MichaelBuessemeyer could have a look at this after #6613 is done?

Note that this branch opens fossildb with different column families. I run rm -rf fossildb/data; git co fossildb/data/.gitignore when switching to and from this branch to avoid errors. If you need your fossildb data, you can of course create a backup first.

@MichaelBuessemeyer
Copy link
Contributor

Since Philipp is out of office, maybe @MichaelBuessemeyer could have a look at this after #6613 is done?

Sure :)

@fm3
Copy link
Member Author

fm3 commented Oct 23, 2024

@MichaelBuessemeyer the next batch of frontend todos has arrived 😇 I have not turned all of them into checkboxes in the description, feel free to do that if you prefer.

  • Some update actions are now duplicated, because I have to distinguish between SkeletonUpdateActions and VolumeUpdateActions, even if they are basically identical:

    • updateUserBoundingBoxes → updateUserBoundingBoxesInSkeletonTracing, updateUserBoundingBoxesInVolumeTracing
    • updateUserBoundingBoxVisibility → updateUserBoundingBoxVisibilityInSkeletonTracing, updateUserBoundingBoxVisibilityInVolumeTracing
    • updateTracing → updateVolumeTracing, updateSkeletonTracing
  • no renaming, but note: these is an AnnotationUpdateAction (no actionTracingId)

    • updateTdCamera
    • revertToVersion
  • New update actions that were previously separate routes, either to wk or to tracingstore:

    • addLayerToAnnotation
      • layerParameters: AnnotationLayerParameters (same as before: typ, name, magRestrictions, etc)
    • deleteLayerFromAnnotation
      • tracingId: String
      • layerName: String
      • type: AnnotationLayerType (enum, "Skeleton", "Volume")
    • updateLayerMetadata
      • tracingId: String
      • layerName: String
    • updateMetadataOfAnnotation
      • name: Option[String]
      • description: Option[String]
  • The frontend should fetch the new annotation proto object from GET {tracingstore_uri}/tracings/annotation/:annotationId to get correct layer set, name, description for requested version (postgres only knows latest for the dashboard). This is especially relevant during version restore view (or if we eventually add permalinks to open previous versions).

@MichaelBuessemeyer
Copy link
Contributor

Regarding the first point:

Some update actions are now duplicated, because I have to distinguish between SkeletonUpdateActions and VolumeUpdateActions, even if they are basically identical:

It seems like @philippotto already implemented this 🥳

But regarding:

updateUserBoundingBoxVisibility → updateUserBoundingBoxVisibilityInSkeletonTracing, updateUserBoundingBoxVisibilityInVolumeTracing

The frontend does not know this update action. Maybe it was deprecated and is no longer sent by the frontend? At least cannot find it in the frontend code of this branch and the master as well 🥴. Maybe this is an old update action that is still supported by the backend for compatibility reasons? But on the other side, the version restore view does not have such an entry for legacy purposes.
A quick check of the backend master code shows, that updateUserBoundingBoxVisibility indeed exists but I don't see any place in the frontend that would send such an update action.
Moreover, I just checked what happens if the visibility of one of the user bboxes is changed. The frontend sends an updateAction in form of a "updateUserBoundingBoxes".

=> Maybe the updateUserBoundingBoxVisibility updateAction can be removed from the backend code 🤔?

Copy link
Contributor

coderabbitai bot commented Oct 25, 2024

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

116 files out of 230 files are above the max files limit of 75. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@MichaelBuessemeyer
Copy link
Contributor

MichaelBuessemeyer commented Oct 28, 2024

@fm3 While implementing the new update actions which replaced some routes I came across the following questions:

deleteLayerFromAnnotation
tracingId: String
layerName: String
type: AnnotationLayerType (enum, "Skeleton", "Volume")

Why is the layerName and type needed for this annotation action? The tracingId should be unique enough to identify the layer, isn't it?

Answer: This is only used for more detailed information in the version restore view.

For update action

updateMetadataOfAnnotation
name: Option[String]
description: Option[String]

the previous equivalent route was able to update the following annotation properties:

export type EditableAnnotation = {
  name: string;
  description: string;
  visibility: APIAnnotationVisibility;
  tags: Array<string>;
  viewConfiguration?: AnnotationViewConfiguration;
};

export function editAnnotation(
  annotationId: string,
  annotationType: APIAnnotationType,
  data: Partial<EditableAnnotation>,
): Promise<void> {
  return Request.sendJSONReceiveJSON(`/api/annotations/${annotationType}/${annotationId}/edit`, {
    data,
    method: "PATCH",
  });
}

Meaning the route was not only able to update the description and name but also the tags, visibility and viewConfig. Could this also be added to the updateMetadataOfAnnotation update action? If not, another update action is needed to update the missing three properties. At least that's how I see / understand it right now :)

Answer: The name and description will be versioned as update actions but the editAnnotation route will still exist and take updates regarding tags, visibility, and viewConfig.

@MichaelBuessemeyer
Copy link
Contributor

MichaelBuessemeyer commented Oct 28, 2024

Some more questions :D

The frontend should fetch the new annotation proto object from GET {tracingstore_uri}/tracings/annotation/:annotationId to get correct layer set, name, description for requested version (postgres only knows latest for the dashboard). This is especially relevant during version restore view (or if we eventually add permalinks to open previous versions).

What about sandbox annotations? The current route for this is: /api/datasets/${datasetId.owningOrganization}/${datasetId.name}/sandbox/${tracingType}

Answer: Keep it. & Sandbox is only supported for skeleton annotations.

Moreover, the old annotation info fetching route (/api/annotations/${annotationId}/info?timestamp=${Date.now()};`) is still required as the frontend needs to identify the tracing store URL for the annotation.

TODO [ ]: Always only use the info from the TS route. Maybe the core backend has layers that were deleted already and so on.

TODO: micha [ ] Test me with version restore

Moreover, is it possible to split up an annotation (its layers) across multiple tracing stores? If that's the case, the frontend needs to call all tracing stores available in the maybe outdated annotation info object from the backend and then merge this information?

There can be only one TS at a time for a single WK instance. => So this is not possible

@fm3
Copy link
Member Author

fm3 commented Oct 29, 2024

note to self:
note to self, open questions:

  • remove downsample feature? → yes
  • duplicate history?
    • duplicate update actions (needed for merging editable mappings)
    • duplicate v0 in addition to current version?
    • Also in task assignment case? How to mark earliest accessible version? We don’t want users to revert too far, right?
    • Should we also copy intermediate materialized versions? Or just 0 and current?
    • What about intermediate bucket versions? They are not in the updates
    • perf: duplicate api for fossilDB?
  • resetToBase as update action?
  • during apply, what to load in memory? is it all or nothing? → load all, as usually we request everything
  • during apply, what to flush to fossil? is it all or nothing? → flush all layers that were changed (updates for the layer exist)
  • avoid duplicate update applying (async cache?)

@MichaelBuessemeyer
Copy link
Contributor

Making an annotation's mapping editable (starting proofreading actions should work again.

Although, I'd like to ask why the updateMappingName actions now makes a mapping editable. To me this sounds kinda like magic because I would not have expected an action called updateMappingName to make a mapping editable. Can we maybe make this more obvious? E.g. an extra action or so? or at least a parameter / property of the updateMappingName like makeMappingEditable or isMappingEditable?

@fm3
Copy link
Member Author

fm3 commented Nov 11, 2024

or at least a parameter / property of the updateMappingName like makeMappingEditable or isMappingEditable?

Yes, such an extra property is required! It’s called isEditable. For this particular context, both isEditable and isLocked should be set to true. https://github.com/scalableminds/webknossos/blob/master/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala#L359

Thanks for having a look at this!

@MichaelBuessemeyer
Copy link
Contributor

Yes, such an extra property is required! It’s called isEditable. For this particular context, both isEditable and isLocked should be set to true. https://github.com/scalableminds/webknossos/blob/master/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala#L359

Thanks for having a look at this!

Oh right, thanks. I got confused between updateActions and dispatchActions here, because the dispatch action does not have fields like isLocked and isEditable. But it should work now ™️

@@ -91,7 +66,7 @@ class AnnotationController @Inject()(
// For Task and Explorational annotations, id is an annotation id. For CompoundTask, id is a task id. For CompoundProject, id is a project id. For CompoundTaskType, id is a task type id
id: String,
// Timestamp in milliseconds (time at which the request is sent)
timestamp: Long): Action[AnyContent] = sil.UserAwareAction.async { implicit request =>
timestamp: Option[Long]): Action[AnyContent] = sil.UserAwareAction.async { implicit request =>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated to unified annotation versioning, just wanted to do manual get requests. I think this is a fair change. This is used only for user time tracking.

@fm3
Copy link
Member Author

fm3 commented Nov 18, 2024

@MichaelBuessemeyer the backend should be ready for review. I’ll continue by building the FossilDB migration, this will occupy me for some time. I’d suggest to first finish the frontend here as far as possible (maybe @philippotto can help with that once he’s back) and then you could start the backend review here. I’ll happily give an introduction in person.

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fm3 I think the TimeController also needs to be updated a little to serve the new format of the AnnotationStats for each time tracking entry (instead of an array of stats).
I did not test this so it might be working already, but to me the code in the Controller reads like it needs to be updated. Moreover, the pr does not have any changes for TimeController.scala also indicating a potential open TODO

@fm3
Copy link
Member Author

fm3 commented Nov 19, 2024

You’re right, I’ll add that! I didn’t even remember that the time tracking api includes the stats 😅

@MichaelBuessemeyer
Copy link
Contributor

The newest changes look also good to me :) Thanks for incorporating them. I also did some slight unstructured testing (tried merging annotations, compound tasks & compound projects)

@MichaelBuessemeyer
Copy link
Contributor

MichaelBuessemeyer commented Jan 21, 2025

I did some more testing (went trough WK Testing steps and did want I did not yesterday):

While testing I noticed that a single proofreading action results in 3 version entries. Is it maybe possible to reduce this to one version entry?

Sorry but I found another little frontend bug:

@philippotto
Copy link
Member

While testing I noticed that a single proofreading action results in 3 version entries. Is it maybe possible to reduce this to one version entry?

we can probably slim this down, but let's do it in a follow up. I created #8345

https://github.com/user-attachments/assets/2e4a65fa-ce8b-4784-8720-90bc82a427e6 Maybe the reason for this bug is that I selected the newest version and then closed the view 🤔?

thanks for spotting this! I fixed this now. please have a look at my latest commit. I tried to clean up how the allowUpdate property is restored. the original problem was that useWillUnmount didn't have a dependency on the initialAllowUpdate property (and that could be set to false because of the dummy annotation in the default state).

…nds/webknossos into unified-annotation-versioning
@MichaelBuessemeyer
Copy link
Contributor

thanks for spotting this! I fixed this now. please have a look at my latest commit. I tried to clean up how the allowUpdate property is restored. the original problem was that useWillUnmount didn't have a dependency on the initialAllowUpdate property (and that could be set to false because of the dummy annotation in the default state).

Awesome. Thanks for fixing this. I can no longer trigger the buggy behaviour 🎉

@MichaelBuessemeyer
Copy link
Contributor

From my side this PR is ready to go ✔️

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for all the work you put into this @fm3 @philippotto 🙏

I could not find anything anymore and the code looks good to me.

Maybe a little final quick check from @normanrz regarding the migration guide changes would be useful before merging

@fm3
Copy link
Member Author

fm3 commented Jan 23, 2025

@philippotto could you have nother look at the new merge conflicts? For VolumeTracingLayer.scala it should be safe to just use “ours”.

Copy link
Member

@normanrz normanrz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration looks good 👍

tools/hosting/docker-compose.yml Show resolved Hide resolved
@philippotto
Copy link
Member

@philippotto could you have nother look at the new merge conflicts? For VolumeTracingLayer.scala it should be safe to just use “ours”.

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants