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

Fix dataset downloading #7864

Merged
merged 43 commits into from
May 28, 2024
Merged

Fix dataset downloading #7864

merged 43 commits into from
May 28, 2024

Conversation

zhiltsov-max
Copy link
Contributor

@zhiltsov-max zhiltsov-max commented May 8, 2024

Motivation and context

This PR addresses several problems:

  • when requesting a dataset download, it's possible to get the 500 error with the message "The result file does not exist in export cache", which isn't expected for this request
  • when downloading the dataset the same error can be obtained if the file is requested right before the cache expiration
  • there are several TOCTOU bugs related to dataset cache file existence checks
  • under some conditions, it's possible that the export job is never started
  • the finished RQ jobs were removed automatically on result reporting (after the client requested the result). This made it hard to debug problems for admins, as the jobs were often removed already

This PR fixes the problems by the following:

  • introduced dataset cache file locking (via redis) during reading, writing, and removal
  • the 500 error is changed to automatic reexporting attempt on export status request
  • the 500 error is changed to 404 when the file is not available for downloading
  • the exported files are now have different names for each instance update time
  • the lifetime of the exported files is now automatically prolonged on each export request for the file (given the export is still valid)
  • the deferred export jobs are now checked to have ghost dependencies. If so, the requested job is restarted
  • added several environment variables for configuration
  • finished RQ export jobs are not removed automatically on result retrieval. Now, they just use the export cache lifetime instead (should be continued in another PR)

How has this been tested?

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

    • Improved reliability of file handling during export and cleanup processes.
    • Introduced new functionality for managing export cache locks and directories.
  • Bug Fixes

    • Addressed race conditions in concurrent export and cleanup operations.
  • Dependencies

    • Updated multiple packages to their latest versions for enhanced security and performance:
      • cryptography to 42.0.7
      • django to 4.2.13
      • django-health-check to 3.18.2
      • freezegun to 1.5.1
      • jinja2 to 3.1.4
      • limits to 3.12.0
      • lxml to 5.2.2
      • orjson to 3.10.3
      • Added pottery version 3.0.0
      • Updated tqdm to 4.66.4

- prolong export cache lifetime on cache requests
- change 500 to 404 on missing cache file during downloading
- remove rq job removal after result retrieval
- make export filenames more distinct
- fix possible infinite deferring of dependent rq jobs
@zhiltsov-max zhiltsov-max marked this pull request as draft May 8, 2024 10:20
Copy link
Contributor

coderabbitai bot commented May 8, 2024

Walkthrough

The changes introduce new functionalities for handling concurrent export and cleanup processes in the CVAT application, alongside updates to package dependencies. Key additions include new classes and methods for multiprocessing, lock management, and export file handling. These improvements aim to enhance the reliability and efficiency of export operations. Additionally, multiple package versions have been upgraded to maintain compatibility and security.

Changes

Files/Paths Change Summary
cvat/apps/dataset_manager/tests/... Added classes and methods for testing concurrent export and cleanup processes, focusing on multiprocessing and locks.
cvat/apps/dataset_manager/util.py Introduced new functions and classes for managing export cache locks, directories, filenames, and parsing file paths.
cvat/requirements/base.txt Updated versions of several packages and added a new dependency on pottery==3.0.0.
utils/dataset_manifest/requirements.txt Updated the version of the tqdm package from 4.66.2 to 4.66.4.

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant User
    participant CVATApp
    participant ExportManager
    participant LockManager
    participant FileHandler

    User->>CVATApp: Initiate export
    CVATApp->>ExportManager: Start export process
    ExportManager->>LockManager: Acquire lock
    LockManager-->>ExportManager: Lock acquired
    ExportManager->>FileHandler: Export data
    FileHandler-->>ExportManager: Data exported
    ExportManager->>LockManager: Release lock
    LockManager-->>ExportManager: Lock released
    ExportManager-->>CVATApp: Export completed
    CVATApp-->>User: Export successful

In fields of code, where functions grow,
New features bloom, enhancements flow.
With locks and threads, they work in sync,
To make our exports quick as a wink.
Dependencies updated, all in line,
Our CVAT shines, simply divine.
— 🐇 CodeRabbit


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.
    • @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 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 configuration to show the current CodeRabbit configuration for the repository.
  • @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: 0

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

Line range hint 44-44: Remove unused import to clean up the code.

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

Line range hint 1966-1966: Remove unnecessary f-string as it does not contain any placeholders.

-    server_address = request.scheme + '://'
+    server_address = str(request.scheme) + '://'

Line range hint 2713-2713: Remove unnecessary f-string as it does not contain any placeholders.

