-
Notifications
You must be signed in to change notification settings - Fork 27
Make user bounding box updates more efficient #8492
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
Conversation
📝 Walkthrough""" WalkthroughThis change refactors user bounding box update handling from bulk list-based updates to granular, incremental actions for adding, deleting, updating, and toggling visibility of individual bounding boxes. It updates frontend logic, backend API, serialization, and tests to support and verify these fine-grained operations, improving efficiency and clarity. Changes
Assessment against linked issues
Suggested labels
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
⏰ Context from checks skipped due to timeout of 90000ms (3)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@MichaelBuessemeyer How would you feel about three new actions, Add/Delete/Update bbox? That way we don’t need an update action for every property. |
Yes, I agree that there are already a lot of update actions. My idea was to follow how it is done for skeleton update actions. There we also have the update action on such a detailed degree. Moreover, when thinking about the upcoming collab feature, the design doc states:
I can image the same problem with bounding boxes:
At 4. the frontend now needs to be really smart to first find out by a diff that the name of the bounding box was changed. A simple comparison with the current BBox state of User B is not possible as the diff would also reveal that the color changed (done by User B). But only the name did and only the name should be updated. This complexity could be saved in case we have an action for each field making things easier implementation wise (at least that's how I see it on first thought 🤔) Although as the bounding boxes state are saved at the time Users B last successful update was submitted to the backend, there should be a state that can be compared with to find out that actually only the name changed. But I am not sure how complicated such a code would look like compared to a "updateBBoxNameAction" and simply applying it 🤔. To me more update actions sound more tedious code-wise but should be more straight forward to implement leading to more code lines but less code complexity |
I'm also in favor of having less update action types. I think we should aim for update actions where not all properties have to be listed. For example, one could have I think, we already had an issue for this, but I cannot find it anymore. from what I remember, the above is a bit tricky, because scala cannot distinguish between |
You are probably referring to #6657 – there we didn’t consider this for update actions but it might just work there too. |
what timestamp should be put into the updateAction? the moment the first frontend action was fired or the moment when the annotation is saved? edit: this is done within update_actions.ts with the types |
my initial solution is to call the frontend updateAction with a edit: talked with @MichaelBuessemeyer, seems to be fine |
@MichaelBuessemeyer I updated the frontend code, so that new the new updateActions are called. I tested it a bit with the old backend code, and it seems like the right actions are dispatched if the bounding boxes change. Let me know if the code seems buggy in any way! |
frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts
Outdated
Show resolved
Hide resolved
…e-efficient-bbox-update-actions
…e-efficient-bbox-update-actions
…w add update actions
Nice 👍 awesome. Thanks a lot 🙏
Yeah sorry there is a little buggy / unexpected behaviour: Moreover, I noticed that the frontend seems to send "empty" updates: The Moreover, the cherry on top would be that multiple consecutive bounding box updates to the same bounding box would be combined into a single action. Something like this is already done by the frontend before sending the update actions. See |
@MichaelBuessemeyer thanks for the reports! I will work on this now and try to fix the three problems. |
…bleminds/webknossos into more-efficient-bbox-update-actions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (1)
158-193
: Well-implemented tri-state option handling for partial updates.The
UpdateUserBoundingBoxVolumeAction
correctly implements the tri-state option pattern where:
None
= don't update the fieldSome(None)
= set field to null/NoneSome(Some(value))
= set field to valueThe logic in
applyOn
properly handles these cases and maintains existing values when updates aren't specified.Consider extracting the tri-state update logic into helper methods for better readability:
+ private def updateOptionalField[T](currentValue: T, updateValue: Option[Option[T]]): T = + updateValue.getOrElse(currentValue) + + private def updateProtoField[T, P](currentValue: P, updateValue: Option[Option[T]], converter: Option[T] => P): P = + if (updateValue.isDefined) updateValue.flatMap(converter).getOrElse(currentValue) else currentValue override def applyOn(tracing: VolumeTracing): VolumeTracing = { def updateUserBoundingBoxes() = tracing.userBoundingBoxes.map { currentBoundingBox => if (boundingBoxId == currentBoundingBox.id) { currentBoundingBox.copy( - name = name.getOrElse(currentBoundingBox.name), - color = if (color.isDefined) color.flatMap(colorOptToProto) else currentBoundingBox.color, - boundingBox = - if (boundingBox.isDefined) - boundingBox.flatMap(boundingBoxOptToProto).getOrElse(currentBoundingBox.boundingBox) - else currentBoundingBox.boundingBox + name = updateOptionalField(currentBoundingBox.name, name), + color = updateProtoField(currentBoundingBox.color, color, colorOptToProto), + boundingBox = updateProtoField(currentBoundingBox.boundingBox, boundingBox, boundingBoxOptToProto) ) } else currentBoundingBox } tracing.withUserBoundingBoxes(updateUserBoundingBoxes()) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala
(3 hunks)webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala
(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
🔇 Additional comments (9)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (3)
122-138
: LGTM! Well-structured action following established patterns.The
AddUserBoundingBoxVolumeAction
correctly implements the pattern used by other volume update actions. TheapplyOn
method properly appends the new bounding box to the existing list using the:+
operator.
140-156
: LGTM! Clean deletion logic with appropriate no-op behavior.The
DeleteUserBoundingBoxVolumeAction
correctly filters out the bounding box by ID. The no-op behavior when the ID doesn't exist is reasonable and consistent with similar actions in the codebase.
478-489
: LGTM! Appropriate JSON formatting for all new actions.The companion objects correctly provide JSON formatters following the established pattern. The use of
tristateOptionParsing
forUpdateUserBoundingBoxVolumeAction
is appropriate for handling the tri-state option fields.webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala (6)
4-6
: LGTM!The new imports are appropriately added to support the new bounding box update actions.
544-560
: LGTM!The
AddUserBoundingBoxSkeletonAction
correctly implements the addition of a new bounding box by appending it to the existing list.
562-578
: LGTM!The
DeleteUserBoundingBoxSkeletonAction
correctly implements bounding box deletion by filtering out the box with the specified ID.
580-615
: LGTM!The
UpdateUserBoundingBoxSkeletonAction
correctly implements partial updates using tristate options, allowing fine-grained property updates as discussed in the PR. The logic properly handles optional fields and preserves existing values when fields are not provided.
698-709
: LGTM!The JSON formatters are correctly implemented, with
UpdateUserBoundingBoxSkeletonAction
appropriately usingTristateOptionJsonHelper
to handle partial updates.
526-542
:❓ Verification inconclusive
Verify the coexistence of bulk and granular update actions.
The existing
UpdateUserBoundingBoxesSkeletonAction
performs bulk replacement of all bounding boxes, while the new actions provide granular add/delete/update operations. Please confirm that maintaining both approaches is intentional for backward compatibility or specific use cases.Run the following script to check the usage of both bulk and granular bounding box update actions:
🏁 Script executed:
#!/bin/bash # Description: Check usage patterns of bulk vs granular bounding box update actions # Search for usage of bulk update action echo "=== Bulk update action usage ===" rg -A 3 "UpdateUserBoundingBoxesSkeletonAction" # Search for usage of granular update actions echo -e "\n=== Granular update actions usage ===" rg -A 3 "(AddUserBoundingBoxSkeletonAction|DeleteUserBoundingBoxSkeletonAction|UpdateUserBoundingBoxSkeletonAction)"Length of output: 12251
Verify coexistence of bulk and granular update actions
Both the bulk
UpdateUserBoundingBoxesSkeletonAction
and the new granular actions (AddUserBoundingBoxSkeletonAction
,DeleteUserBoundingBoxSkeletonAction
,UpdateUserBoundingBoxSkeletonAction
, etc.) are still defined in
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala
and are all referenced inwebknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala
for JSON (de)serialization.Please confirm that retaining both the bulk replace action and the incremental update actions is intentional (e.g., for backward compatibility or distinct usage scenarios).
...tore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (1)
11-11
: Remove unused NamedBoundingBoxUpdate importThe
NamedBoundingBoxUpdate
import is still present but not used anywhere in the new code. This should be removed as flagged in the previous review.
🧹 Nitpick comments (1)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (1)
158-193
: Complex but correct tristate option handlingThe
UpdateUserBoundingBoxVolumeAction
implementation correctly handles the tristate option pattern (Option[Option[T]]
) for partial updates. However, the logic inapplyOn
is quite complex and could benefit from better readability.Consider extracting the update logic into helper methods:
override def applyOn(tracing: VolumeTracing): VolumeTracing = { - def updateUserBoundingBoxes() = - tracing.userBoundingBoxes.map { currentBoundingBox => - if (boundingBoxId == currentBoundingBox.id) { - currentBoundingBox.copy( - name = name.getOrElse(currentBoundingBox.name), - color = if (color.isDefined) color.flatMap(colorOptToProto) else currentBoundingBox.color, - boundingBox = - if (boundingBox.isDefined) - boundingBox.flatMap(boundingBoxOptToProto).getOrElse(currentBoundingBox.boundingBox) - else currentBoundingBox.boundingBox - ) - } else - currentBoundingBox - } + def updateUserBoundingBoxes() = + tracing.userBoundingBoxes.map { currentBoundingBox => + if (boundingBoxId == currentBoundingBox.id) { + updateBoundingBoxFields(currentBoundingBox) + } else { + currentBoundingBox + } + } + + def updateBoundingBoxFields(current: NamedBoundingBoxProto) = + current.copy( + name = name.getOrElse(current.name), + color = updateColor(current.color), + boundingBox = updateBoundingBox(current.boundingBox) + ) + + def updateColor(currentColor: Option[ColorProto]) = + if (color.isDefined) color.flatMap(colorOptToProto) else currentColor + + def updateBoundingBox(currentBBox: Option[BoundingBoxProto]) = + if (boundingBox.isDefined) + boundingBox.flatMap(boundingBoxOptToProto).getOrElse(currentBBox) + else currentBBox tracing.withUserBoundingBoxes(updateUserBoundingBoxes()) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala
(3 hunks)
🔇 Additional comments (3)
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (3)
122-138
: LGTM! Clean implementation for adding user bounding boxesThe
AddUserBoundingBoxVolumeAction
implementation is straightforward and correct. TheapplyOn
method properly appends the new bounding box to the existing list, and all required override methods are implemented consistently.
140-156
: LGTM! Correct implementation for deleting user bounding boxesThe
DeleteUserBoundingBoxVolumeAction
correctly filters out the bounding box by ID. The implementation is clean and follows the established patterns in the codebase.
478-489
: Appropriate JSON format implementationsThe JSON format objects are correctly implemented:
- Standard JSON.format for
AddUserBoundingBoxVolumeAction
andDeleteUserBoundingBoxVolumeAction
- Proper use of
TristateOptionJsonHelper
andtristateOptionParsing
forUpdateUserBoundingBoxVolumeAction
to handle the tristate option patternThis ensures proper serialization/deserialization of the complex nested option types.
…e-efficient-bbox-update-actions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/javascripts/viewer/view/version_entry.tsx (1)
91-102
: Address the previous review comments about join separatorThe previous review comments from philippotto suggested using
", "
instead of","
for better readability. This hasn't been addressed yet.Apply this diff to improve readability:
- description: `Updated bounding boxes ${firstAction.value.boundingBoxes.map((bbox) => bbox.id).join()}.`, + description: `Updated bounding boxes ${firstAction.value.boundingBoxes.map((bbox) => bbox.id).join(", ")}.`,Also for line 100:
- description: `Updated bounding boxes ${firstAction.value.boundingBoxes.map((bbox) => bbox.id).join()}.`, + description: `Updated bounding boxes ${firstAction.value.boundingBoxes.map((bbox) => bbox.id).join(", ")}.`,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
frontend/javascripts/viewer/model/reducers/reducer_helpers.ts
(1 hunks)frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts
(5 hunks)frontend/javascripts/viewer/model/sagas/update_actions.ts
(4 hunks)frontend/javascripts/viewer/model/sagas/volumetracing_saga.tsx
(3 hunks)frontend/javascripts/viewer/view/version_entry.tsx
(4 hunks)webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala
(5 hunks)webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala
(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
frontend/javascripts/viewer/model/sagas/update_actions.ts (1)
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: frontend-tests
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (11)
frontend/javascripts/viewer/model/sagas/volumetracing_saga.tsx (1)
491-496
: LGTM! Good refactoring to use shared diffing logic.The change to use
diffBoundingBoxes
for volume tracing aligns well with the skeleton tracing implementation, promoting code reuse and consistency across both tracing types.webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/annotation/UpdateActions.scala (1)
57-60
: JSON serialization correctly implemented for new bounding box actions.The serialization and deserialization cases are properly matched and follow the established naming convention (without "Action" suffix in the string names).
Also applies to: 69-72, 153-161, 174-182
webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala (4)
124-140
: Add action correctly implemented.The
AddUserBoundingBoxVolumeAction
properly appends the new bounding box to the existing list while maintaining immutability.
142-158
: Delete action correctly filters by ID.The implementation properly removes the bounding box with the matching ID while preserving all others.
160-195
: Partial update implementation handles tri-state logic correctly.The
UpdateUserBoundingBoxVolumeAction
properly uses theOption[Option[T]]
pattern to distinguish between:
None
: Don't update the fieldSome(None)
: Clear the fieldSome(Some(value))
: Update the field with the valueThe implementation correctly applies these semantics in the
applyOn
method.
546-549
: JSON formatter correctly configured for tri-state options.The use of
TristateOptionJsonHelper
andtristateOptionParsing
ensures proper serialization/deserialization of the partial update fields.frontend/javascripts/viewer/model/reducers/reducer_helpers.ts (1)
54-59
: LGTM! Clean refactoring to handle single bounding box.The function has been correctly renamed to singular form and refactored to process individual bounding boxes, aligning with the new granular update pattern.
frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts (2)
608-614
: Good implementation of bounding box diffing with proper optimizations.The early return check and use of
Utils.diffArrays
for ID comparison is efficient and follows the suggested performance improvements.
666-670
: Verify the intention of always sending complete bounds.When the boundingBox changes, the code ensures both
min
andmax
are included in the update, even if only one property changed. This deviates from the granular update pattern where only changed properties are sent.Is this intentional to ensure the backend always receives complete bounds, or should it respect the granular diff?
frontend/javascripts/viewer/model/sagas/update_actions.ts (1)
13-14
: Good documentation for the type's purpose.The comment clearly explains that this type contains only the properties that have changed, which helps prevent confusion.
frontend/javascripts/viewer/view/version_entry.tsx (1)
103-150
: LGTM! Well-structured bounding box action handlersThe new bounding box action handlers are implemented consistently:
- Clear, descriptive messages that include the bounding box ID
- Appropriate icons for each action type (create, delete, update, visibility toggle)
- Follows the existing pattern in the codebase
- Aligns with the learnings about separate visibility update actions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
frontend/javascripts/viewer/model/sagas/update_actions.ts (1)
494-513
:⚠️ Potential issueFix logic error: use
rest
instead ofupdatedProps
when boundingBox is null.When
boundingBox
is null/undefined, the function returnsupdatedProps
which still contains the boundingBox property. This should userest
to exclude the property entirely when it's not being updated.const { boundingBox, ...rest } = updatedProps; const updatedPropsForServer = boundingBox != null ? { ...rest, boundingBox: Utils.computeBoundingBoxObjectFromBoundingBox(boundingBox) } - : updatedProps; + : rest;
🧹 Nitpick comments (2)
frontend/javascripts/viewer/model/helpers/compaction/compact_save_queue.ts (2)
89-91
: Consider using a Set for action name lookups.For better performance, especially if this function is called frequently, consider using a Set for O(1) lookups instead of array includes().
+const BBOX_UPDATE_ACTION_NAMES = new Set([ + "updateUserBoundingBoxInSkeletonTracing", + "updateUserBoundingBoxInVolumeTracing", +]); + export function removeSubsequentUpdateBBoxActions(updateActionsBatches: Array<SaveQueueEntry>) { // Actions are obsolete, if they are for the same bounding box and for the same prop. // E.g. when rezising a bounding box, multiple updateActions are sent during the resize, while only the last one is needed. const previousActionsById: Record< string, UpdateUserBoundingBoxInSkeletonTracingAction | UpdateUserBoundingBoxInVolumeTracingAction > = {}; const relevantActions = []; for (let i = updateActionsBatches.length - 1; i >= 0; i--) { const currentActions = updateActionsBatches[i].actions; if ( currentActions.length === 1 && - ["updateUserBoundingBoxInSkeletonTracing", "updateUserBoundingBoxInVolumeTracing"].includes( - currentActions[0].name, - ) + BBOX_UPDATE_ACTION_NAMES.has(currentActions[0].name) ) {
77-114
: Well-implemented compaction logic with good backward iteration strategy.The function correctly implements compaction for bounding box update actions using backward iteration to preserve order while removing redundant actions. The logic properly handles:
- Different action types (skeleton vs volume tracing)
- Different bounding box IDs
- Different sets of updated properties
- Non-bounding box actions
The key comparison using
Set
and_.isEqual
onObject.keys(value)
is a clever approach to determine if actions target the same properties.One minor suggestion for improved readability:
- !_.isEqual( - new Set(Object.keys(previousAction.value)), - new Set(Object.keys(currentAction.value)), - ) + !_.isEqual( + new Set(Object.keys(previousAction.value)), + new Set(Object.keys(currentAction.value)), + )Consider extracting the key comparison to a helper function for better readability:
const hasDifferentProperties = (action1: any, action2: any) => !_.isEqual( new Set(Object.keys(action1.value)), new Set(Object.keys(action2.value)), );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/javascripts/test/model/compact_save_queue.spec.ts
(1 hunks)frontend/javascripts/viewer/model/helpers/compaction/compact_save_queue.ts
(4 hunks)frontend/javascripts/viewer/model/sagas/update_actions.ts
(4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
frontend/javascripts/viewer/model/sagas/update_actions.ts (1)
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: frontend-tests
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (7)
frontend/javascripts/viewer/model/helpers/compaction/compact_save_queue.ts (4)
138-156
: LGTM! Clean refactoring using functional composition.The use of
_.flow
for composing the compaction functions is an excellent improvement that makes the code more maintainable and easier to extend.
2-5
: LGTM: Clean import addition for bounding box actions.The import statement correctly brings in the required types for the new bounding box compaction functionality.
138-145
: Excellent refactoring with the pipeline approach.The
compactAll
pipeline using_.flow
greatly improves readability and follows functional programming principles. This addresses the "getting out of hand" concern mentioned in past review comments and makes the code more maintainable.
155-155
: Clean integration of the new pipeline.The refactored function call using
compactAll(result)
is much cleaner than the previous nested approach and integrates well with the new bounding box compaction functionality.frontend/javascripts/test/model/compact_save_queue.spec.ts (1)
189-259
: LGTM! Good test coverage for non-compactable scenarios.The test correctly verifies that update actions with different properties are not compacted together, ensuring the compaction logic preserves necessary updates.
frontend/javascripts/viewer/model/sagas/update_actions.ts (2)
541-568
: LGTM! Clean separation of visibility updates.The dedicated functions for visibility updates properly separate this concern from other property updates, making the code more maintainable and explicit about intent.
505-512
:✅ Verification successful
Verify the structure of update action values.
The helper function spreads
updatedPropsForServer
directly into the value object, but based on the compaction logic incompact_save_queue.ts
, it seems the structure should includeupdatedProps
as a nested object. This discrepancy could affect the compaction functionality.Run the following script to verify the expected structure of update actions:
🏁 Script executed:
#!/bin/bash # Description: Check how update actions are structured in the codebase # Search for how these actions are used in tests echo "=== Checking test usage ===" rg -A 10 "updateUserBoundingBoxIn(Skeleton|Volume)Tracing\(" frontend/javascripts/test/ # Check how the compaction logic accesses these values echo -e "\n=== Checking compaction logic ===" rg -A 5 "\.value\." frontend/javascripts/viewer/model/helpers/compaction/compact_save_queue.ts # Check backend expectations echo -e "\n=== Checking backend action definitions ===" ast-grep --pattern 'case class Update.*BoundingBox.*Action'Length of output: 9780
🏁 Script executed:
#!/bin/bash # Display a broader context around the helper in update_actions.ts sed -n '400,600p' frontend/javascripts/viewer/model/sagas/update_actions.tsLength of output: 5025
Update action value structure is correct
The
_updateUserBoundingBoxHelper
spreadingupdatedPropsForServer
directly into the action’svalue
matches both the shape asserted incompact_save_queue.spec.ts
and the compaction logic’s use ofObject.keys(value)
. No changes are needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I adapted the front-end to the newest backend changes. the front-end should be good to go now in my opinion. leaving final approval to @fm3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
frontend/javascripts/test/model/compact_save_queue.spec.ts (2)
158-160
: Remove redundant null check.This null check is unnecessary since the
expect(volumeActionValue).not.toBeNull()
assertion on line 157 would already fail the test if the value were null.- if (volumeActionValue == null) { - throw new Error("volumeActionValue must not be null"); - }
173-175
: Remove redundant null check.This null check is redundant as
skeletonActionValue
was already verified to be non-null by the assertion on line 172.- if (skeletonActionValue == null) { - throw new Error("skeletonActionValue must not be null"); - }
🧹 Nitpick comments (1)
frontend/javascripts/test/model/compact_save_queue.spec.ts (1)
13-146
: Consider extracting a helper function to reduce code duplication.The test data setup is quite verbose with repetitive
SaveQueueEntry
objects. Consider creating a helper function to make the test more maintainable and readable.function createSaveQueueEntry( transactionId: string, timestamp: number, action: UpdateUserBoundingBoxInVolumeTracingAction | UpdateUserBoundingBoxInSkeletonTracingAction ): SaveQueueEntry { return { version: -1, transactionId, transactionGroupCount: 1, transactionGroupIndex: 0, timestamp, authorId: "123", actions: [action], stats: undefined, info: "", }; }Then use it to create the test data more concisely:
const actions: SaveQueueEntry[] = [ createSaveQueueEntry("eq3scfgvoa", 1747994347590, updateUserBoundingBoxInVolumeTracing(3, { boundingBox: { min: [3224, 3955, 944], max: [3225, 4112, 956] } }, "volumeTracing1") ), // ... other entries ];
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
CHANGELOG.unreleased.md
(1 hunks)frontend/javascripts/test/model/compact_save_queue.spec.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- CHANGELOG.unreleased.md
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: backend-tests
- GitHub Check: build-smoketest-push
- GitHub Check: frontend-tests
🔇 Additional comments (2)
frontend/javascripts/test/model/compact_save_queue.spec.ts (2)
12-183
: Test coverage is comprehensive and logic is correct.The test properly verifies that:
- Multiple consecutive bounding box updates for the same tracing are compacted down to the latest values
- Both volume and skeleton tracing actions are handled correctly
- The final bounding box coordinates are correctly computed from min/max to topLeft/width/height/depth format
The assertions validate the expected behavior accurately.
185-254
: Good test case for preventing over-compaction.This test correctly verifies that actions updating different properties (name vs boundingBox) are not compacted together, which is important for maintaining the integrity of different types of updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backend LGTM
before merging @philippotto did you test this? At least I did not 🙈 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/javascripts/viewer/view/version_entry.tsx
(4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
🔇 Additional comments (5)
frontend/javascripts/viewer/view/version_entry.tsx (5)
26-28
: LGTM! Comprehensive import additions for bounding box actions.The imports correctly include all the new action types needed for the granular bounding box update handlers. The LEGACY_ prefix appropriately indicates backward compatibility support.
Also applies to: 37-40, 60-63
91-102
: LGTM! Proper legacy action handling with backward compatibility.The LEGACY_ prefix clearly indicates the deprecated status while maintaining the existing functionality for bulk bounding box updates. The join with ", " formatting follows the previous feedback.
103-114
: LGTM! Consistent implementation of add bounding box actions.The handlers follow the established pattern for creation actions with appropriate PlusOutlined icons and descriptive text that includes the bounding box ID as requested in previous reviews.
115-126
: LGTM! Consistent implementation of delete bounding box actions.The handlers correctly follow the established pattern for deletion actions with appropriate DeleteOutlined icons and clear descriptions that include the bounding box ID.
127-138
: LGTM! Consistent implementation of update bounding box actions.The handlers follow the established pattern for update actions with appropriate EditOutlined icons and clear descriptions that include the bounding box ID.
yes, I tested it. will merge tomorrow for a better support window :) |
…only some of them changed; replace dangerous implementation of diffObjects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/javascripts/dashboard/dataset/dataset_settings_view.tsx
(2 hunks)frontend/javascripts/libs/utils.ts
(1 hunks)frontend/javascripts/test/sagas/bounding_box_saving.spec.ts
(1 hunks)frontend/javascripts/viewer/model/sagas/save_saga.ts
(1 hunks)frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts
(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: knollengewaechs
PR: scalableminds/webknossos#8492
File: frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:671-674
Timestamp: 2025-05-15T19:44:16.110Z
Learning: Visibility updates for user bounding boxes are intentionally handled separately from other property updates, with dedicated update actions for visibility changes.
⏰ Context from checks skipped due to timeout of 90000ms (2)
- GitHub Check: build-smoketest-push
- GitHub Check: backend-tests
🔇 Additional comments (3)
frontend/javascripts/viewer/model/sagas/save_saga.ts (1)
163-165
: LGTM! Documentation improvement.The JSDoc comment has been appropriately generalized to better reflect the function's actual behavior, removing the specific mention of tracingId which makes it more accurate.
frontend/javascripts/libs/utils.ts (1)
1087-1108
:❓ Verification inconclusive
Verify the intentional change from deep to shallow diffing.
The
diffObjects
function has been completely rewritten with improved type safety and documentation. However, this changes the behavior from deep diffing to shallow diffing.Please confirm that the change from deep to shallow diffing is intentional and won't break existing functionality:
🏁 Script executed:
#!/bin/bash # Description: Search for all usages of diffObjects to verify the impact of changing from deep to shallow diffing # Expected: Find all call sites to assess if shallow diffing is sufficient echo "Searching for diffObjects usage..." rg -A 3 -B 1 "diffObjects" --type ts --type tsx echo "Searching for imports of diffObjects..." rg "import.*diffObjects" --type ts --type tsxLength of output: 319
🏁 Script executed:
#!/bin/bash # Re-run search for diffObjects usages without relying on filetype detection echo "Searching for diffObjects usage in .ts and .tsx files..." rg -n "diffObjects" -g "*.ts" -g "*.tsx" echo "Searching for imports of diffObjects in .ts and .tsx files..." rg -n "import.*diffObjects" -g "*.ts" -g "*.tsx"Length of output: 703
Confirm shallow-only diffing is intended
I found one usage of
diffObjects
in:
frontend/javascripts/viewer/model/sagas/skeletontracing_saga.ts:658
(comparing two flat bounding-box objects)With the new implementation, any nested objects will no longer be diffed (only top-level keys are compared). Please verify that:
- All call sites use only flat, primitive-valued objects
- No existing consumers rely on recursive/deep diffing behavior
If deeper comparisons are still needed, consider renaming the function (e.g.
shallowDiffObjects
) or reintroducing recursive logic.frontend/javascripts/test/sagas/bounding_box_saving.spec.ts (1)
1-356
: Excellent test coverage for bounding box functionality.This comprehensive test suite effectively validates the new bounding box diffing and compaction logic with well-structured test cases:
- Bounding box diffing: Thoroughly tests the generation of correct update actions for deletions, additions, visibility changes, and property updates
- Compaction logic: Validates that multiple updates to the same bounding box are properly compacted
- Non-compaction edge case: Ensures different property updates are not incorrectly compacted
The test data is realistic, assertions are specific, and the test structure follows best practices.
This PR add more update actions for user bounding boxes. This makes it more efficient to update a single bounding box (including adding and deleting). Moreover, the version restore view can display more details on what was actually changed (e.g. color of the bounding box).
URL of deployed dev instance (used for testing):
Steps to test:
TODOs:
updateUserBoundingBoxVisibility
actions for visibility updatesupdateUserSpecificBoundingBox[Volume|Skeleton]
)Issues:
(Please delete unneeded items, merge only when none are left open)