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

[GSoC2024] Implemented drag and drop feature #7624

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from

Conversation

KrishavRajSingh
Copy link
Contributor

@KrishavRajSingh KrishavRajSingh commented Mar 17, 2024

Added the feature of dragging attributes

Motivation and context

resolves #7370

Checklist

  • [*] I submit my changes into the develop branch
  • [*] I have created a changelog fragment
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • [*] I have linked related issues (see GitHub docs)
  • I have increased versions of npm packages if it is necessary
    (cvat-canvas,
    cvat-core,
    cvat-data and
    cvat-ui)

License

  • I submit my code changes under the same MIT License that covers the project.
    Feel free to contact the maintainers if that's a concern.

Summary by CodeRabbit

  • New Features

    • Introduced drag-and-drop functionality for label attributes in the label editor.
  • Style

    • Updated styles to support drag-and-drop handles with a pointer cursor.

@KrishavRajSingh KrishavRajSingh changed the title Added up/down icon for changing order of attributes [GSoC2024] Added up/down icon for changing order of attributes Mar 17, 2024
Copy link
Contributor

@klakhov klakhov left a comment

Choose a reason for hiding this comment

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

Generally im not sure if this solution is good in UX terms. Design with arrows is a bit outdated and not so comfortable to use (if I want to place 10th attribute to the first place I need to make 10 clicks 😟).
Lets return to original proposal, as the author of 7370 mentiones, it sould be implemented as drag-n-drop. I belive we need to use dots icon that is shown only on hover.
It should be something similar to:
list
In CVAT, we use react-grid-layout to help us implement draggable and responsible layouts on analytics page and canvas-layout. I would suggest to implement this feature using specific rows-only draggable layout.

Also, please, add a link to the issue you're trying to resolve in PR description. And dont forget to add a changelog entry.

.vscode/launch.json Outdated Show resolved Hide resolved
@klakhov klakhov requested review from klakhov and removed request for bsekachev and azhavoro March 18, 2024 12:57
@klakhov klakhov added the ui/ux label Mar 18, 2024
@KrishavRajSingh
Copy link
Contributor Author

Ok, I will make the changes

@KrishavRajSingh
Copy link
Contributor Author

Generally im not sure if this solution is good in UX terms. Design with arrows is a bit outdated and not so comfortable to use (if I want to place 10th attribute to the first place I need to make 10 clicks 😟). Lets return to original proposal, as the author of 7370 mentiones, it sould be implemented as drag-n-drop. I belive we need to use dots icon that is shown only on hover. It should be something similar to: list list In CVAT, we use react-grid-layout to help us implement draggable and responsible layouts on analytics page and canvas-layout. I would suggest to implement this feature using specific rows-only draggable layout.

Also, please, add a link to the issue you're trying to resolve in PR description. And dont forget to add a changelog entry.

Hey I think react-grid-layout doesn't works on Forms.

@klakhov
Copy link
Contributor

klakhov commented Mar 20, 2024

What do you mean by doesnt work on Forms?
I dont think it can work directly on the the Form.Item components. As usage examples of react-grid-layouts shows, we need to pass just a div inside of it. My first thoughts of the solution are: we can wrap Form.Item in div and then manage layout somewhere in the state of the label form component. At least antd form docs here and here show that it should be possible. Probably we need to refactor the label form component as its heavily dependent on antd form so we can manage attributes layout correctly.

Could you describe the soultion you are trying to implement so we can discuss it?

@KrishavRajSingh
Copy link
Contributor Author

KrishavRajSingh commented Mar 20, 2024

What do you mean by doesnt work on Forms? I dont think it can work directly on the the Form.Item components. As usage examples of react-grid-layouts shows, we need to pass just a div inside of it. My first thoughts of the solution are: we can wrap Form.Item in div and then manage layout somewhere in the state of the label form component. At least antd form docs here and here show that it should be possible. Probably we need to refactor the label form component as its heavily dependent on antd form so we can manage attributes layout correctly.

Could you describe the soultion you are trying to implement so we can discuss it?

I was trying to implement the same but after wrapping with < ReactGridLayout >, this.renderAttributes was not returning anything. Most probably because fieldInstances was empty when calling from < ReactGridLayout >.
This is a gist of the changes I made <script src="https://gist.github.com/KrishavRajSingh/0b7c9ca11b75bd677413e0be0b055702.js"></script>

