superannotate 4.4.30b3__tar.gz → 4.4.31.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {superannotate-4.4.30b3/src/superannotate.egg-info → superannotate-4.4.31.dev2}/PKG-INFO +1 -1
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/__init__.py +1 -1
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk_interface.py +64 -17
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/serviceproviders.py +2 -0
- superannotate-4.4.31.dev2/src/superannotate/lib/core/usecases/integrations.py +179 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/controller.py +9 -1
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/annotation.py +32 -14
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/integration.py +5 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/stream_data_handler.py +19 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/utils.py +72 -2
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2/src/superannotate.egg-info}/PKG-INFO +1 -1
- superannotate-4.4.30b3/src/superannotate/lib/core/usecases/integrations.py +0 -78
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/LICENSE +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/MANIFEST.in +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/README.rst +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/requirements.txt +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/setup.cfg +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/setup.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/aggregators.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/common.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/bin/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/bin/superannotate.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/common.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/exceptions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/helpers.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/conversion.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/baseStrategy.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_api.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_pixel.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_pixel_to_coco.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/sa_vector_to_coco.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/converters.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_pixel.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_pixel.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_pixel.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_helper.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_pixel.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vott_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/yolo_converters/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_strategies.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_to_sa_vector.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/import_to_sa_conversions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/sa_conversion.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/base_interface.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/cli_interface.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/folders.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/project.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/types.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/serializers.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/base_usecases.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/conditions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/config.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/base.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/classes.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/filters.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/folder.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/integrations.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/items.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/project.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/project_entities.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/work_managament.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/enums.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/exceptions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/jsx_conditions.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/plugin.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/pydantic_v1.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/reporter.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/repositories.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/response.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/service_types.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/types.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/annotations.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/base.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/classes.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/custom_fields.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/folders.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/images.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/items.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/models.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/projects.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/utils.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/video_convertor.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/annotation_adapter.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/custom_entities.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/helpers.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/query_builder.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/repositories.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/serviceprovider.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/__init__.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/annotation_class.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/explore.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/folder.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/http_client.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/item.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/item_service.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/project.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/work_management.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/validators.py +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/SOURCES.txt +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/dependency_links.txt +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/entry_points.txt +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/requires.txt +0 -0
- {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/top_level.txt +0 -0
|
@@ -505,20 +505,19 @@ class SAClient(BaseInterfaceFacade, metaclass=TrackableMeta):
|
|
|
505
505
|
) -> Tuple[bool, typing.Any]:
|
|
506
506
|
try:
|
|
507
507
|
for component in component_data:
|
|
508
|
-
if
|
|
509
|
-
component["type"] == "webComponent"
|
|
510
|
-
and component["id"] == component_pk
|
|
511
|
-
):
|
|
512
|
-
return True, component.get("context")
|
|
513
|
-
if (
|
|
514
|
-
component["type"] in ("group", "grid")
|
|
515
|
-
and "children" in component
|
|
516
|
-
):
|
|
508
|
+
if "children" in component:
|
|
517
509
|
found, val = retrieve_context(
|
|
518
510
|
component["children"], component_pk
|
|
519
511
|
)
|
|
520
512
|
if found:
|
|
521
513
|
return found, val
|
|
514
|
+
if (
|
|
515
|
+
"id" in component
|
|
516
|
+
and component["id"] == component_pk
|
|
517
|
+
and component["type"] == "webComponent"
|
|
518
|
+
):
|
|
519
|
+
return True, json.loads(component.get("context"))
|
|
520
|
+
|
|
522
521
|
except KeyError as e:
|
|
523
522
|
logger.debug("Got key error:", component_data)
|
|
524
523
|
raise e
|
|
@@ -2920,32 +2919,80 @@ class SAClient(BaseInterfaceFacade, metaclass=TrackableMeta):
|
|
|
2920
2919
|
project: NotEmptyStr,
|
|
2921
2920
|
integration: Union[NotEmptyStr, IntegrationEntity],
|
|
2922
2921
|
folder_path: Optional[NotEmptyStr] = None,
|
|
2922
|
+
*,
|
|
2923
|
+
query: Optional[NotEmptyStr] = None,
|
|
2924
|
+
item_name_column: Optional[NotEmptyStr] = None,
|
|
2925
|
+
custom_item_name: Optional[NotEmptyStr] = None,
|
|
2926
|
+
component_mapping: Optional[Dict[str, str]] = None,
|
|
2923
2927
|
):
|
|
2924
|
-
"""Link images from integrated external storage to SuperAnnotate.
|
|
2928
|
+
"""Link images from integrated external storage to SuperAnnotate from AWS, GCP, Azure, Databricks.
|
|
2925
2929
|
|
|
2926
2930
|
:param project: project name or folder path where items should be attached (e.g., “project1/folder1”).
|
|
2927
2931
|
:type project: str
|
|
2928
2932
|
|
|
2929
|
-
:param integration:
|
|
2930
|
-
|
|
2933
|
+
:param integration: The existing integration name or metadata dict to pull items from.
|
|
2934
|
+
Mandatory keys in integration metadata’s dict is “name”.
|
|
2931
2935
|
:type integration: str or dict
|
|
2932
2936
|
|
|
2933
2937
|
:param folder_path: Points to an exact folder/directory within given storage.
|
|
2934
|
-
|
|
2938
|
+
If None, items are fetched from the root directory.
|
|
2935
2939
|
:type folder_path: str
|
|
2940
|
+
|
|
2941
|
+
:param query: (Only for Databricks). The SQL query to retrieve specific columns from Databricks.
|
|
2942
|
+
If provided, the function will execute the query and use the results for mapping and uploading.
|
|
2943
|
+
:type query: Optional[str]
|
|
2944
|
+
|
|
2945
|
+
:param item_name_column: (Only for Databricks). The column name from the SQL query whose values
|
|
2946
|
+
will be used as item names. If this is provided, custom_item_name cannot be used.
|
|
2947
|
+
The column must exist in the query result.
|
|
2948
|
+
:type item_name_column: Optional[str]
|
|
2949
|
+
|
|
2950
|
+
:param custom_item_name: (Only for Databricks). A manually defined prefix for item names.
|
|
2951
|
+
A random 10-character suffix will be appended to ensure uniqueness.
|
|
2952
|
+
If this is provided, item_name_column cannot be used.
|
|
2953
|
+
:type custom_item_name: Optional[str]
|
|
2954
|
+
|
|
2955
|
+
:param component_mapping: (Only for Databricks). A dictionary mapping Databricks
|
|
2956
|
+
columns to SuperAnnotate component IDs.
|
|
2957
|
+
:type component_mapping: Optional[dict]
|
|
2958
|
+
|
|
2959
|
+
|
|
2960
|
+
Request Example:
|
|
2961
|
+
::
|
|
2962
|
+
|
|
2963
|
+
client.attach_items_from_integrated_storage(
|
|
2964
|
+
project="project_name",
|
|
2965
|
+
integration="databricks_integration",
|
|
2966
|
+
query="SELECT * FROM integration_data LIMIT 10",
|
|
2967
|
+
item_name_column="prompt",
|
|
2968
|
+
component_mapping={
|
|
2969
|
+
"category": "_item_category",
|
|
2970
|
+
"prompt_id": "id",
|
|
2971
|
+
"prompt": "prompt"
|
|
2972
|
+
}
|
|
2973
|
+
)
|
|
2974
|
+
|
|
2936
2975
|
"""
|
|
2937
2976
|
project, folder = self.controller.get_project_folder_by_path(project)
|
|
2938
2977
|
_integration = None
|
|
2939
2978
|
if isinstance(integration, str):
|
|
2940
2979
|
integration = IntegrationEntity(name=integration)
|
|
2941
2980
|
for i in self.controller.integrations.list().data:
|
|
2942
|
-
if integration.name == i.name:
|
|
2981
|
+
if integration.name.lower() == i.name.lower():
|
|
2943
2982
|
_integration = i
|
|
2944
2983
|
break
|
|
2945
2984
|
else:
|
|
2946
2985
|
raise AppException("Integration not found.")
|
|
2986
|
+
|
|
2947
2987
|
response = self.controller.integrations.attach_items(
|
|
2948
|
-
project,
|
|
2988
|
+
project=project,
|
|
2989
|
+
folder=folder,
|
|
2990
|
+
integration=_integration,
|
|
2991
|
+
folder_path=folder_path,
|
|
2992
|
+
query=query,
|
|
2993
|
+
item_name_column=item_name_column,
|
|
2994
|
+
custom_item_name=custom_item_name,
|
|
2995
|
+
component_mapping=component_mapping,
|
|
2949
2996
|
)
|
|
2950
2997
|
if response.errors:
|
|
2951
2998
|
raise AppException(response.errors)
|
|
@@ -3594,7 +3641,7 @@ class SAClient(BaseInterfaceFacade, metaclass=TrackableMeta):
|
|
|
3594
3641
|
"skip", "replace", "replace_annotations_only"
|
|
3595
3642
|
] = "skip",
|
|
3596
3643
|
):
|
|
3597
|
-
"""Copy
|
|
3644
|
+
"""Copy items in bulk between folders in a project
|
|
3598
3645
|
|
|
3599
3646
|
:param source: project name (root) or folder path to pick items from (e.g., “project1/folder1”).
|
|
3600
3647
|
:type source: str
|
|
@@ -3658,7 +3705,7 @@ class SAClient(BaseInterfaceFacade, metaclass=TrackableMeta):
|
|
|
3658
3705
|
"skip", "replace", "replace_annotations_only"
|
|
3659
3706
|
] = "skip",
|
|
3660
3707
|
):
|
|
3661
|
-
"""Move
|
|
3708
|
+
"""Move items in bulk between folders in a project
|
|
3662
3709
|
|
|
3663
3710
|
:param source: project name (root) or folder path to pick items from (e.g., “project1/folder1”).
|
|
3664
3711
|
:type source: str
|
{superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/serviceproviders.py
RENAMED
|
@@ -490,6 +490,7 @@ class BaseAnnotationService(SuperannotateServiceProvider):
|
|
|
490
490
|
self,
|
|
491
491
|
project: entities.ProjectEntity,
|
|
492
492
|
item_ids: List[int],
|
|
493
|
+
chunk_size: int = 1000,
|
|
493
494
|
) -> Dict[str, List]:
|
|
494
495
|
raise NotImplementedError
|
|
495
496
|
|
|
@@ -592,6 +593,7 @@ class BaseIntegrationService(SuperannotateServiceProvider):
|
|
|
592
593
|
folder: entities.FolderEntity,
|
|
593
594
|
integration: entities.IntegrationEntity,
|
|
594
595
|
folder_name: str = None,
|
|
596
|
+
options: Dict[str, str] = None,
|
|
595
597
|
) -> ServiceResponse:
|
|
596
598
|
raise NotImplementedError
|
|
597
599
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from lib.core.conditions import Condition
|
|
5
|
+
from lib.core.conditions import CONDITION_EQ as EQ
|
|
6
|
+
from lib.core.entities import FolderEntity
|
|
7
|
+
from lib.core.entities import IntegrationEntity
|
|
8
|
+
from lib.core.entities import ProjectEntity
|
|
9
|
+
from lib.core.entities.integrations import IntegrationTypeEnum
|
|
10
|
+
from lib.core.enums import ProjectType
|
|
11
|
+
from lib.core.exceptions import AppException
|
|
12
|
+
from lib.core.reporter import Reporter
|
|
13
|
+
from lib.core.response import Response
|
|
14
|
+
from lib.core.serviceproviders import BaseServiceProvider
|
|
15
|
+
from lib.core.usecases import BaseReportableUseCase
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GetIntegrations(BaseReportableUseCase):
|
|
19
|
+
def __init__(self, reporter: Reporter, service_provider: BaseServiceProvider):
|
|
20
|
+
|
|
21
|
+
super().__init__(reporter)
|
|
22
|
+
self._service_provider = service_provider
|
|
23
|
+
|
|
24
|
+
def execute(self) -> Response:
|
|
25
|
+
integrations = self._service_provider.integrations.list().data.integrations
|
|
26
|
+
integrations = list(sorted(integrations, key=lambda x: x.createdAt))
|
|
27
|
+
integrations.reverse()
|
|
28
|
+
self._response.data = integrations
|
|
29
|
+
return self._response
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AttachIntegrations(BaseReportableUseCase):
|
|
33
|
+
MULTIMODAL_INTEGRATIONS = [
|
|
34
|
+
IntegrationTypeEnum.DATABRICKS,
|
|
35
|
+
IntegrationTypeEnum.SNOWFLAKE,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
reporter: Reporter,
|
|
41
|
+
project: ProjectEntity,
|
|
42
|
+
folder: FolderEntity,
|
|
43
|
+
service_provider: BaseServiceProvider,
|
|
44
|
+
integration: IntegrationEntity,
|
|
45
|
+
folder_path: str = None,
|
|
46
|
+
query: Optional[str] = None,
|
|
47
|
+
item_name_column: Optional[str] = None,
|
|
48
|
+
custom_item_name: Optional[str] = None,
|
|
49
|
+
component_mapping: Optional[Dict[str, str]] = None,
|
|
50
|
+
):
|
|
51
|
+
super().__init__(reporter)
|
|
52
|
+
self._project = project
|
|
53
|
+
self._folder = folder
|
|
54
|
+
self._integration = integration
|
|
55
|
+
self._service_provider = service_provider
|
|
56
|
+
self._folder_path = folder_path
|
|
57
|
+
self._query = query
|
|
58
|
+
self._item_name_column = item_name_column
|
|
59
|
+
self._custom_item_name = custom_item_name
|
|
60
|
+
self._component_mapping = component_mapping
|
|
61
|
+
self._options = {} # using only for Databricks and Snowflake
|
|
62
|
+
self._item_category_column = None
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def _upload_path(self):
|
|
66
|
+
return f"{self._project.name}{f'/{self._folder.name}' if self._folder.name != 'root' else ''}"
|
|
67
|
+
|
|
68
|
+
def validate_integration(self):
|
|
69
|
+
# TODO add support in next iterations
|
|
70
|
+
if self._integration.type == IntegrationTypeEnum.SNOWFLAKE:
|
|
71
|
+
raise AppException(
|
|
72
|
+
"Attaching items is not supported with Snowflake integration."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if self._integration.type in self.MULTIMODAL_INTEGRATIONS:
|
|
76
|
+
if self._project.type != ProjectType.MULTIMODAL:
|
|
77
|
+
raise AppException(
|
|
78
|
+
f"{self._integration.name} integration is supported only for Multimodal projects."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def validate_options_for_multimodal_integration(self):
|
|
82
|
+
if self._integration.type in self.MULTIMODAL_INTEGRATIONS:
|
|
83
|
+
if self._item_name_column and self._custom_item_name:
|
|
84
|
+
raise AppException(
|
|
85
|
+
"‘item_name_column and custom_item_name cannot be used simultaneously."
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if not self._item_name_column and not self._custom_item_name:
|
|
89
|
+
raise AppException(
|
|
90
|
+
"Either item_name_column or custom_item_name is required."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if not all((self._query, self._component_mapping)):
|
|
94
|
+
raise AppException(
|
|
95
|
+
f"{self._integration.name} integration requires both a query and component_mapping."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
category_setting: bool = bool(
|
|
99
|
+
next(
|
|
100
|
+
(
|
|
101
|
+
setting.value
|
|
102
|
+
for setting in self._service_provider.projects.list_settings(
|
|
103
|
+
self._project
|
|
104
|
+
).data
|
|
105
|
+
if setting.attribute == "CategorizeItems"
|
|
106
|
+
),
|
|
107
|
+
None,
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
if (
|
|
111
|
+
not category_setting
|
|
112
|
+
and "_item_category" in self._component_mapping.values()
|
|
113
|
+
):
|
|
114
|
+
raise AppException(
|
|
115
|
+
"Item Category must be enabled for a project to use _item_category"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
self._item_category_column = next(
|
|
119
|
+
(
|
|
120
|
+
k
|
|
121
|
+
for k, v in self._component_mapping.items()
|
|
122
|
+
if v == "_item_category"
|
|
123
|
+
),
|
|
124
|
+
None,
|
|
125
|
+
)
|
|
126
|
+
if self._item_category_column:
|
|
127
|
+
del self._component_mapping[self._item_category_column]
|
|
128
|
+
|
|
129
|
+
sa_components = [
|
|
130
|
+
c.name.lower()
|
|
131
|
+
for c in self._service_provider.annotation_classes.list(
|
|
132
|
+
condition=Condition("project_id", self._project.id, EQ)
|
|
133
|
+
).data
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
for i in self._component_mapping.values():
|
|
137
|
+
if i.lower() not in sa_components:
|
|
138
|
+
raise AppException(
|
|
139
|
+
f"Component mapping contains invalid component ID: `{i}`"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def generate_options_for_multimodal_integration(self):
|
|
143
|
+
self._options["query"] = self._query
|
|
144
|
+
self._options["item_name"] = (
|
|
145
|
+
self._custom_item_name if self._custom_item_name else self._item_name_column
|
|
146
|
+
)
|
|
147
|
+
self._options["prefix"] = True if self._custom_item_name else False
|
|
148
|
+
self._options["column_class_map"] = self._component_mapping
|
|
149
|
+
if self._item_category_column:
|
|
150
|
+
self._options["item_category"] = self._item_category_column
|
|
151
|
+
|
|
152
|
+
def execute(self) -> Response:
|
|
153
|
+
if self.is_valid():
|
|
154
|
+
if self._integration.type in self.MULTIMODAL_INTEGRATIONS:
|
|
155
|
+
self.generate_options_for_multimodal_integration()
|
|
156
|
+
|
|
157
|
+
self.reporter.log_info(
|
|
158
|
+
"Attaching file(s) from "
|
|
159
|
+
f"{self._integration.root}{f'/{self._folder_path}' if self._folder_path else ''} "
|
|
160
|
+
f"to {self._upload_path}. This may take some time."
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
attache_response = self._service_provider.integrations.attach_items(
|
|
164
|
+
project=self._project,
|
|
165
|
+
folder=self._folder,
|
|
166
|
+
integration=self._integration,
|
|
167
|
+
folder_name=self._folder_path
|
|
168
|
+
if self._integration.type not in self.MULTIMODAL_INTEGRATIONS
|
|
169
|
+
else None,
|
|
170
|
+
options=self._options if self._options else None,
|
|
171
|
+
)
|
|
172
|
+
if not attache_response.ok:
|
|
173
|
+
self._response.errors = AppException(
|
|
174
|
+
f"An error occurred for {self._integration.name}. Please make sure: "
|
|
175
|
+
"\n - The bucket exists."
|
|
176
|
+
"\n - The connection is valid."
|
|
177
|
+
"\n - The path to a specified directory is correct."
|
|
178
|
+
)
|
|
179
|
+
return self._response
|
|
@@ -1138,7 +1138,11 @@ class IntegrationManager(BaseManager):
|
|
|
1138
1138
|
project: ProjectEntity,
|
|
1139
1139
|
folder: FolderEntity,
|
|
1140
1140
|
integration: IntegrationEntity,
|
|
1141
|
-
folder_path: str,
|
|
1141
|
+
folder_path: str = None,
|
|
1142
|
+
query: Optional[str] = None,
|
|
1143
|
+
item_name_column: Optional[str] = None,
|
|
1144
|
+
custom_item_name: Optional[str] = None,
|
|
1145
|
+
component_mapping: Optional[Dict[str, str]] = None,
|
|
1142
1146
|
):
|
|
1143
1147
|
use_case = usecases.AttachIntegrations(
|
|
1144
1148
|
reporter=Reporter(),
|
|
@@ -1147,6 +1151,10 @@ class IntegrationManager(BaseManager):
|
|
|
1147
1151
|
folder=folder,
|
|
1148
1152
|
integration=integration,
|
|
1149
1153
|
folder_path=folder_path,
|
|
1154
|
+
query=query,
|
|
1155
|
+
item_name_column=item_name_column,
|
|
1156
|
+
custom_item_name=custom_item_name,
|
|
1157
|
+
component_mapping=component_mapping,
|
|
1150
1158
|
)
|
|
1151
1159
|
return use_case.execute()
|
|
1152
1160
|
|
|
@@ -18,6 +18,8 @@ from lib.core.service_types import UploadAnnotations
|
|
|
18
18
|
from lib.core.service_types import UploadAnnotationsResponse
|
|
19
19
|
from lib.core.serviceproviders import BaseAnnotationService
|
|
20
20
|
from lib.infrastructure.stream_data_handler import StreamedAnnotations
|
|
21
|
+
from lib.infrastructure.utils import annotation_is_valid
|
|
22
|
+
from lib.infrastructure.utils import divide_to_chunks
|
|
21
23
|
|
|
22
24
|
try:
|
|
23
25
|
from pydantic.v1 import parse_obj_as
|
|
@@ -170,21 +172,29 @@ class AnnotationService(BaseAnnotationService):
|
|
|
170
172
|
self,
|
|
171
173
|
project: entities.ProjectEntity,
|
|
172
174
|
item_ids: List[int],
|
|
175
|
+
chunk_size: int = 1000,
|
|
173
176
|
) -> Dict[str, List]:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
small = []
|
|
178
|
+
large = []
|
|
179
|
+
|
|
180
|
+
chunks = divide_to_chunks(item_ids, chunk_size)
|
|
181
|
+
for chunk in chunks:
|
|
182
|
+
response = self.client.request(
|
|
183
|
+
method="POST",
|
|
184
|
+
url=urljoin(
|
|
185
|
+
self.get_assets_provider_url(), self.URL_CLASSIFY_ITEM_SIZE
|
|
186
|
+
),
|
|
187
|
+
params={"limit": len(chunk)},
|
|
188
|
+
data={
|
|
189
|
+
"project_id": project.id,
|
|
190
|
+
"item_ids": chunk,
|
|
191
|
+
},
|
|
192
|
+
)
|
|
193
|
+
if not response.ok:
|
|
194
|
+
raise AppException(response.error)
|
|
195
|
+
small.extend([i["data"] for i in response.data.get("small", {}).values()])
|
|
196
|
+
large.extend(response.data.get("large", []))
|
|
197
|
+
return {"small": small, "large": large}
|
|
188
198
|
|
|
189
199
|
async def download_big_annotation(
|
|
190
200
|
self,
|
|
@@ -218,6 +228,14 @@ class AnnotationService(BaseAnnotationService):
|
|
|
218
228
|
) as session:
|
|
219
229
|
start_response = await session.request("post", url, params=query_params)
|
|
220
230
|
res = await start_response.json()
|
|
231
|
+
if start_response.status > 299 or not annotation_is_valid(res):
|
|
232
|
+
logger.debug(
|
|
233
|
+
f"Failed to download large annotation; item_id [{item_id}];"
|
|
234
|
+
f" response: {res}; http_status: {start_response.status}"
|
|
235
|
+
)
|
|
236
|
+
raise AppException(
|
|
237
|
+
f"Failed to download large annotation, ID: {item_id}"
|
|
238
|
+
)
|
|
221
239
|
Path(download_path).mkdir(exist_ok=True, parents=True)
|
|
222
240
|
|
|
223
241
|
dest_path = Path(download_path) / (item_name + ".json")
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
1
3
|
from lib.core import entities
|
|
2
4
|
from lib.core.service_types import IntegrationListResponse
|
|
3
5
|
from lib.core.serviceproviders import BaseIntegrationService
|
|
@@ -23,6 +25,7 @@ class IntegrationService(BaseIntegrationService):
|
|
|
23
25
|
folder: entities.FolderEntity,
|
|
24
26
|
integration: entities.IntegrationEntity,
|
|
25
27
|
folder_name: str = None,
|
|
28
|
+
options: Dict[str, str] = None,
|
|
26
29
|
):
|
|
27
30
|
data = {
|
|
28
31
|
"team_id": project.team_id,
|
|
@@ -32,6 +35,8 @@ class IntegrationService(BaseIntegrationService):
|
|
|
32
35
|
}
|
|
33
36
|
if folder_name:
|
|
34
37
|
data["customer_folder_name"] = folder_name
|
|
38
|
+
if options:
|
|
39
|
+
data["options"] = options
|
|
35
40
|
return self.client.request(
|
|
36
41
|
self.URL_ATTACH_INTEGRATIONS.format(project.team_id), "post", data=data
|
|
37
42
|
)
|
|
@@ -6,8 +6,12 @@ import typing
|
|
|
6
6
|
from typing import Callable
|
|
7
7
|
|
|
8
8
|
import aiohttp
|
|
9
|
+
from lib.core.exceptions import AppException
|
|
10
|
+
from lib.core.exceptions import BackendError
|
|
9
11
|
from lib.core.reporter import Reporter
|
|
10
12
|
from lib.infrastructure.services.http_client import AIOHttpSession
|
|
13
|
+
from lib.infrastructure.utils import annotation_is_valid
|
|
14
|
+
from lib.infrastructure.utils import async_retry_on_generator
|
|
11
15
|
|
|
12
16
|
_seconds = 2**10
|
|
13
17
|
TIMEOUT = aiohttp.ClientTimeout(
|
|
@@ -42,6 +46,7 @@ class StreamedAnnotations:
|
|
|
42
46
|
self._reporter.log_error(f"Invalud chunk: {str(e)}")
|
|
43
47
|
return None
|
|
44
48
|
|
|
49
|
+
@async_retry_on_generator((BackendError,))
|
|
45
50
|
async def fetch(
|
|
46
51
|
self,
|
|
47
52
|
method: str,
|
|
@@ -59,6 +64,7 @@ class StreamedAnnotations:
|
|
|
59
64
|
buffer = ""
|
|
60
65
|
line_groups = b""
|
|
61
66
|
decoder = json.JSONDecoder()
|
|
67
|
+
data_received = False
|
|
62
68
|
async for line in response.content.iter_any():
|
|
63
69
|
line_groups += line
|
|
64
70
|
try:
|
|
@@ -71,6 +77,19 @@ class StreamedAnnotations:
|
|
|
71
77
|
if buffer.startswith(self.DELIMITER):
|
|
72
78
|
buffer = buffer[self.DELIMITER_LEN :]
|
|
73
79
|
json_obj, index = decoder.raw_decode(buffer)
|
|
80
|
+
if not annotation_is_valid(json_obj):
|
|
81
|
+
logger.warning(
|
|
82
|
+
f"Invalid JSON detected in small annotations stream process, json: {json_obj}."
|
|
83
|
+
)
|
|
84
|
+
if data_received:
|
|
85
|
+
raise AppException(
|
|
86
|
+
"Invalid JSON detected in small annotations stream process."
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
raise BackendError(
|
|
90
|
+
"Invalid JSON detected at the start of the small annotations stream process."
|
|
91
|
+
)
|
|
92
|
+
data_received = True
|
|
74
93
|
yield json_obj
|
|
75
94
|
if len(buffer[index:]) >= self.DELIMITER_LEN:
|
|
76
95
|
buffer = buffer[index + self.DELIMITER_LEN :]
|
{superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/utils.py
RENAMED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
1
3
|
import time
|
|
2
4
|
from abc import ABC
|
|
3
5
|
from abc import abstractmethod
|
|
6
|
+
from functools import wraps
|
|
4
7
|
from itertools import islice
|
|
5
8
|
from pathlib import Path
|
|
6
9
|
from typing import Any
|
|
10
|
+
from typing import Callable
|
|
7
11
|
from typing import Dict
|
|
8
12
|
from typing import Optional
|
|
9
13
|
from typing import Tuple
|
|
14
|
+
from typing import Type
|
|
10
15
|
from typing import Union
|
|
11
16
|
|
|
12
17
|
from lib.core.entities import ProjectEntity
|
|
@@ -16,6 +21,9 @@ from lib.core.exceptions import PathError
|
|
|
16
21
|
from lib.infrastructure.services.work_management import WorkManagementService
|
|
17
22
|
|
|
18
23
|
|
|
24
|
+
logger = logging.getLogger("sa")
|
|
25
|
+
|
|
26
|
+
|
|
19
27
|
def divide_to_chunks(it, size):
|
|
20
28
|
it = iter(it)
|
|
21
29
|
return iter(lambda: tuple(islice(it, size)), ())
|
|
@@ -44,6 +52,66 @@ def extract_project_folder(user_input: Union[str, dict]) -> Tuple[str, Optional[
|
|
|
44
52
|
raise PathError("Invalid project path")
|
|
45
53
|
|
|
46
54
|
|
|
55
|
+
def async_retry_on_generator(
|
|
56
|
+
exceptions: Tuple[Type[Exception]],
|
|
57
|
+
retries: int = 3,
|
|
58
|
+
delay: float = 0.3,
|
|
59
|
+
backoff: float = 0.3,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
An async retry decorator that retries a function only on specific exceptions.
|
|
63
|
+
|
|
64
|
+
Parameters:
|
|
65
|
+
exceptions (tuple): Tuple of exception classes to retry on.
|
|
66
|
+
retries (int): Number of retry attempts.
|
|
67
|
+
delay (float): Initial delay between retries in seconds.
|
|
68
|
+
backoff (float): Factor to increase the delay after each failure.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def decorator(func: Callable):
|
|
72
|
+
@wraps(func)
|
|
73
|
+
async def wrapper(*args, **kwargs):
|
|
74
|
+
attempt = 0
|
|
75
|
+
current_delay = delay
|
|
76
|
+
raised_exception = None
|
|
77
|
+
|
|
78
|
+
while attempt < retries:
|
|
79
|
+
try:
|
|
80
|
+
async for v in func(*args, **kwargs):
|
|
81
|
+
yield v
|
|
82
|
+
return
|
|
83
|
+
except exceptions as e:
|
|
84
|
+
raised_exception = e
|
|
85
|
+
logger.debug(
|
|
86
|
+
f"Attempt {attempt + 1}/{retries} failed with error: {e}. "
|
|
87
|
+
f"Retrying in {current_delay} seconds..."
|
|
88
|
+
)
|
|
89
|
+
await asyncio.sleep(current_delay)
|
|
90
|
+
current_delay += backoff # Exponential backoff
|
|
91
|
+
finally:
|
|
92
|
+
attempt += 1
|
|
93
|
+
if raised_exception:
|
|
94
|
+
logger.error(
|
|
95
|
+
f"All {retries} attempts failed due to {raised_exception}."
|
|
96
|
+
)
|
|
97
|
+
raise raised_exception
|
|
98
|
+
|
|
99
|
+
return wrapper
|
|
100
|
+
|
|
101
|
+
return decorator
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def annotation_is_valid(annotation: dict) -> bool:
|
|
105
|
+
annotation_keys = annotation.keys()
|
|
106
|
+
if (
|
|
107
|
+
"errors" in annotation_keys
|
|
108
|
+
or "error" in annotation_keys
|
|
109
|
+
or "metadata" not in annotation_keys
|
|
110
|
+
):
|
|
111
|
+
return False
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
|
|
47
115
|
class BaseCachedWorkManagementRepository(ABC):
|
|
48
116
|
def __init__(self, ttl_seconds: int, work_management: WorkManagementService):
|
|
49
117
|
self.ttl_seconds = ttl_seconds
|
|
@@ -80,10 +148,12 @@ class RoleCache(BaseCachedWorkManagementRepository):
|
|
|
80
148
|
roles = response.data["data"]
|
|
81
149
|
self._K_V_map[project.id] = {
|
|
82
150
|
"role_name_id_map": {
|
|
83
|
-
role["role"]["name"]: role["role_id"] for role in roles
|
|
151
|
+
**{role["role"]["name"]: role["role_id"] for role in roles},
|
|
152
|
+
"ProjectAdmin": 3
|
|
84
153
|
},
|
|
85
154
|
"role_id_name_map": {
|
|
86
|
-
role["role_id"]: role["role"]["name"] for role in roles
|
|
155
|
+
**{role["role_id"]: role["role"]["name"] for role in roles},
|
|
156
|
+
3: "ProjectAdmin"
|
|
87
157
|
},
|
|
88
158
|
}
|
|
89
159
|
self._update_cache_timestamp(project.id)
|