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.
Files changed (147) hide show
  1. {superannotate-4.4.30b3/src/superannotate.egg-info → superannotate-4.4.31.dev2}/PKG-INFO +1 -1
  2. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/__init__.py +1 -1
  3. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk_interface.py +64 -17
  4. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/serviceproviders.py +2 -0
  5. superannotate-4.4.31.dev2/src/superannotate/lib/core/usecases/integrations.py +179 -0
  6. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/controller.py +9 -1
  7. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/annotation.py +32 -14
  8. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/integration.py +5 -0
  9. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/stream_data_handler.py +19 -0
  10. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/utils.py +72 -2
  11. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2/src/superannotate.egg-info}/PKG-INFO +1 -1
  12. superannotate-4.4.30b3/src/superannotate/lib/core/usecases/integrations.py +0 -78
  13. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/LICENSE +0 -0
  14. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/MANIFEST.in +0 -0
  15. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/README.rst +0 -0
  16. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/requirements.txt +0 -0
  17. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/setup.cfg +0 -0
  18. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/setup.py +0 -0
  19. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/__init__.py +0 -0
  20. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/__init__.py +0 -0
  21. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/__init__.py +0 -0
  22. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/aggregators.py +0 -0
  23. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/analytics/common.py +0 -0
  24. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/bin/__init__.py +0 -0
  25. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/bin/superannotate.py +0 -0
  26. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/common.py +0 -0
  27. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/exceptions.py +0 -0
  28. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/helpers.py +0 -0
  29. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/__init__.py +0 -0
  30. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/conversion.py +0 -0
  31. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/__init__.py +0 -0
  32. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/baseStrategy.py +0 -0
  33. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/__init__.py +0 -0
  34. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_api.py +0 -0
  35. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_converter.py +0 -0
  36. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/coco_converters/coco_strategies.py +0 -0
  37. {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
  38. {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
  39. {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
  40. {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
  41. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/converters.py +0 -0
  42. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/__init__.py +0 -0
  43. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_helper.py +0 -0
  44. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/dataloop_converters/dataloop_strategies.py +0 -0
  45. {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
  46. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/__init__.py +0 -0
  47. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/googlecloud_converters/googlecloud_strategies.py +0 -0
  48. {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
  49. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/__init__.py +0 -0
  50. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_helper.py +0 -0
  51. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/labelbox_converters/labelbox_strategies.py +0 -0
  52. {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
  53. {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
  54. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sa_json_helper.py +0 -0
  55. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/__init__.py +0 -0
  56. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/sagemaker_converters/sagemaker_strategies.py +0 -0
  57. {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
  58. {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
  59. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/__init__.py +0 -0
  60. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_helper.py +0 -0
  61. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/supervisely_converters/supervisely_strategies.py +0 -0
  62. {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
  63. {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
  64. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/__init__.py +0 -0
  65. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_helper.py +0 -0
  66. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vgg_converters/vgg_strategies.py +0 -0
  67. {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
  68. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/__init__.py +0 -0
  69. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_helper.py +0 -0
  70. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/voc_converters/voc_strategies.py +0 -0
  71. {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
  72. {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
  73. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vott_converters/__init__.py +0 -0
  74. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/vott_converters/vott_strategies.py +0 -0
  75. {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
  76. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/yolo_converters/__init__.py +0 -0
  77. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/converters/yolo_converters/yolo_strategies.py +0 -0
  78. {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
  79. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/export_from_sa_conversions.py +0 -0
  80. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/import_to_sa_conversions.py +0 -0
  81. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/input_converters/sa_conversion.py +0 -0
  82. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/__init__.py +0 -0
  83. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/base_interface.py +0 -0
  84. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/cli_interface.py +0 -0
  85. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/__init__.py +0 -0
  86. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/folders.py +0 -0
  87. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/sdk/project.py +0 -0
  88. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/interface/types.py +0 -0
  89. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/app/serializers.py +0 -0
  90. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/__init__.py +0 -0
  91. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/base_usecases.py +0 -0
  92. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/conditions.py +0 -0
  93. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/config.py +0 -0
  94. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/__init__.py +0 -0
  95. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/base.py +0 -0
  96. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/classes.py +0 -0
  97. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/filters.py +0 -0
  98. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/folder.py +0 -0
  99. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/integrations.py +0 -0
  100. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/items.py +0 -0
  101. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/project.py +0 -0
  102. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/project_entities.py +0 -0
  103. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/entities/work_managament.py +0 -0
  104. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/enums.py +0 -0
  105. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/exceptions.py +0 -0
  106. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/jsx_conditions.py +0 -0
  107. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/plugin.py +0 -0
  108. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/pydantic_v1.py +0 -0
  109. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/reporter.py +0 -0
  110. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/repositories.py +0 -0
  111. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/response.py +0 -0
  112. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/service_types.py +0 -0
  113. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/types.py +0 -0
  114. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/__init__.py +0 -0
  115. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/annotations.py +0 -0
  116. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/base.py +0 -0
  117. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/classes.py +0 -0
  118. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/custom_fields.py +0 -0
  119. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/folders.py +0 -0
  120. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/images.py +0 -0
  121. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/items.py +0 -0
  122. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/models.py +0 -0
  123. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/usecases/projects.py +0 -0
  124. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/utils.py +0 -0
  125. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/core/video_convertor.py +0 -0
  126. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/__init__.py +0 -0
  127. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/annotation_adapter.py +0 -0
  128. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/custom_entities.py +0 -0
  129. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/helpers.py +0 -0
  130. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/query_builder.py +0 -0
  131. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/repositories.py +0 -0
  132. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/serviceprovider.py +0 -0
  133. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/__init__.py +0 -0
  134. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/annotation_class.py +0 -0
  135. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/explore.py +0 -0
  136. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/folder.py +0 -0
  137. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/http_client.py +0 -0
  138. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/item.py +0 -0
  139. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/item_service.py +0 -0
  140. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/project.py +0 -0
  141. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/services/work_management.py +0 -0
  142. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate/lib/infrastructure/validators.py +0 -0
  143. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/SOURCES.txt +0 -0
  144. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/dependency_links.txt +0 -0
  145. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/entry_points.txt +0 -0
  146. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/requires.txt +0 -0
  147. {superannotate-4.4.30b3 → superannotate-4.4.31.dev2}/src/superannotate.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: superannotate
3
- Version: 4.4.30b3
3
+ Version: 4.4.31.dev2
4
4
  Summary: Python SDK to SuperAnnotate platform
5
5
  Home-page: https://github.com/superannotateai/superannotate-python-sdk
6
6
  Author: SuperAnnotate AI
@@ -3,7 +3,7 @@ import os
3
3
  import sys
4
4
 
5
5
 
6
- __version__ = "4.4.30b3"
6
+ __version__ = "4.4.31dev2"
7
7
 
8
8
  os.environ.update({"sa_version": __version__})
9
9
  sys.path.append(os.path.split(os.path.realpath(__file__))[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: existing integration name or metadata dict to pull items from.
2930
- Mandatory keys in integration metadata’s dict is “name”.
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
- If None, items are fetched from the root directory.
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, folder, _integration, folder_path
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 images in bulk between folders in a project
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 images in bulk between folders in a project
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
@@ -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
- response_data = {"small": [], "large": []}
175
- response = self.client.request(
176
- url=urljoin(self.get_assets_provider_url(), self.URL_CLASSIFY_ITEM_SIZE),
177
- method="POST",
178
- params={"limit": len(item_ids)},
179
- data={"project_id": project.id, "item_ids": item_ids},
180
- )
181
- if not response.ok:
182
- raise AppException(response.error)
183
- response_data["small"] = [
184
- i["data"] for i in response.data.get("small", {}).values()
185
- ]
186
- response_data["large"] = response.data.get("large", [])
187
- return response_data
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 :]
@@ -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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: superannotate
3
- Version: 4.4.30b3
3
+ Version: 4.4.31.dev2
4
4
  Summary: Python SDK to SuperAnnotate platform
5
5
  Home-page: https://github.com/superannotateai/superannotate-python-sdk
6
6
  Author: SuperAnnotate AI