@klakhov
Copy link
Contributor

klakhov commented Mar 21, 2024

I think we dont need to pass Form.List into the grid, instead we should create a grid inside of a list. As you can see, Form.List expects a function that will render a list of Form.Items. We already use this function to render a plain list, but instead of that we can firstly create a layout and then return a react grid layout component. See the example. The thing is that we need to manage layout correcly in the component to keep the order of attributes in list. Also, need to work with the styles.

@KrishavRajSingh
Copy link
Contributor Author

KrishavRajSingh commented Mar 23, 2024

I think we dont need to pass Form.List into the grid, instead we should create a grid inside of a list. As you can see, Form.List expects a function that will render a list of Form.Items. We already use this function to render a plain list, but instead of that we can firstly create a layout and then return a react grid layout component. See the example. The thing is that we need to manage layout correcly in the component to keep the order of attributes in list. Also, need to work with the styles.

Hey, I have almost completed it but I am facing a problem. The thing is when we change the layout, I update the attributes list and this layout updates based on this new attributes list. These were the changes that I have made: url.
Screencast from 23-03-24 04:34:48 PM IST.webm

@klakhov
Copy link
Contributor

klakhov commented Mar 26, 2024

Hi @KrishavRajSingh , As I can see you are trying to update form after layout changes and then it re-renders. I think we can fix this problem by managing layout somewhere outside of the form. There should be some state for the layout. By default it should be empty, then on first render of the form we set it up. If user makes some changes, we set the state of the layout object. When the form is submitted (handleSubmit func) we can use the layout state to provide correct order of the attributes to the onSubmit func.

@bsekachev
Copy link
Member

bsekachev commented Mar 26, 2024

Also, I would recommend to look at sorting component. It may be found on projects/tasks/cloud storages pages
Drag & Drop is implemented there

image

And this solution will not work when attributes were already saved on the server.

@klakhov
Copy link
Contributor

klakhov commented Mar 30, 2024

Hi @KrishavRajSingh,
I was trying to invistigate why ui-only fix works for this issue in the first place. A brief look over the CVAT server, shows that there are no ordering fields, updated date, or explicit ordering in queryset for the Attribute model. There is no ORDER BY in sql queries to DB and no sorting in django. But still the experiments show that attributes are returned from the server with implicit sorting by updated_date(even we dont really have such field on our side). That means the fix works only because of some kind of optimization on DB side(which maybe caches rows in order of updation, I dont really know). The fact that it uses some ordering is undocumented and cant be relied on.
So it seems we need to extend the solution with more reliable approach to sorting.. We need something on the server side to support the ui. Can you think about it? Maybe we need to introduce ordering field, maybe we should add updated_date to attribute and sort by it.

@KrishavRajSingh
Copy link
Contributor Author

I am looking into this.

@KrishavRajSingh
Copy link
Contributor Author

Contributor

Thanks for this, I was finally able to implement this drag&drop.

Drag.Drop_CVAT.mp4

@KrishavRajSingh KrishavRajSingh changed the title [GSoC2024] Added up/down icon for changing order of attributes [GSoC2024] Implemented drag snd drop feature Apr 1, 2024
@KrishavRajSingh KrishavRajSingh changed the title [GSoC2024] Implemented drag snd drop feature [GSoC2024] Implemented drag and drop feature Apr 1, 2024
Copy link
Contributor

coderabbitai bot commented May 15, 2024

Walkthrough

The update introduces a drag-and-drop feature to reorder attributes in the CVAT UI's labels editor. This is achieved by integrating react-grid-layout to manage the layout changes and updating the label form component to handle attribute updates dynamically. Additionally, new styling has been added to support the drag-and-drop functionality.

Changes

Files/Paths Change Summary
cvat-ui/src/components/labels-editor/... Imported RGL and WidthProvider, declared ReactGridLayout, modified LabelForm for drag-and-drop
cvat-ui/src/components/labels-editor/... Added styling for .cvat-drag-drop class to set cursor to pointer

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant User
    participant LabelForm
    participant ReactGridLayout
    User->>LabelForm: Initiates drag-and-drop
    LabelForm->>ReactGridLayout: Updates layout
    ReactGridLayout->>LabelForm: Confirms new order
    LabelForm->>User: Displays updated order