-    server_address += request.get_host()
+    server_address = server_address + str(request.get_host())
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 86c5a77 and 4c58bf5.
Files selected for processing (10)
  • cvat/apps/dataset_manager/apps.py (1 hunks)
  • cvat/apps/dataset_manager/default_settings.py (1 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
  • cvat/apps/dataset_manager/views.py (4 hunks)
  • cvat/apps/engine/views.py (4 hunks)
  • cvat/requirements/base.in (1 hunks)
  • cvat/requirements/base.txt (10 hunks)
  • cvat/requirements/development.txt (3 hunks)
  • cvat/requirements/production.txt (1 hunks)
  • utils/dataset_manifest/requirements.txt (2 hunks)
Files skipped from review due to trivial changes (5)
  • cvat/apps/dataset_manager/apps.py
  • cvat/apps/dataset_manager/default_settings.py
  • cvat/requirements/base.in
  • cvat/requirements/development.txt
  • utils/dataset_manifest/requirements.txt
Additional Context Used
Ruff (4)
cvat/apps/dataset_manager/views.py (1)

27-27: .util.LockMode imported but unused

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

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


1966-1966: f-string without any placeholders


2713-2713: f-string without any placeholders

Additional comments not posted (6)
cvat/requirements/production.txt (1)

13-13: Update of exceptiongroup to 1.2.1 appears safe and beneficial.

cvat/apps/dataset_manager/util.py (1)

93-146: Introduced a robust locking mechanism using Redlock. Ensure thorough testing, especially for edge cases like rapid lock acquisition and release.

cvat/requirements/base.txt (1)

Line range hint 53-325: Updates to package versions in base.txt are minor and likely include improvements or security patches. Verify compatibility with existing codebase.

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

Line range hint 27-149: Integration of new locking mechanism and error handling in dataset management views appears well-implemented. Recommend additional testing for robustness.

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

2962-2962: Ensure that the new caching logic in _export_annotations is correctly implemented and tested.


3022-3022: Confirm that the error handling in export_annotations covers all potential failure scenarios effectively.

Verification successful

The output from the revised script provides a comprehensive list of exception handling within the export_annotations method and related areas in the cvat/apps/engine/views.py file. The exceptions handled include ValidationError, PermissionDenied, NotFound, AttributeError, IntegrityError, APIException, ValueError, CloudStorageModel.DoesNotExist, and general Exception. This indicates a robust error handling mechanism that covers a wide range of potential issues that could arise during the execution of the export_annotations method.

Given this information, it appears that the error handling within the export_annotations method is indeed comprehensive, covering various specific and general exceptions that could occur. This aligns with the initial review comment's concern about ensuring that all potential failure scenarios are effectively covered.

Conclusion:
The error handling in the export_annotations method appears to be comprehensive and well-implemented, addressing a variety of error conditions that could potentially disrupt the functionality.

  • The method handles specific exceptions like ValidationError, PermissionDenied, and NotFound, which are crucial for robust API behavior.
  • It also covers database-related errors through AttributeError and IntegrityError.
  • General exceptions and unexpected errors are caught by broad Exception handling.
  • Special cases like CloudStorageModel.DoesNotExist are also considered, which is important for operations involving external storage.

This thorough approach ensures that the method can gracefully handle errors and provide appropriate responses, thereby enhancing the reliability and user experience of the dataset exporting functionalities.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Check all branches of error handling in the `export_annotations` method to ensure comprehensive coverage.

# Test: Search for error handling branches in the method. Expect: Each branch handles a specific error scenario appropriately.
rg --type python $'export_annotations'

Length of output: 68



Script:

#!/bin/bash
# Description: Re-check all branches of error handling in the `export_annotations` method to ensure comprehensive coverage.

# Test: Search for error handling branches in the method. Expect: Each branch handles a specific error scenario appropriately.
rg 'export_annotations' --files-with-matches | xargs rg 'except'

Length of output: 3895

@zhiltsov-max
Copy link
Contributor Author

/check

Copy link
Contributor

github-actions bot commented May 8, 2024

❌ Some checks failed
📄 See logs here

Copy link
Contributor

github-actions bot commented May 8, 2024

❌ Some checks failed
📄 See logs here

@zhiltsov-max zhiltsov-max marked this pull request as ready for review May 9, 2024 10:44
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 (6)
cvat/apps/dataset_manager/util.py (1)

Line range hint 16-41: Consider adding more detailed docstrings for the utility functions to improve maintainability and clarity for other developers.

+    """
+    Creates a zip archive from a directory.
+
+    Args:
+        src_path (str): The source directory path.
+        dst_path (str): The destination zip file path.
+    """
tests/python/rest_api/test_resource_import_export.py (2)

Line range hint 32-57: Comprehensive test cases for exporting resources to cloud storage. Consider adding comments to explain the test scenarios in detail for better maintainability.

+    """
+    Test saving resources to cloud storage with specific configurations.
+    This test covers various object types and ensures that the export functionality
+    respects the specified cloud storage locations and settings.
+    """

Line range hint 182-207: Import functionality tests are well-covered. Ensure that edge cases, such as network failures or partial data availability, are also tested.

Would you like me to help by adding some tests for edge cases involving network failures or partial data availability?

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

Line range hint 44-44: Remove unused import to clean up the code.

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

Line range hint 1967-1967: The f-string used here does not contain any placeholders, which is unnecessary. Consider using a regular string if no formatting is needed.

- server_address = request.scheme + '://'
+ server_address = str(request.scheme) + '://'

Line range hint 2714-2714: Similar to the previous comment, an f-string is used here without placeholders. Use a regular string instead.

- filename_pattern = build_annotations_file_name(
+ filename_pattern = str(build_annotations_file_name(
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 4c58bf5 and 05f6dd1.
Files selected for processing (11)
  • changelog.d/20240508_144202_mzhiltso_fix_dataset_downloading.md (1 hunks)
  • cvat/apps/dataset_manager/default_settings.py (1 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
  • cvat/apps/dataset_manager/views.py (4 hunks)
  • cvat/apps/engine/views.py (4 hunks)
  • cvat/requirements/base.in (1 hunks)
  • cvat/requirements/base.txt (10 hunks)
  • cvat/requirements/development.txt (1 hunks)
  • cvat/requirements/testing.txt (1 hunks)
  • tests/python/rest_api/test_resource_import_export.py (5 hunks)
  • utils/dataset_manifest/requirements.txt (2 hunks)
Files skipped from review due to trivial changes (1)
  • cvat/requirements/testing.txt
Files skipped from review as they are similar to previous changes (4)
  • cvat/apps/dataset_manager/default_settings.py
  • cvat/requirements/base.in
  • cvat/requirements/base.txt
  • cvat/requirements/development.txt
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 (5)
changelog.d/20240508_144202_mzhiltso_fix_dataset_downloading.md (1)

1-5: Changelog entry is clear and properly linked to the PR.

utils/dataset_manifest/requirements.txt (2)

8-9: Ensure that non-binary flags for lxml and xmlsec are required for your project's needs.

Verification successful

The comprehensive search across the codebase confirms the consistent use of --no-binary flags for lxml and xmlsec across various requirement files and Dockerfiles. This indicates a deliberate and justified choice to compile these libraries from source, likely for compatibility or optimization reasons specific to the project's needs. The presence of related development libraries in Dockerfiles further supports this requirement.

  • utils/dataset_manifest/requirements.txt: Both lxml and xmlsec are set to compile from source.
  • cvat/requirements/: Multiple environment-specific files (base, testing, development, production) also enforce non-binary installation.
  • Dockerfile: Includes libraries like libxmlsec1-dev and libxmlsec1-openssl, which are related to xmlsec, suggesting system-level dependencies that might require source compilation.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify compatibility of updated package versions.

# Test: Search for the package usage in the Python files. Expect: No deprecated functions used.
rg --type py 'lxml|xmlsec'

Length of output: 28



Script:

#!/bin/bash
# Description: Comprehensive search for any references to 'lxml' or 'xmlsec' in the entire codebase.

# Search all files for 'lxml' or 'xmlsec' to ensure no usage is missed.
rg 'lxml|xmlsec'

Length of output: 1026


21-21: Update to tqdm==4.66.4 looks good. Ensure it's tested for compatibility with your project's usage.

cvat/apps/dataset_manager/util.py (1)

104-141: Locking mechanism using Redlock is implemented correctly. Ensure thorough testing, especially for edge cases in concurrent environments.

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

179-227: Robust error handling in clear_export_cache. Ensure that all possible exceptions are considered and handled appropriately.

cvat/apps/dataset_manager/views.py Outdated Show resolved Hide resolved
cvat/requirements/base.in Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/apps.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Outdated Show resolved Hide resolved
@zhiltsov-max zhiltsov-max marked this pull request as draft May 14, 2024 08:25
@zhiltsov-max zhiltsov-max marked this pull request as ready for review May 16, 2024 16:58
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 (1)
cvat/apps/engine/views.py (1)

Line range hint 44-44: Remove unused import.

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

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 05f6dd1 and 228e82e.
Files selected for processing (12)
  • cvat/apps/dataset_manager/default_settings.py (1 hunks)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
  • cvat/apps/dataset_manager/views.py (3 hunks)
  • cvat/apps/engine/tests/test_rest_api_3D.py (3 hunks)
  • cvat/apps/engine/tests/utils.py (4 hunks)
  • cvat/apps/engine/views.py (4 hunks)
  • cvat/requirements/base.in (1 hunks)
  • cvat/requirements/base.txt (13 hunks)
  • cvat/requirements/development.txt (3 hunks)
  • cvat/requirements/testing.in (1 hunks)
  • cvat/requirements/testing.txt (1 hunks)
Files not reviewed due to errors (1)
  • cvat/apps/dataset_manager/util.py (no review received)
Files skipped from review due to trivial changes (5)
  • cvat/apps/dataset_manager/default_settings.py
  • cvat/apps/engine/tests/test_rest_api_3D.py
  • cvat/requirements/base.in
  • cvat/requirements/development.txt
  • cvat/requirements/testing.in
Additional Context Used
Ruff (4)
cvat/apps/dataset_manager/views.py (1)

29-29: .util.EXPORT_CACHE_DIR_NAME imported but unused

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 (33)
cvat/requirements/testing.txt (3)

9-10: Ensure consistency of --no-binary options across all requirements files.


14-14: LGTM! The update to fakeredis[lua] ensures compatibility with Lua scripts.


16-16: LGTM! The addition of lupa is appropriate for Lua scripting support.

cvat/apps/engine/tests/utils.py (2)

51-91: LGTM! The clear_rq_jobs function is well-structured and includes necessary error handling.


95-96: LGTM! The _clear_rq_jobs method improves test isolation and reliability.

cvat/requirements/base.txt (5)

9-10: Ensure consistency of --no-binary options across all requirements files.


56-56: LGTM! The update to cryptography includes important security patches and bug fixes.


74-74: LGTM! The update to django includes important security patches and bug fixes.


105-105: LGTM! The update to django-health-check includes important security patches and bug fixes.


189-190: LGTM! The addition of mmh3 and pottery is appropriate for the new locking mechanisms.

Also applies to: 215-216

cvat/apps/dataset_manager/views.py (12)

55-60: LGTM! The function correctly returns the TTL for different database instances.


60-77: LGTM! The initial setup for logging and determining the database instance is correct.


77-79: LGTM! The cache TTL and directory setup is correct.


85-95: LGTM! The export filename creation and directory preparation are correct.


97-127: LGTM! The locking mechanism and export process are correctly implemented.


130-137: LGTM! The exception handling is correctly implemented.


162-164: LGTM! The initial setup and backward compatibility comment are correct.


166-173: LGTM! The locking mechanism and file existence check are correctly implemented.


175-193: LGTM! The cache TTL check and file removal process are correctly implemented.


200-201: LGTM! The function correctly returns a list of export formats.


Line range hint 203-204: LGTM! The function correctly returns a list of import formats.


Line range hint 206-210: LGTM! The function correctly returns a dictionary of importers and exporters.

cvat/apps/dataset_manager/tests/test_rest_api_formats.py (8)

11-11: New imports are necessary for concurrency handling and test enhancements.

Also applies to: 17-19, 22-23, 25-25


97-97: Inheriting _DbTestBase from ApiTestBase ensures consistency and consolidates the test base classes.


419-420: Adding _clear_rq_jobs ensures a clean test environment and avoids interference from previous tests.


526-527: Adding _clear_rq_jobs ensures a clean test environment and avoids interference from previous tests.


613-614: Adding _clear_rq_jobs ensures a clean test environment and avoids interference from previous tests.


1265-1590: The ExportBehaviorTest class and its methods are well-structured and necessary for testing concurrency handling in export operations.


2058-2059: Adding _clear_rq_jobs ensures a clean test environment and avoids interference from previous tests.


2140-2141: Adding _clear_rq_jobs ensures a clean test environment and avoids interference from previous tests.

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

2963-2964: LGTM! Ensure the logic for handling cache TTL and instance update time is correct.

Also applies to: 2967-2967, 2969-2969

Verification successful

The _export_annotations function in cvat/apps/engine/views.py correctly handles the cache TTL and instance update time. The function retrieves the cache TTL using dm.views.get_export_cache_ttl(db_instance) and the instance update time using timezone.localtime(db_instance.updated_date). If the db_instance is a Project, it updates the instance_update_time to the maximum update time of its tasks.

  • The cache TTL is correctly retrieved and used in the function.
  • The instance update time is correctly retrieved and updated if necessary.

Therefore, the logic for handling cache TTL and instance update time is correct.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the logic for handling cache TTL and instance update time.

# Test: Search for the function usage. Expect: Correct handling of cache TTL and instance update time.
rg --type python $'_export_annotations'

Length of output: 69



Script:

#!/bin/bash
# Description: Verify the logic for handling cache TTL and instance update time.

# Test: Search for the function usage. Expect: Correct handling of cache TTL and instance update time.
rg --type py '_export_annotations'

Length of output: 1280



Script:

#!/bin/bash
# Description: Extract the implementation of `_export_annotations` function from `cvat/apps/engine/views.py`.

# Extract the function implementation.
ast-grep --lang python --pattern $'def _export_annotations($_, $_, $$$):\n    $$$' cvat/apps/engine/views.py

Length of output: 13779


3034-3035: LGTM! Ensure the logic for handling cloud storage and temporary files is correct.

Also applies to: 3036-3036, 3037-3037, 3038-3038, 3039-3039, 3040-3040, 3041-3041, 3042-3042, 3043-3043, 3044-3044, 3045-3045, 3046-3046, 3047-3047, 3048-3048, 3049-3049


3078-3078: LGTM! Ensure the logic for handling cloud storage and temporary files is correct.

Also applies to: 3093-3094

Verification successful

The _import_project_dataset function and its usages have been extracted for review. Here are the relevant code segments:

Function Definition

def _import_project_dataset(request, rq_id_template, rq_func, db_obj, format_name, filename=None, conv_mask_to_poly=True, location_conf=None):
    format_desc = {f.DISPLAY_NAME: f
        for f in dm.views.get_import_formats()}.get(format_name)
    if format_desc is None:
        raise serializers.ValidationError(
            "Unknown input format '{}'".format(format_name))
    elif not format_desc.ENABLED:
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

    rq_id = rq_id_template.format(db_obj.pk, request.user)

Function Usages

  1. Usage in import_annotations method:

    if request.method in {'POST', 'OPTIONS'}:
        return self.import_annotations(
            request=request,
            db_obj=self._object,
            import_func=_import_project_dataset,
            rq_func=dm.project.import_dataset_as_project,
            rq_id_template=self.IMPORT_RQ_ID_TEMPLATE
        )
  2. Usage in another context:

    conv_mask_to_poly = to_bool(request.query_params.get('conv_mask_to_poly', True))
    tmp_dir = self._object.get_tmp_dirname()
    uploaded_file = None
    if os.path.isfile(os.path.join(tmp_dir, filename)):
        uploaded_file = os.path.join(tmp_dir, filename)
    return _import_project_dataset(
        request=request,
        filename=uploaded_file,
        rq_id_template=self.IMPORT_RQ_ID_TEMPLATE,
        rq_func=dm.project.import_dataset_as_project,
        db_obj=self._object,
    )

Analysis

  • The function _import_project_dataset includes logic to handle different dataset formats and raises appropriate errors for unknown or disabled formats.
  • The usages of the function correctly pass the necessary parameters, including handling temporary files (uploaded_file) and cloud storage configurations.

Based on the extracted code segments and the analysis, the logic for handling cloud storage and temporary files appears to be correctly implemented.

Final Response

The logic for handling cloud storage and temporary files in the _import_project_dataset function and its usages is correctly implemented.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the logic for handling cloud storage and temporary files.

# Test: Search for the function usage. Expect: Correct handling of cloud storage and temporary files.
rg --type python $'_import_project_dataset'

Length of output: 73



Script:

#!/bin/bash
# Description: Verify the logic for handling cloud storage and temporary files.

# Test: Search for the function usage. Expect: Correct handling of cloud storage and temporary files.
rg --type py '_import_project_dataset'

Length of output: 356



Script:

#!/bin/bash
# Description: Extract the definition and usages of `_import_project_dataset` function for detailed review.

# Extract the function definition
rg --type py --context 10 'def _import_project_dataset' cvat/apps/engine/views.py

# Extract the usages of the function
rg --type py --context 5 '_import_project_dataset' cvat/apps/engine/views.py

Length of output: 2662

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 228e82e and 447b4df.
Files selected for processing (1)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
Additional comments not posted (6)
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (6)

11-23: New imports for concurrency handling and utility functions.


97-97: New class _DbTestBase for database-related test setup and utility functions.


1265-1265: New class ExportBehaviorTest for testing concurrent export and cleanup operations.


1368-1590: New test method test_concurrent_export_and_cleanup for testing concurrent export and cleanup operations.


1591-1787: New test method test_concurrent_download_and_cleanup for testing concurrent download and cleanup operations.


1789-1811: New test method test_export_can_create_file_and_cleanup_job for testing export file creation and cleanup job scheduling.

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 (3)
cvat/apps/engine/views.py (3)

Line range hint 44-44: Remove unused import to clean up the code.

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

Line range hint 1967-1967: The f-string is used without placeholders, which is unnecessary. Convert it to a regular string.

-    instance_timestamp = datetime.strftime(instance_update_time, "%Y_%m_%d_%H_%M_%S")
+    instance_timestamp = datetime.strftime(instance_update_time, "%Y_%m_%d_%H_%M_%S")

Line range hint 2714-2714: The f-string is used without placeholders, which is unnecessary. Convert it to a regular string.

-    instance_timestamp = datetime.strftime(instance_update_time, "%Y_%m_%d_%H_%M_%S")
+    instance_timestamp = datetime.strftime(instance_update_time, "%Y_%m_%d_%H_%M_%S")
Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between 447b4df and de2c805.
Files selected for processing (1)
  • cvat/apps/engine/views.py (5 hunks)
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 (4)
cvat/apps/engine/views.py (4)

2963-2975: Ensure proper handling of the instance_update_time for projects with multiple tasks. The logic to determine the latest update time across all tasks and the project itself is sound.


Line range hint 2976-3055: The logic to handle outdated exports and re-enqueue jobs is complex but correctly implements the requirements. It checks if the existing job's data is outdated compared to the instance update time and handles it accordingly. This includes canceling outdated jobs and re-enqueuing them if necessary.


3021-3033: The logic to handle file existence and re-enqueue jobs if the file does not exist is correctly implemented. It ensures that files are re-exported if they are not found, which is crucial for maintaining data integrity.


3039-3055: Properly handle failed jobs by providing detailed error information to the client. This is crucial for debugging and understanding why a job failed.

cvat/apps/engine/views.py Show resolved Hide resolved
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: 6

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between de2c805 and e76382d.
Files selected for processing (1)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
Additional comments not posted (2)
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (2)

17-17: Ensure proper usage of context managers to handle resources efficiently.

Verification successful

Ensure proper usage of context managers to handle resources efficiently

The usage of ExitStack and contextmanager in cvat/apps/dataset_manager/tests/test_rest_api_formats.py appears to be correct and follows proper resource management practices. Here are the key points:

  • @contextmanager Decorators: These are used to define context managers for handling processes and locks, ensuring that resources are properly managed and released.
  • ExitStack: This is used to manage multiple context managers, ensuring that all resources are properly cleaned up even if an error occurs.

The code snippets provided show that these constructs are used appropriately to handle resources efficiently.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the usage of ExitStack and contextmanager in the file to ensure proper resource management.

# Test: Search for ExitStack and contextmanager usage. Expect: Proper usage patterns.
rg --type python $'ExitStack|contextmanager' cvat/apps/dataset_manager/tests/test_rest_api_formats.py

Length of output: 133



Script:

#!/bin/bash
# Description: Verify the usage of ExitStack and contextmanager in the file to ensure proper resource management.

# Test: Search for ExitStack and contextmanager usage. Expect: Proper usage patterns.
rg 'ExitStack|contextmanager' cvat/apps/dataset_manager/tests/test_rest_api_formats.py

Length of output: 269



Script:

#!/bin/bash
# Description: Extract surrounding lines of ExitStack and contextmanager usage for detailed verification.

# Extract 5 lines before and after each match to understand the context.
rg -C 5 'ExitStack|contextmanager' cvat/apps/dataset_manager/tests/test_rest_api_formats.py

Length of output: 2772


1792-1792: Ensure proper handling of file usage conflicts during cleanup operations.

Verification successful

Ensure proper handling of file usage conflicts during cleanup operations

The search results indicate that LockNotAvailableError and FileIsBeingUsedError are indeed referenced in the test file cvat/apps/dataset_manager/tests/test_rest_api_formats.py. This suggests that there are mechanisms in place to handle file usage conflicts during cleanup operations.

  • LockNotAvailableError is imported and used multiple times.
  • FileIsBeingUsedError is also imported and used in assertions.

These references confirm that the code includes error handling for file usage conflicts.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that file usage conflicts are handled correctly in cleanup operations.

# Test: Search for conflict handling in cleanup operations. Expect: Proper error handling mechanisms.
rg --type python $'LockNotAvailableError|FileIsBeingUsedError' cvat/apps/dataset_manager/tests/test_rest_api_formats.py

Length of output: 151



Script:

#!/bin/bash
# Description: Verify that file usage conflicts are handled correctly in cleanup operations.

# Test: Search for conflict handling in cleanup operations. Expect: Proper error handling mechanisms.
rg --type py $'LockNotAvailableError|FileIsBeingUsedError' cvat/apps/dataset_manager/tests/test_rest_api_formats.py

Length of output: 1084

@codecov-commenter
Copy link

codecov-commenter commented May 17, 2024

Codecov Report

Attention: Patch coverage is 88.57143% with 20 lines in your changes are missing coverage. Please review.

Project coverage is 83.37%. Comparing base (852b396) to head (297d686).
Report is 20 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #7864      +/-   ##
===========================================
+ Coverage    83.35%   83.37%   +0.02%     
===========================================
  Files          380      383       +3     
  Lines        40032    40171     +139     
  Branches      3766     3769       +3     
===========================================
+ Hits         33367    33492     +125     
- Misses        6665     6679      +14     
Components Coverage Δ
cvat-ui 79.22% <ø> (-0.01%) ⬇️
cvat-server 87.17% <86.66%> (+0.02%) ⬆️

cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
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: 10

Outside diff range and nitpick comments (1)
cvat/apps/dataset_manager/views.py (1)

99-129: Add detailed logging for export operations.

Consider adding more detailed logging in the export function to provide better visibility into the export process. This could include logging the start and end of the export operation, any errors encountered, and the final status of the export. This would help in debugging and monitoring the export operations.

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between e76382d and aafc369.
Files selected for processing (3)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
  • cvat/apps/dataset_manager/views.py (3 hunks)
Additional Context Used
Ruff (1)
cvat/apps/dataset_manager/views.py (1)

29-29: .util.EXPORT_CACHE_DIR_NAME imported but unused

Learnings (1)
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (1)
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:1266-1266
Timestamp: 2024-05-17T12:20:07.528Z
Learning: The CVAT project prefers using the `attrs` module over `dataclasses` due to its additional features and is commonly used across the project.
Additional comments not posted (4)
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (4)

1266-1266: Use Python's dataclasses module for simplicity and efficiency.

- @define
+ from dataclasses import dataclass

- @dataclass
class SharedBase:

Likely invalid or redundant comment.


1265-1265: Enhance the ExportBehaviorTest class with additional comments for better understanding.

+ # This class tests the behavior of concurrent export and cleanup operations.
class ExportBehaviorTest(_DbTestBase):

Likely invalid or redundant comment.


419-419: Optimize the cleanup of RQ jobs to enhance readability and understanding of its functionality.

- self._clear_rq_jobs() # clean up from previous tests and iterations
+ self.clearResourceQueueJobs() # Clear resource queue jobs from previous tests and iterations

Likely invalid or redundant comment.


97-97: Consider renaming _DbTestBase to DatabaseTestBase for improved readability and adherence to Python naming conventions.

- class _DbTestBase(ApiTestBase):
+ class DatabaseTestBase(ApiTestBase):

Likely invalid or redundant comment.

cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Outdated Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Show resolved Hide resolved
cvat/apps/dataset_manager/views.py Show resolved Hide resolved
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

Outside diff range and nitpick comments (1)
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (1)

421-421: Repeated calls to _clear_rq_jobs can be optimized.

Consider refactoring the repeated calls to _clear_rq_jobs across different test methods into a setup or teardown method to reduce redundancy and improve maintainability.

Also applies to: 528-528, 615-615, 865-865, 2071-2071, 2153-2153

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between aafc369 and 275a274.
Files selected for processing (2)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
Additional Context Used
Learnings (2)
cvat/apps/dataset_manager/util.py (1)
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/util.py:149-157
Timestamp: 2024-05-27T14:31:27.282Z
Learning: zhiltsov-max prefers error messages in the `get_export_cache_dir` function to remain less detailed to facilitate manual investigation.
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (2)
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:2061-2061
Timestamp: 2024-05-27T14:34:32.440Z
Learning: The project uses a specific naming convention for functions and methods, which should be adhered to in suggestions and comments.
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:1266-1266
Timestamp: 2024-05-17T12:20:07.528Z
Learning: The CVAT project prefers using the `attrs` module over `dataclasses` due to its additional features and is commonly used across the project.
Additional comments not posted (14)
cvat/apps/dataset_manager/util.py (2)

149-157: Review error handling in get_export_cache_dir.

The error handling in get_export_cache_dir is appropriate as it raises a FileNotFoundError when the base directory does not exist. This is crucial for debugging and ensuring that the file operations are performed on valid directories.


106-144: Review the use of locking mechanism.

The implementation of get_export_cache_lock uses Redlock for distributed locking, which is generally a robust choice. However, consider the following:

  1. Verify that the auto_release_time set by ttl is appropriate for the operations being locked.
  2. Ensure that the lock acquisition logic (block and acquire_timeout) is tested under high concurrency to prevent deadlocks or long wait times.
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (12)

1267-1267: The ExportBehaviorTest class effectively handles concurrency tests.

This class is well-structured to test various concurrency issues related to dataset export and cleanup.


1268-1268: Usage of attrs over dataclasses is consistent with project standards.

Good adherence to project conventions by using attrs for defining data classes.


1272-1272: Inheritance from SharedBase is correctly implemented in SharedBool and SharedString.

The inheritance hierarchy is clear and correctly utilizes shared functionality from SharedBase.

Also applies to: 1284-1284


1298-1298: Custom exception _LockTimeoutError is well-defined for specific timeout errors.

This custom exception helps in handling specific timeout scenarios clearly within the tests.


1301-1301: Method overloading for set_condition is correctly used to handle different types.

The use of method overloading here allows for flexible setting of conditions based on variable type, enhancing code robustness.

Also applies to: 1305-1305


1326-1326: Utility functions side_effect and chain_side_effects are well-implemented for test flexibility.

These functions provide a robust way to handle side effects in a controlled manner during testing, which is crucial for accurate concurrency testing.

Also applies to: 1339-1339


1358-1358: The process_closing context manager is a critical addition for safely handling process termination.

This method ensures that processes are properly closed after tests, preventing potential resource leaks.


1370-1370: The test_concurrent_export_and_cleanup method is comprehensive in testing race conditions.

This method effectively simulates and tests potential race conditions between export and cleanup operations, ensuring robustness in concurrent environments.


1593-1593: The test_concurrent_download_and_cleanup method thoroughly tests file handling under concurrency.

This test method is well-designed to ensure that file download and cleanup operations handle concurrency correctly without data loss or corruption.


1794-1794: The test_export_can_create_file_and_cleanup_job method correctly tests the export functionality under normal and edge conditions.

This method ensures that the export functionality is robust, handling both file creation and subsequent cleanup effectively.


1881-1881: The test_cleanup_can_remove_file method effectively tests the cleanup functionality of the export system.

This method ensures that files are properly removed after their intended lifecycle, preventing unnecessary storage usage.


99-99: Consider renaming _DbTestBase to DatabaseTestBase for improved readability.

- class _DbTestBase(ApiTestBase):
+ class DatabaseTestBase(ApiTestBase):

Likely invalid or redundant comment.

cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
cvat/apps/dataset_manager/util.py Show resolved Hide resolved
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

Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between aafc369 and 297d686.
Files selected for processing (5)
  • cvat/apps/dataset_manager/tests/test_rest_api_formats.py (9 hunks)
  • cvat/apps/dataset_manager/util.py (2 hunks)
  • cvat/requirements/base.txt (13 hunks)
  • cvat/requirements/development.txt (3 hunks)
  • cvat/requirements/testing.txt (1 hunks)
Files skipped from review due to trivial changes (1)
  • cvat/requirements/development.txt
Additional Context Used
Learnings (2)
cvat/apps/dataset_manager/util.py (1)
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/util.py:149-157
Timestamp: 2024-05-27T14:31:27.282Z
Learning: zhiltsov-max prefers error messages in the `get_export_cache_dir` function to remain less detailed to facilitate manual investigation.
cvat/apps/dataset_manager/tests/test_rest_api_formats.py (2)
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:2061-2061
Timestamp: 2024-05-27T14:34:32.440Z
Learning: The project uses a specific naming convention for functions and methods, which should be adhered to in suggestions and comments.
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:1266-1266
Timestamp: 2024-05-17T12:20:07.528Z
Learning: The CVAT project prefers using the `attrs` module over `dataclasses` due to its additional features and is commonly used across the project.
Additional comments not posted (13)
cvat/requirements/testing.txt (3)

9-9: Updated coverage tool version to 7.2.3.


12-14: Updated fakeredis and its dependency lupa for testing environments.


14-14: Updated sortedcontainers to maintain compatibility with the new fakeredis version.

cvat/requirements/base.txt (3)

72-72: Updated Django to 4.2.13, ensuring the application is up-to-date with the latest security patches and bug fixes.


103-103: Updated django-health-check to 3.18.2 for improved health checking capabilities.


213-214: Added new dependency, pottery, for enhanced Redis functionalities, likely related to the new locking mechanisms.

cvat/apps/dataset_manager/util.py (3)

105-144: The implementation of Redis locking using the Redlock algorithm in get_export_cache_lock is correctly handled. Good use of context management and error handling.


149-157: The function get_export_cache_dir correctly checks for the existence of the directory and raises an appropriate error if not found. This aligns with your preference for less detailed error messages to facilitate manual investigation.


190-231: The function parse_export_file_path effectively uses regular expressions to parse the export file path and extract necessary metadata, with robust error handling for parsing failures.

cvat/apps/dataset_manager/tests/test_rest_api_formats.py (4)

1268-1268: Using attrs for defining data classes is consistent with project standards. Good use of the library's features for multiprocessing compatibility.


1794-1794: The method test_concurrent_download_and_cleanup effectively tests TOCTOU issues with file handling. It's a critical test that ensures the robustness of file operations under concurrency.


99-99: Consider renaming _DbTestBase to DatabaseTestBase for better readability and adherence to Python naming conventions.

- class _DbTestBase(ApiTestBase):
+ class DatabaseTestBase(ApiTestBase):
Skipped due to learnings
User: zhiltsov-max
PR: cvat-ai/cvat#7864
File: cvat/apps/dataset_manager/tests/test_rest_api_formats.py:2061-2061
Timestamp: 2024-05-27T14:34:32.440Z
Learning: The project uses a specific naming convention for functions and methods, which should be adhered to in suggestions and comments.

2024-2024: The ProjectDumpUpload class tests are comprehensive and cover a wide range of scenarios. Ensure that all new formats added to the system are included in these tests to maintain coverage.

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 297d686 and 6fc3f63.
Files selected for processing (1)
  • utils/dataset_manifest/requirements.txt (2 hunks)
Files skipped from review due to trivial changes (1)
  • utils/dataset_manifest/requirements.txt

@zhiltsov-max zhiltsov-max merged commit f883838 into develop May 28, 2024
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants