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 402 commits into
base: master
Choose a base branch
from
Open

Unified annotation versioning #7917

wants to merge 402 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.

103 files out of 212 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?)

@fm3 fm3 marked this pull request as ready for review December 12, 2024 14:48
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.

I reviewed the migration script. Looks good to me, but I left a few comments to understand some details better.

Comment on lines 12 to 13
- FossilDB must now be opened with new column family set `skeletons,volumes,volumeData,volumeSegmentIndex,editableMappingsInfo,editableMappingsAgglomerateToGraph,editableMappingsSegmentToAgglomerate,annotations,annotationUpdates`. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
- The FossilDB content needs to be migrated. For that, use the python program at `tools/migration-unified-annotation-versioning` (see python main.py --help for instructions). Note that it writes to a completely new FossilDB, that must first be opened with the new column families, see above. The migration code needs to connect to postgres, to the old FossilDB and to the new. After the migration, replace the old FossilDB by the new one. The migration can also be run in several steps so that the majority of the data can already be migrated while WEBKNOSSOS is still running. Then only annotations that have been edited again since the first run need to be migrated in the incremental second run during a WEBKNOSSOS downtime. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- FossilDB must now be opened with new column family set `skeletons,volumes,volumeData,volumeSegmentIndex,editableMappingsInfo,editableMappingsAgglomerateToGraph,editableMappingsSegmentToAgglomerate,annotations,annotationUpdates`. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
- The FossilDB content needs to be migrated. For that, use the python program at `tools/migration-unified-annotation-versioning` (see python main.py --help for instructions). Note that it writes to a completely new FossilDB, that must first be opened with the new column families, see above. The migration code needs to connect to postgres, to the old FossilDB and to the new. After the migration, replace the old FossilDB by the new one. The migration can also be run in several steps so that the majority of the data can already be migrated while WEBKNOSSOS is still running. Then only annotations that have been edited again since the first run need to be migrated in the incremental second run during a WEBKNOSSOS downtime. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
- The versioning scheme of annotations has been changed. That requires a larger migration including the FossilDB content. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
- FossilDB must now be opened with new column family set `skeletons,volumes,volumeData,volumeSegmentIndex,editableMappingsInfo,editableMappingsAgglomerateToGraph,editableMappingsSegmentToAgglomerate,annotations,annotationUpdates`. [#7917](https://github.com/scalableminds/webknossos/pull/7917)
- The FossilDB content needs to be migrated. For that, use the python program at `tools/migration-unified-annotation-versioning` (see python main.py --help for instructions). Note that it writes to a completely new FossilDB, that must first be opened with the new column families, see above. The migration code needs to connect to postgres, to the old FossilDB and to the new. After the migration, replace the old FossilDB by the new one. The migration can also be run in several steps so that the majority of the data can already be migrated while WEBKNOSSOS is still running. Then only annotations that have been edited again since the first run need to be migrated in the incremental second run during a WEBKNOSSOS downtime. [#7917](https://github.com/scalableminds/webknossos/pull/7917)

Copy link
Member

Choose a reason for hiding this comment

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

I guess it would also be helpful to explain how to start a second fossildb instance.

Copy link
Member

Choose a reason for hiding this comment

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

No version constraints?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure what the best variant for ths is. I now added the versions that pip show gave me with == but I guess that may be too restrictive? What do you think?

tools/migration-unified-annotation-versioning/migration.py Outdated Show resolved Hide resolved
tools/migration-unified-annotation-versioning/migration.py Outdated Show resolved Hide resolved
tools/migration-unified-annotation-versioning/migration.py Outdated Show resolved Hide resolved
@fm3 fm3 requested a review from normanrz December 16, 2024 13:56
@normanrz normanrz force-pushed the unified-annotation-versioning branch from 5cb76ad to 60139a0 Compare December 18, 2024 10:59
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.

Hej, I finished the first backend review round. 🎉
The code looks in a very good state to me. Well done @fm3 🥇

I mostly have question for understanding regarding functionality, design and only few high level remarks / questions. Moreover, I also answered the questions myself sometimes but left them for you to double check in case I understood something wrong. So please don't feel overwhelmed by all my comments 🙈

Open TODOs for me:

  • Check backend tests (I skipped those)
  • Do testing
  • Review frontend

If I may have one wish for such big future PRs & I am just guessing here whether that was actually the problem: I noticed that some files were moved and also changed. Somehow my IDE which I used to review the code did not realize that a file was moved, thus showing the changes not in an optimal way (github as well). This might be originating from the fact that git itself did not realize that a file was moved. In case you did not do that: Please make sure when moving a file to commit the deletion and adding the new file in the same commit. This hopefully makes git realize that the file was moved instead of deleted and a new one was added. Changes to the moved file can then be done in another commit. Hopefully, in this case the changes will be shown as a moved file with changes instead of deletion a file and addition of a new file.

@@ -160,7 +161,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] =
Copy link
Contributor

Choose a reason for hiding this comment

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

Cant this also use the new token context?

private def handleAnnotationAccess(annotationId: String,
mode: AccessMode,
userBox: Box[User],
token: Option[String]): Fox[UserAccessAnswer] = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Cant this also use the new token context?

Comment on lines +695 to +701
def resetToBase(annotation: Annotation)(implicit ctx: DBAccessContext): Fox[Unit] =
for {
_ <- bool2Fox(annotation.typ == AnnotationType.Task) ?~> "annotation.revert.tasksOnly"
dataset <- datasetDAO.findOne(annotation._dataset)
tracingStoreClient <- tracingStoreService.clientFor(dataset)
_ <- tracingStoreClient.resetToBase(annotation._id) ?~> "annotation.revert.failed"
} yield ()
Copy link
Contributor

Choose a reason for hiding this comment

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

Here the postgres data is not updated (previously it wasn't as well). This is not needed because it will automatically happen in case the update action resetToBase (or so) is applied? Is this ensured by the frontend by e.g. auto reloading the annotation after triggering the action?

Just wanted to double check this as it came up in my mind. I am not familiar with the reset process of task annotations :)

def duplicateSkeletonTracing(skeletonTracingId: String,
versionString: Option[String] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

just double checking: we do no longer support duplicating a specific version becasue this code is only called when creating a task based on the base annotation and this does not have versions anyway?

POST /tracingstores/:name/validateUserAccess controllers.UserTokenController.validateAccessViaTracingstore(name: String, key: String, token: Option[String])
PUT /tracingstores/:name controllers.TracingStoreController.update(name: String)
GET /tracingstores/:name/dataSource controllers.WKRemoteTracingStoreController.dataSourceForTracing(name: String, key: String, tracingId: String)
GET /tracingstores/:name/dataSourceId controllers.WKRemoteTracingStoreController.dataSourceIdForTracing(name: String, key: String, tracingId: String)
GET /tracingstores/:name/annotationId controllers.WKRemoteTracingStoreController.annotationIdForTracing(name: String, key: String, tracingId: String)
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a random thought: If this route is called often, wouldn't it be possible to save the annotation id in the tracing proto object and just get ir from there instead of asking he core backend?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah right, I remember now seeing the TSRemoveWKClient -> this is cached. So it should be fine imo.

))
} yield ()
} else Fox.successful(())
}
_ = Instant.logSince(
before,
s"Duplicating $bucketCount volume buckets from $sourceTracingId v${sourceTracing.version} to $newTracingId v${newTracing.version}.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be removed? Was this for performance testing or do you want to keep it for general monitoring?

userToken: Option[String])(implicit mp: MessagesProvider): Fox[MergedVolumeStats] = {
def mergeVolumeData(
tracingIds: Seq[String],
tracings: Seq[VolumeTracing],
Copy link
Contributor

Choose a reason for hiding this comment

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

On a high level:
Honestly, I think it would be useful to include the tracing id in the volume and skeleton proto. This helps in the code not having to always pass both. This might make things easier. e.g. the tracingIds.zip(tracings) from below might not be needed.

this.copy(actionAuthorId = authorId)
override def isViewOnlyChange: Boolean = true
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you remove this?

this.copy(actionAuthorId = authorId)
override def isViewOnlyChange: Boolean = true
Copy link
Contributor

Choose a reason for hiding this comment

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

why did you remove this here as well?


object UpdateUserBoundingBoxVisibility {
implicit val jsonFormat: OFormat[UpdateUserBoundingBoxVisibility] = Json.format[UpdateUserBoundingBoxVisibility]
override def isViewOnlyChange: Boolean = true
Copy link
Contributor

Choose a reason for hiding this comment

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

🤔 in some skeleton actions you explicitly removed this isViewOnlyChange. Here you added it

@MichaelBuessemeyer
Copy link
Contributor

re-requesting review for frontend

@MichaelBuessemeyer MichaelBuessemeyer requested review from philippotto and MichaelBuessemeyer and removed request for philippotto December 23, 2024 11:22
@MichaelBuessemeyer
Copy link
Contributor

MichaelBuessemeyer requested a review from philippotto now

sorry, misclick 🙈

Comment on lines +1937 to +1943
const params = new URLSearchParams();

const segmentIdBuffer = serializeProtoListOfLong<T>(segmentIds);
const listArrayBuffer: ArrayBuffer = await doWithToken((token) =>
Request.receiveArraybuffer(
`${dataStoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/agglomerates/${mappingId}/agglomeratesForSegments?token=${token}`,
const listArrayBuffer: ArrayBuffer = await doWithToken((token) => {
params.append("token", token);
return Request.receiveArraybuffer(
`${dataStoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/agglomerates/${mappingId}/agglomeratesForSegments?${params}`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Very nitpicky but what about

Suggested change
const params = new URLSearchParams();
const segmentIdBuffer = serializeProtoListOfLong<T>(segmentIds);
const listArrayBuffer: ArrayBuffer = await doWithToken((token) =>
Request.receiveArraybuffer(
`${dataStoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/agglomerates/${mappingId}/agglomeratesForSegments?token=${token}`,
const listArrayBuffer: ArrayBuffer = await doWithToken((token) => {
params.append("token", token);
return Request.receiveArraybuffer(
`${dataStoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/agglomerates/${mappingId}/agglomeratesForSegments?${params}`,
const segmentIdBuffer = serializeProtoListOfLong<T>(segmentIds);
const listArrayBuffer: ArrayBuffer = await doWithToken((token) => {
const params = new URLSearchParams({token});
return Request.receiveArraybuffer(
`${dataStoreUrl}/data/datasets/${dataSourceId.owningOrganization}/${dataSourceId.directoryName}/layers/${layerName}/agglomerates/${mappingId}/agglomeratesForSegments?${params}`,

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.

Ok 🎉 here is my frontend review.
Thanks for implementing the frontend part @philippotto.

The code was mainly very straight forward imo. So no big suggestions from my side.

Open TODOs for me

  • Testing
  • Review backend tests

Comment on lines +39 to 58
// The action creators pushSaveQueueTransaction and pushSaveQueueTransactionIsolated
// are typed so that update actions that need isolation are isolated in a group each.
// From this point on, we can assume that the groups fulfil the isolation requirement.
export const pushSaveQueueTransaction = (
items: Array<UpdateAction>,
saveQueueType: SaveQueueType,
tracingId: string,
transactionId: string = getUid(),
) =>
items: Array<UpdateActionWithoutIsolationRequirement>,
): PushSaveQueueTransaction =>
({
type: "PUSH_SAVE_QUEUE_TRANSACTION",
items,
saveQueueType,
tracingId,
transactionId,
transactionId: getUid(),
}) as const;

export const pushSaveQueueTransactionIsolated = (
item: UpdateActionWithIsolationRequirement,
): PushSaveQueueTransaction =>
({
type: "PUSH_SAVE_QUEUE_TRANSACTION",
items: [item],
transactionId: getUid(),
}) as const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice 🎉
looks like you found a very good solution to type support the isolation requirement and also made it readable in the code 🎉 💪

Comment on lines +266 to 274
const volumeTracing = yield* select((state) => getActiveSegmentationTracing(state));
if (!volumeTracing || !volumeTracing.mappingName) {
// This should never occur, because the proofreading tool is only available when a volume tracing layer is active.
throw new Error("No active segmentation tracing layer. Cannot create editable mapping.");
}
const upToDateVolumeTracing = yield* select((state) => getActiveSegmentationTracing(state));
if (upToDateVolumeTracing == null) {
throw new Error("No active segmentation tracing layer. Cannot create editable mapping.");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

One of the selects can be removed. I don't see any scenario in which the might differ as no reducer or such should be able to run inbetween the two select statements. Or am I wrong here?

Moreover, the second check (ll. 272-274) is already included in the first if check. Therefore, this can also be removed IMO

Comment on lines 218 to -224

if (showVersionRestore) {
yield* put(setVersionRestoreVisibilityAction(true));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for moving cleaning this up by moving it to annotation_saga (if I remember correctly)

Comment on lines +391 to +394
export enum AnnotationLayerEnum {
Skeleton = "Skeleton",
Volume = "Volume",
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@philippotto I think I added this change. As you did not undo this refactoring I can assume that you agree that this is an improvement?

@@ -220,14 +227,14 @@ export function AnnotationStats({
{asInfoBlock && <p className="sidebar-label">Statistics</p>}
<table className={asInfoBlock ? "annotation-stats-table" : "annotation-stats-table-slim"}>
<tbody>
{"treeCount" in stats ? (
{skeletonStats && "treeCount" in skeletonStats ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't the "treeCount" in skeletonStats too much checking here? Shouldn't checking skeletonStats != null be sufficient?

Comment on lines +137 to +139
if (initialMaybeCompoundType != null) {
annotation = await getAnnotationCompoundInformation(annotationId, initialMaybeCompoundType);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

@fm3, I am just double checking here Does the frontend also need to fetch an annotation proto object in order to get an up to date compound annotation? Afaik not as this is "just" a merged annotation which is in the temporary cache in the tracing store.

Copy link
Contributor

Choose a reason for hiding this comment

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

So imo the code here should be correct not requesting an annotation proto object from the tracing store

@@ -118,10 +124,11 @@ export const annotation: APIAnnotation = {
allowDownload: true,
},
annotationLayers: [
// does this still exist?
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean by that?

@@ -2,10 +2,10 @@ import mockRequire from "mock-require";
import test from "ava";
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for improving the typing in this file 🙏

t.deepEqual(updateActions[1], {
name: "deleteTree",
value: {
actionTracingId: "tracingId",
Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer Dec 23, 2024

Choose a reason for hiding this comment

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

Maybe define actionTracingId for the whole file and use it like this

Suggested change
actionTracingId: "tracingId",
value: {
actionTracingId,
id: 2,
},

@@ -1544,7 +1539,7 @@ Generated by [AVA](https://avajs.dev).
interpolation: true,
mag: 2,
position: {
x: 9120,
x: 9286,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did the coordinates change here?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's actually pretty important to clarify this (IMO)

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