Assessment against linked issues

Objective (Issue #7370) Addressed Explanation
Allow changing the order of attributes
Ensure the new order is maintained
Add drag-and-drop functionality

In the land of code where pixels dance,
Attributes now move at a glance.
Drag and drop, they find their place,
Enhancing speed, a smoother pace.
With careful clicks, the order's set,
A feature that you won't forget.

Tip

New Features and Improvements

Review Settings

Introduced new personality profiles for code reviews. Users can now select between "Chill" and "Assertive" review tones to tailor feedback styles according to their preferences. The "Assertive" profile posts more comments and nitpicks the code more aggressively, while the "Chill" profile is more relaxed and posts fewer comments.

AST-based Instructions

CodeRabbit offers customizing reviews based on the Abstract Syntax Tree (AST) pattern matching. Read more about AST-based instructions in the documentation.

Community-driven AST-based Rules

We are kicking off a community-driven initiative to create and share AST-based rules. Users can now contribute their AST-based rules to detect security vulnerabilities, code smells, and anti-patterns. Please see the ast-grep-essentials repository for more information.

New Static Analysis Tools

We are continually expanding our support for static analysis tools. We have added support for biome, hadolint, and ast-grep. Update the settings in your .coderabbit.yaml file or head over to the settings page to enable or disable the tools you want to use.

Tone Settings

Users can now customize CodeRabbit to review code in the style of their favorite characters or personalities. Here are some of our favorite examples:

  • Mr. T: "You must talk like Mr. T in all your code reviews. I pity the fool who doesn't!"
  • Pirate: "Arr, matey! Ye must talk like a pirate in all yer code reviews. Yarrr!"
  • Snarky: "You must be snarky in all your code reviews. Snark, snark, snark!"

Revamped Settings Page

We have redesigned the settings page for a more intuitive layout, enabling users to find and adjust settings quickly. This change was long overdue; it not only improves the user experience but also allows our development team to add more settings in the future with ease. Going forward, the changes to .coderabbit.yaml will be reflected in the settings page, and vice versa.

Miscellaneous

  • Turn off free summarization: You can switch off free summarization of PRs opened by users not on a paid plan using the enable_free_tier setting.
  • Knowledge-base scope: You can now set the scope of the knowledge base to either the repository (local) or the organization (global) level using the knowledge_base setting. In addition, you can specify Jira project keys and Linear team keys to limit the knowledge base scope for those integrations.
  • High-level summary placement: You can now customize the location of the high-level summary in the PR description using the high_level_summary_placeholder setting (default @coderabbitai summary).
  • Revamped request changes workflow: You can now configure CodeRabbit to auto-approve or request changes on PRs based on the review feedback using the request_changes_workflow setting.

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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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.

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 as 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 full the review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Out of diff range and nitpick comments (2)
cvat/apps/engine/views.py (2)

Line range hint 1967-1967: Convert the f-string to a regular string as it doesn't contain any placeholders.

-                return Response("{} is an invalid directory".format(directory_param),
+                return Response("{} is an invalid directory".format(directory_param),

Line range hint 2714-2714: Convert the f-string to a regular string as it doesn't contain any placeholders.

-                return Response(data='No such file were uploaded',
+                return Response(data='No such file were uploaded',
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between f4ee3f9 and 52849b4.
Files selected for processing (6)
  • changelog.d/20240401_121841_krishavrajsingh_drag_drop.md (1 hunks)
  • cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
  • cvat-ui/src/components/labels-editor/styles.scss (1 hunks)
  • cvat/apps/engine/migrations/0079_attributespec_updated_time.py (1 hunks)
  • cvat/apps/engine/models.py (1 hunks)
  • cvat/apps/engine/views.py (2 hunks)
Files skipped from review due to trivial changes (3)
  • changelog.d/20240401_121841_krishavrajsingh_drag_drop.md
  • cvat-ui/src/components/labels-editor/styles.scss
  • cvat/apps/engine/migrations/0079_attributespec_updated_time.py
Additional Context Used
Ruff (3)
cvat/apps/engine/views.py (3)

44-44: cvat.apps.dataset_manager.views imported but unused


1967-1967: f-string without any placeholders


2714-2714: f-string without any placeholders

Additional comments not posted (6)
cvat-ui/src/components/labels-editor/label-form.tsx (4)

Line range hint 20-31: Correct integration of react-grid-layout with WidthProvider.

The integration of react-grid-layout with WidthProvider and the creation of the ReactGridLayout constant are correctly implemented.


70-78: Ensure the layout state is correctly updated and attributes are reordered.

The logic for updating the layout state and reordering attributes seems correct. However, ensure that the updatedLayout and values.attributes are always in sync to avoid potential issues.


464-500: Correct rendering of attributes with drag-and-drop handle.

The rendering logic for attributes with the drag-and-drop handle using react-grid-layout is correctly implemented.


703-729: Correct integration of Form.List with react-grid-layout.

The integration of Form.List with react-grid-layout for reordering attributes using drag-and-drop is correctly implemented.

cvat/apps/engine/models.py (1)

861-862: Correct addition of updated_time field in AttributeSpec model.

The updated_time field with auto_now=True is correctly added to the AttributeSpec model.

cvat/apps/engine/views.py (1)

52-52: The import statement looks good and is necessary for the changes related to AttributeSpec.

@KrishavRajSingh
Copy link
Contributor Author

@KrishavRajSingh, Yes, I suppose it sould be like this. Could you discribe what will happen when we will save new attribute order?

The attributes will come from the front-end as it is, then a new variable display_order is introduced on the server side whose value is equal to the index of the attribute in attributes. "display_order" is set for each attribute and then it is saved to the databse.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Out of diff range and nitpick comments (1)
cvat/apps/engine/views.py (1)

Line range hint 2714-2714: Remove unnecessary f-string.

- f""
+ ""
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 52849b4 and a72c640.
Files selected for processing (6)
  • cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
  • cvat/apps/engine/migrations/0080_alter_attributespec_options_and_more.py (1 hunks)
  • cvat/apps/engine/migrations/0081_alter_attributespec_options.py (1 hunks)
  • cvat/apps/engine/models.py (1 hunks)
  • cvat/apps/engine/serializers.py (4 hunks)
  • cvat/apps/engine/views.py (2 hunks)
Files skipped from review due to trivial changes (2)
  • cvat/apps/engine/migrations/0080_alter_attributespec_options_and_more.py
  • cvat/apps/engine/migrations/0081_alter_attributespec_options.py
Additional Context Used
Ruff (5)
cvat/apps/engine/serializers.py (2)

711-711: f-string without any placeholders


1146-1146: f-string without any placeholders

cvat/apps/engine/views.py (3)

44-44: cvat.apps.dataset_manager.views imported but unused


1967-1967: f-string without any placeholders


2714-2714: f-string without any placeholders

Additional comments not posted (13)
cvat-ui/src/components/labels-editor/label-form.tsx (4)

8-10: The new imports for RGL and WidthProvider from react-grid-layout are necessary for the drag-and-drop functionality. Ensure that react-grid-layout is listed as a dependency in your package.json.

Also applies to: 20-20


31-31: The ReactGridLayout constant is correctly defined using WidthProvider and RGL. This setup is essential for the drag-and-drop functionality.


464-496: The inclusion of the drag-and-drop handle in the renderAttribute method is correct and aligns with the new feature requirements.


703-730: The integration of ReactGridLayout with Form.List for the drag-and-drop functionality is correct. Ensure that the layout state is managed correctly and consistently.

Verification successful

The integration of ReactGridLayout with Form.List for the drag-and-drop functionality is correct. The layout state is being managed correctly using this.formRef.current?.setFieldsValue and this.formRef.current?.getFieldValue.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the layout state management in the `LabelForm` component.

# Test: Search for the layout state management. Expect: Proper handling of layout state.
rg --type typescript 'layout' cvat-ui/src/components/labels-editor/label-form.tsx

Length of output: 116



Script:

#!/bin/bash
# Description: Verify the layout state management in the `LabelForm` component.

# Test: Search for the layout state management. Expect: Proper handling of layout state.
rg --type tsx 'layout' cvat-ui/src/components/labels-editor/label-form.tsx

Length of output: 102



Script:

#!/bin/bash
# Description: Verify the layout state management in the `LabelForm` component.

# Test: Search for the layout state management. Expect: Proper handling of layout state.
rg 'layout' cvat-ui/src/components/labels-editor/label-form.tsx

Length of output: 482

cvat/apps/engine/models.py (3)

861-861: The addition of the updated_time field to the AttributeSpec model is a good practice for tracking the last modification time.


862-862: The addition of the display_order field to the AttributeSpec model will help manage the order of attributes explicitly.


867-867: The addition of the ordering option in the Meta class of the AttributeSpec model ensures that attributes are ordered by the display_order field by default.

cvat/apps/engine/serializers.py (6)

238-238: Addition of display_order field is consistent with the new drag-and-drop feature.


242-242: Inclusion of display_order in the fields list ensures proper serialization.


409-409: Initialization of display_order to 0 is necessary for setting the initial order of attributes.


432-432: Setting and incrementing display_order ensures unique ordering of attributes.


438-438: Incrementing display_order after saving each attribute ensures correct ordering.


491-492: Adding display_order to the attributes dictionary ensures correct ordering for new attributes.

@@ -49,7 +49,7 @@
from cvat.apps.engine.frame_provider import FrameProvider
from cvat.apps.engine.media_extractors import get_mime
from cvat.apps.engine.models import (
ClientFile, Job, JobType, Label, SegmentType, Task, Project, Issue, Data,
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,
Copy link
Contributor

@coderabbitai coderabbitai bot May 16, 2024

Choose a reason for hiding this comment

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

Remove unused import.

- import cvat.apps.dataset_manager.views  # pylint: disable=unused-import

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove unused import.

Copy link
Contributor

Choose a reason for hiding this comment

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

@klakhov, I have verified the imports in the cvat/apps/engine/views.py file. The import statement import cvat.apps.dataset_manager.views # pylint: disable=unused-import is indeed unused and can be safely removed.

Here is the updated import section without the unused import:

from cvat.apps.engine.models import (
    ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,
)

Would you like me to open a new GitHub issue to track this change, or will you handle it in this PR?

Analysis chain
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Scan for unused imports in the specified Python file
flake8 --select=F401 cvat/apps/engine/views.py

Length of output: 122



Script:

#!/bin/bash
# List all import statements in the file
grep -E '^import |^from ' cvat/apps/engine/views.py > imports.txt

# Check usage of each imported module or symbol
while read -r line; do
    # Extract the module or symbol name
    symbol=$(echo "$line" | sed -E 's/^import |^from ([^ ]+) import .*/\1/')
    # Check if the symbol is used in the file
    if ! grep -q "$symbol" cvat/apps/engine/views.py; then
        echo "Unused import: $line"
    fi
done < imports.txt

Length of output: 11277


New Learnings Added
User: klakhov
PR: cvat-ai/cvat#7624
File: cvat/apps/engine/views.py:52-52
Timestamp: 2024-05-23T13:50:52.155Z
Learning: The import statement `import cvat.apps.dataset_manager.views  # pylint: disable=unused-import` in `cvat/apps/engine/views.py` is unused and can be removed.

Note: Learnings are effective only in the context of specific code segments. To apply general coding guidelines, please configure review instructions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove unused import.

I have not imported this. This is here since 3 years ago.
image
Should i remove this??

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@@ -2196,7 +2196,7 @@ class LabelViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
mixins.RetrieveModelMixin, mixins.DestroyModelMixin, PartialUpdateModelMixin
):
queryset = Label.objects.prefetch_related(
'attributespec_set',
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')),
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove unnecessary f-string.

- f""
+ ""

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')),
Prefetch('attributespec_set', queryset=AttributeSpec.objects.order_by('display_order')),

Comment on lines 70 to 78
const arr: SerializedAttribute[] = []; // to store the updated attributes
const updatedLayout = this.formRef.current?.getFieldValue('layout');
if (updatedLayout) {
const attr = values.attributes;
updatedLayout.forEach((obj: any, index: number) => {
arr[obj.y] = attr[index];
});
}
values.attributes = arr;
Copy link
Contributor

Choose a reason for hiding this comment

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

The logic for updating the attributes based on the new layout state is correct. Consider adding comments to explain the purpose of each step for better readability.

+ // Store the updated attributes based on the new layout
const arr: SerializedAttribute[] = [];
const updatedLayout = this.formRef.current?.getFieldValue('layout');
if (updatedLayout) {
    const attr = values.attributes;
    updatedLayout.forEach((obj: any, index: number) => {
        arr[obj.y] = attr[index];
    });
}
values.attributes = arr;

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
const arr: SerializedAttribute[] = []; // to store the updated attributes
const updatedLayout = this.formRef.current?.getFieldValue('layout');
if (updatedLayout) {
const attr = values.attributes;
updatedLayout.forEach((obj: any, index: number) => {
arr[obj.y] = attr[index];
});
}
values.attributes = arr;
// Store the updated attributes based on the new layout
const arr: SerializedAttribute[] = []; // to store the updated attributes
const updatedLayout = this.formRef.current?.getFieldValue('layout');
if (updatedLayout) {
const attr = values.attributes;
updatedLayout.forEach((obj: any, index: number) => {
arr[obj.y] = attr[index];
});
}
values.attributes = arr;

@KrishavRajSingh
Copy link
Contributor Author

Do I still need updated_date as I am using display_order for the sorting field??

@bsekachev
Copy link
Member

@klakhov

Any comments? Please, try to finalize the pull request this week.

@klakhov
Copy link
Contributor

klakhov commented May 20, 2024

Hi @KrishavRajSingh ,
I belive we need updated_date as its good practice. Changing the order should update the model and its updated date. As Ive said earlier, consider looking on the TimestampedModel instead of adding the field explicitly as all our models with such field use this base class.
Please, let us know when the PR is ready, we will review the UI and server part.

@bsekachev
Copy link
Member

bsekachev commented May 20, 2024

@klakhov

I belive we need updated_date as its good practice

IMHO it is worthy of a dedicated pull request if we want these fields in Attribute and Label model.
That is not related to this one.

@klakhov
Copy link
Contributor

klakhov commented May 20, 2024

Alright, lets keep it for dedicated PR.

@KrishavRajSingh, lets remove updated_date for now.

@KrishavRajSingh
Copy link
Contributor Author

Alright, lets keep it for dedicated PR.

@KrishavRajSingh, lets remove updated_date for now.

Removed it, you may review it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Outside diff range and nitpick comments (2)
cvat/apps/engine/serializers.py (2)

Line range hint 711-711: Remove unnecessary f-string.

- raise exceptions.NotFound(f'Attribute with id #{attr_id} does not exist')
+ raise exceptions.NotFound('Attribute with id #{} does not exist'.format(attr_id))

Line range hint 1146-1146: Remove unnecessary f-string.

- raise serializers.ValidationError(f'The specified project #{project_id} does not exist.')
+ raise serializers.ValidationError('The specified project #{} does not exist.'.format(project_id))
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between a72c640 and 1142ec4.
Files selected for processing (4)
  • cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
  • cvat/apps/engine/migrations/0082_remove_attributespec_updated_time.py (1 hunks)
  • cvat/apps/engine/models.py (1 hunks)
  • cvat/apps/engine/serializers.py (4 hunks)
Files skipped from review due to trivial changes (1)
  • cvat/apps/engine/migrations/0082_remove_attributespec_updated_time.py
Files skipped from review as they are similar to previous changes (2)
  • cvat-ui/src/components/labels-editor/label-form.tsx
  • cvat/apps/engine/models.py
Additional Context Used
Ruff (2)
cvat/apps/engine/serializers.py (2)

711-711: f-string without any placeholders


1146-1146: f-string without any placeholders

Additional comments not posted (4)
cvat/apps/engine/serializers.py (4)

432-432: Ensure that display_order is updated correctly during attribute updates.

This line correctly sets the display_order for each attribute during the update process, ensuring that the order is maintained as per user interaction.


438-438: Increment display_order to maintain the correct sequence.

This line correctly increments the display_order after updating each attribute, ensuring that the order is sequential and consistent.


492-492: Set display_order during attribute creation to maintain order.

This line correctly initializes the display_order for new attributes, ensuring that the order is maintained from the creation phase.


494-494: Increment display_order after creating each attribute to maintain the correct sequence.

This line correctly increments the display_order after creating each attribute, ensuring that the order is sequential and consistent.

@klakhov
Copy link
Contributor

klakhov commented May 21, 2024

@KrishavRajSingh , You need to update the shema python manage.py spectacular > cvat/schema.yml and push the changes in schema.yml so we can run the tests

@KrishavRajSingh
Copy link
Contributor Author

@KrishavRajSingh , You need to update the shema python manage.py spectacular > cvat/schema.yml and push the changes in schema.yml so we can run the tests

Done

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 1142ec4 and e492484.
Files selected for processing (1)
  • cvat/schema.yml (3 hunks)
Additional comments not posted (3)
cvat/schema.yml (3)

4-4: API version updated to reflect new features.


6333-6334: Added display_order field to schema to support attribute reordering.


6361-6362: Consistent addition of display_order across different schema sections.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Outside diff range and nitpick comments (2)
cvat/apps/engine/serializers.py (2)

Line range hint 421-450: Ensure display_order is initialized correctly to support incremental attribute additions.

- display_order = 0
+ display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0

Line range hint 725-725: Replace f-strings with regular strings when there are no placeholders.

- f'label:update Skeleton id:{db_skeleton.id} for label_id:{db_label.id}'
+ 'label:update Skeleton id:{} for label_id:{}'.format(db_skeleton.id, db_label.id)

- f'label:create Skeleton id:{db_skeleton.id} for label_id:{db_label.id}'
+ 'label:create Skeleton id:{} for label_id:{}'.format(db_skeleton.id, db_label.id)

Also applies to: 1160-1160

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between e492484 and 7d6dba5.
Files selected for processing (2)
  • cvat/apps/engine/serializers.py (4 hunks)
  • cvat/schema.yml (2 hunks)
Files skipped from review as they are similar to previous changes (1)
  • cvat/schema.yml
Additional Context Used
Ruff (2)
cvat/apps/engine/serializers.py (2)

725-725: f-string without any placeholders


1160-1160: f-string without any placeholders

@@ -235,10 +235,11 @@ class AttributeSerializer(serializers.ModelSerializer):
values = DelimitedStringListField(allow_empty=True,
child=serializers.CharField(allow_blank=True, max_length=200),
)
display_order = serializers.IntegerField(required=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a default value for display_order to ensure consistent behavior when not explicitly set.

- display_order = serializers.IntegerField(required=False)
+ display_order = serializers.IntegerField(required=False, default=0)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
display_order = serializers.IntegerField(required=False)
display_order = serializers.IntegerField(required=False, default=0)

Comment on lines +501 to +508
display_order = 0
for attr in attributes:
if attr.get('id', None):
del attr['id']

attr['display_order'] = display_order
models.AttributeSpec.objects.create(label=db_label, **attr)
display_order += 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Initialize display_order based on existing attributes to support incremental additions.

- display_order = 0
+ display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
display_order = 0
for attr in attributes:
if attr.get('id', None):
del attr['id']
attr['display_order'] = display_order
models.AttributeSpec.objects.create(label=db_label, **attr)
display_order += 1
display_order = models.AttributeSpec.objects.filter(label=db_label).aggregate(Max('display_order'))['display_order__max'] or 0
for attr in attributes:
if attr.get('id', None):
del attr['id']
attr['display_order'] = display_order
models.AttributeSpec.objects.create(label=db_label, **attr)
display_order += 1

@klakhov
Copy link
Contributor

klakhov commented May 23, 2024

Small problem, after I change the order the drag icon becomes inactive and I cant drag once again:
Peek 2024-05-23 16-35

@@ -49,7 +49,7 @@
from cvat.apps.engine.frame_provider import FrameProvider
from cvat.apps.engine.media_extractors import get_mime
from cvat.apps.engine.models import (
ClientFile, Job, JobType, Label, SegmentType, Task, Project, Issue, Data,
ClientFile, Job, JobType, Label, AttributeSpec, SegmentType, Task, Project, Issue, Data,
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove unused import.

@@ -417,6 +418,7 @@ def update_label(
except models.InvalidLabel as exc:
raise exceptions.ValidationError(str(exc)) from exc

display_order = 0
Copy link
Contributor

Choose a reason for hiding this comment

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

We can use default value in model instead of implicit 0

}

.cvat-drag-drop {
cursor: pointer
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
}

@@ -63,6 +67,16 @@ export default class LabelForm extends React.Component<Props> {
const {
label, onSubmit, onSkeletonSubmit, onCancel, resetSkeleton,
} = this.props;
const arr: SerializedAttribute[] = []; // to store the updated attributes
Copy link
Contributor

Choose a reason for hiding this comment

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

Lets use more descriptive variable name instead of comment

@@ -63,6 +67,16 @@ export default class LabelForm extends React.Component<Props> {
const {
label, onSubmit, onSkeletonSubmit, onCancel, resetSkeleton,
} = this.props;
const arr: SerializedAttribute[] = []; // to store the updated attributes
const updatedLayout = this.formRef.current?.getFieldValue('layout');
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we store layout in the form, but not in the state of the component?
We dont need to send layout to the server as its in the attributes fields

@klakhov
Copy link
Contributor

klakhov commented May 23, 2024

Also padding after attribute list became too small. Previously we had:
image

@klakhov
Copy link
Contributor

klakhov commented May 23, 2024

Can we disable vertical resizing too? Horizontal is disabled as I can see
Peek 2024-05-23 16-54

@klakhov
Copy link
Contributor

klakhov commented May 24, 2024

Also please check failing linters and tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits Files that changed from the base of the PR and between 7d6dba5 and dd35fea.
Files selected for processing (2)
  • cvat-ui/src/components/labels-editor/label-form.tsx (7 hunks)
  • cvat-ui/src/components/labels-editor/styles.scss (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • cvat-ui/src/components/labels-editor/styles.scss
Additional Context Used
Biome (20)
cvat-ui/src/components/labels-editor/label-form.tsx (20)

75-75: Unexpected any. Specify a different type.


157-157: Unexpected any. Specify a different type.


163-163: Unexpected any. Specify a different type.


163-163: Unexpected any. Specify a different type.


166-166: Unexpected any. Specify a different type.


166-166: Unexpected any. Specify a different type.


182-182: Unexpected any. Specify a different type.


196-196: Unexpected any. Specify a different type.


196-196: Unexpected any. Specify a different type.


241-241: Unexpected any. Specify a different type.


241-241: Unexpected any. Specify a different type.


246-246: Unexpected any. Specify a different type.


323-323: Unexpected any. Specify a different type.


345-345: Unexpected any. Specify a different type.


345-345: Unexpected any. Specify a different type.


349-349: Unexpected any. Specify a different type.


398-398: Unexpected any. Specify a different type.


408-408: Unexpected any. Specify a different type.


408-408: Unexpected any. Specify a different type.


426-426: Unexpected any. Specify a different type.

Additional comments not posted (4)
cvat-ui/src/components/labels-editor/label-form.tsx (4)

Line range hint 20-31: Import and setup of react-grid-layout for drag-and-drop functionality.

This setup is crucial for the drag-and-drop feature and has been correctly implemented. The WidthProvider HOC is used to manage the grid's width, which is a common practice with react-grid-layout.


465-501: Implementation of the drag-and-drop UI for attributes.

The UI components for drag-and-drop are well-structured and follow the design requirements. The use of HolderOutlined for the drag handle is consistent with the initial feedback from the review comments.


704-730: Integration of ReactGridLayout for managing attribute layout.

The integration of ReactGridLayout to manage the layout of attributes dynamically is a good use of the library. The onLayoutChange handler updates the form's layout state correctly, ensuring that the attribute order is preserved.


70-79: Handling of attribute reordering logic in the form submission.

The logic to reorder attributes based on the layout from the form is well-implemented. However, consider using a more specific type than any for the obj parameter in the loop to enhance type safety.

if (updatedLayout) {
// Updating attributes based on updatedLayout before sending it to server
const attr = values.attributes;
updatedLayout.forEach((obj: any, index: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Use a more specific type than any.

Using any can lead to potential runtime errors due to lack of type safety. Consider defining a more specific type for the obj parameter.

- updatedLayout.forEach((obj: any, index: number) => {
+ updatedLayout.forEach((obj: LayoutObj, index: number) => {

Committable suggestion was skipped due low confidence.

@KrishavRajSingh
Copy link
Contributor Author

Also please check failing linters and tests

I have fixed almost everything, but I am not able to run the tests locally. Please help me with that.
Thanks.

@klakhov
Copy link
Contributor

klakhov commented May 28, 2024

@KrishavRajSingh
To run tests locally, please refer to our docs. I suppose you need the part with E2E tests and Rest API tests. Also, please look on linter errors.

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

Successfully merging this pull request may close these issues.

Changing the order of attributes.
3 participants