supervisely 6.73.348__py3-none-any.whl → 6.73.350__py3-none-any.whl

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.
@@ -23,7 +23,7 @@ import numpy as np
23
23
  from requests_toolbelt import MultipartDecoder, MultipartEncoder
24
24
  from tqdm import tqdm
25
25
 
26
- from supervisely._utils import batched
26
+ from supervisely._utils import batched, logger, run_coroutine
27
27
  from supervisely.api.module_api import ApiField, ModuleApi, RemoveableBulkModuleApi
28
28
  from supervisely.geometry.rectangle import Rectangle
29
29
  from supervisely.video_annotation.key_id_map import KeyIdMap
@@ -473,24 +473,24 @@ class FigureApi(RemoveableBulkModuleApi):
473
473
  :rtype: :class: `Dict[int, List[FigureInfo]]`
474
474
  """
475
475
  fields = [
476
- "id",
477
- "createdAt",
478
- "updatedAt",
479
- "imageId",
480
- "objectId",
481
- "classId",
482
- "projectId",
483
- "datasetId",
484
- "geometry",
485
- "geometryType",
486
- "geometryMeta",
487
- "tags",
488
- "meta",
489
- "area",
490
- "priority",
476
+ ApiField.ID,
477
+ ApiField.CREATED_AT,
478
+ ApiField.UPDATED_AT,
479
+ ApiField.IMAGE_ID,
480
+ ApiField.OBJECT_ID,
481
+ ApiField.CLASS_ID,
482
+ ApiField.PROJECT_ID,
483
+ ApiField.DATASET_ID,
484
+ ApiField.GEOMETRY,
485
+ ApiField.GEOMETRY_TYPE,
486
+ ApiField.GEOMETRY_META,
487
+ ApiField.TAGS,
488
+ ApiField.META,
489
+ ApiField.AREA,
490
+ ApiField.PRIORITY,
491
491
  ]
492
492
  if skip_geometry is True:
493
- fields = [x for x in fields if x != "geometry"]
493
+ fields = [x for x in fields if x != ApiField.GEOMETRY]
494
494
 
495
495
  if image_ids is None:
496
496
  filters = []
@@ -498,8 +498,8 @@ class FigureApi(RemoveableBulkModuleApi):
498
498
  filters = [
499
499
  {
500
500
  ApiField.FIELD: ApiField.ENTITY_ID,
501
- "operator": "in",
502
- "value": image_ids,
501
+ ApiField.OPERATOR: "in",
502
+ ApiField.VALUE: image_ids,
503
503
  }
504
504
  ]
505
505
  data = {
@@ -784,3 +784,192 @@ class FigureApi(RemoveableBulkModuleApi):
784
784
 
785
785
  if tasks:
786
786
  await asyncio.gather(*tasks)
787
+
788
+ async def download_async(
789
+ self,
790
+ dataset_id: int,
791
+ image_ids: Optional[List[int]] = None,
792
+ skip_geometry: bool = False,
793
+ semaphore: Optional[asyncio.Semaphore] = None,
794
+ log_progress: bool = True,
795
+ ) -> Dict[int, List[FigureInfo]]:
796
+ """
797
+ Asynchronously download figures for the given dataset ID. Can be filtered by image IDs.
798
+ This method is significantly faster than the synchronous version for large datasets.
799
+
800
+ :param dataset_id: Dataset ID in Supervisely.
801
+ :type dataset_id: int
802
+ :param image_ids: Specify the list of image IDs within the given dataset ID. If image_ids is None, the method returns all possible pairs of images with figures.
803
+ :type image_ids: List[int], optional
804
+ :param skip_geometry: Skip the download of figure geometry. May be useful for a significant api request speed increase in the large datasets.
805
+ :type skip_geometry: bool
806
+ :param semaphore: Semaphore to limit the number of concurrent downloads.
807
+ :type semaphore: Optional[asyncio.Semaphore], optional
808
+ :param log_progress: If True, log the progress of the download.
809
+ :type log_progress: bool, optional
810
+ :return: A dictionary where keys are image IDs and values are lists of figures.
811
+ :rtype: Dict[int, List[FigureInfo]]
812
+
813
+ :Usage example:
814
+
815
+ .. code-block:: python
816
+
817
+ import supervisely as sly
818
+
819
+ os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
820
+ os.environ['API_TOKEN'] = 'Your Supervisely API Token'
821
+ api = sly.Api.from_env()
822
+
823
+ dataset_id = 12345
824
+ download_coroutine = api.image.figure.download_async(dataset_id)
825
+ figures = sly.run_coroutine(download_coroutine)
826
+ """
827
+ fields = [
828
+ ApiField.ID,
829
+ ApiField.CREATED_AT,
830
+ ApiField.UPDATED_AT,
831
+ ApiField.IMAGE_ID,
832
+ ApiField.OBJECT_ID,
833
+ ApiField.CLASS_ID,
834
+ ApiField.PROJECT_ID,
835
+ ApiField.DATASET_ID,
836
+ ApiField.GEOMETRY,
837
+ ApiField.GEOMETRY_TYPE,
838
+ ApiField.GEOMETRY_META,
839
+ ApiField.TAGS,
840
+ ApiField.META,
841
+ ApiField.AREA,
842
+ ApiField.PRIORITY,
843
+ ]
844
+ if skip_geometry is True:
845
+ fields = [x for x in fields if x != ApiField.GEOMETRY]
846
+
847
+ if image_ids is None:
848
+ filters = []
849
+ else:
850
+ filters = [
851
+ {
852
+ ApiField.FIELD: ApiField.ENTITY_ID,
853
+ ApiField.OPERATOR: "in",
854
+ ApiField.VALUE: image_ids,
855
+ }
856
+ ]
857
+
858
+ data = {
859
+ ApiField.DATASET_ID: dataset_id,
860
+ ApiField.FIELDS: fields,
861
+ ApiField.FILTER: filters,
862
+ }
863
+
864
+ # Get first page to determine total pages
865
+ if semaphore is None:
866
+ semaphore = self._api.get_default_semaphore()
867
+ images_figures = defaultdict(list)
868
+ pages_count = None
869
+ total = 0
870
+ tasks = []
871
+
872
+ async def _get_page(page_data, page_num):
873
+ async with semaphore:
874
+ response = await self._api.post_async("figures.list", page_data)
875
+ response_json = response.json()
876
+ nonlocal pages_count, total
877
+ pages_count = response_json["pagesCount"]
878
+ if page_num == 1:
879
+ total = response_json["total"]
880
+
881
+ page_figures = []
882
+ for info in response_json["entities"]:
883
+ figure_info = self._convert_json_info(info, True)
884
+ page_figures.append(figure_info)
885
+ return page_figures
886
+
887
+ # Get first page
888
+ data[ApiField.PAGE] = 1
889
+ first_page_figures = await _get_page(data, 1)
890
+
891
+ if log_progress:
892
+ progress_cb = tqdm(total=total, desc="Downloading figures")
893
+
894
+ for figure in first_page_figures:
895
+ images_figures[figure.entity_id].append(figure)
896
+ if log_progress:
897
+ progress_cb.update(1)
898
+
899
+ # Get rest of the pages in parallel
900
+ if pages_count > 1:
901
+ for page in range(2, pages_count + 1):
902
+ page_data = data.copy()
903
+ page_data[ApiField.PAGE] = page
904
+ tasks.append(asyncio.create_task(_get_page(page_data, page)))
905
+
906
+ for task in asyncio.as_completed(tasks):
907
+ page_figures = await task
908
+ for figure in page_figures:
909
+ images_figures[figure.entity_id].append(figure)
910
+ if log_progress:
911
+ progress_cb.update(1)
912
+
913
+ return dict(images_figures)
914
+
915
+ def download_fast(
916
+ self,
917
+ dataset_id: int,
918
+ image_ids: Optional[List[int]] = None,
919
+ skip_geometry: bool = False,
920
+ semaphore: Optional[asyncio.Semaphore] = None,
921
+ log_progress: bool = True,
922
+ ) -> Dict[int, List[FigureInfo]]:
923
+ """
924
+ Download figures for the given dataset ID. Can be filtered by image IDs.
925
+ This method is significantly faster than the synchronous version for large datasets and
926
+ is designed to be used in an asynchronous context.
927
+ Will fallback to the synchronous version if async fails.
928
+
929
+ :param dataset_id: Dataset ID in Supervisely.
930
+ :type dataset_id: int
931
+ :param image_ids: Specify the list of image IDs within the given dataset ID. If image_ids is None, the method returns all possible pairs of images with figures.
932
+ :type image_ids: List[int], optional
933
+ :param skip_geometry: Skip the download of figure geometry. May be useful for a significant api request speed increase in the large datasets.
934
+ :type skip_geometry: bool
935
+ :param semaphore: Semaphore to limit the number of concurrent downloads.
936
+ :type semaphore: Optional[asyncio.Semaphore], optional
937
+ :param log_progress: If True, log the progress of the download.
938
+ :type log_progress: bool, optional
939
+
940
+ :return: A dictionary where keys are image IDs and values are lists of figures.
941
+ :rtype: Dict[int, List[FigureInfo]]
942
+
943
+ :Usage example:
944
+
945
+ .. code-block:: python
946
+
947
+ import supervisely as sly
948
+
949
+ os.environ['SERVER_ADDRESS'] = 'https://app.supervisely.com'
950
+ os.environ['API_TOKEN'] = 'Your Supervisely API Token'
951
+ api = sly.Api.from_env()
952
+
953
+ dataset_id = 12345
954
+ figures = api.image.figure.download_fast(dataset_id)
955
+ """
956
+ try:
957
+ return run_coroutine(
958
+ self.download_async(
959
+ dataset_id=dataset_id,
960
+ image_ids=image_ids,
961
+ skip_geometry=skip_geometry,
962
+ semaphore=semaphore,
963
+ log_progress=log_progress,
964
+ )
965
+ )
966
+ except Exception:
967
+ # Fallback to the synchronous version if async fails
968
+ logger.debug("Async download of figures is failed, falling back to sync download.")
969
+ if log_progress:
970
+ logger.debug("Downloading figures without progress bar.")
971
+ return self.download(
972
+ dataset_id=dataset_id,
973
+ image_ids=image_ids,
974
+ skip_geometry=skip_geometry,
975
+ )
@@ -70,6 +70,7 @@
70
70
  {% if __webpy_script__ %}
71
71
  <script src="https://cdn.jsdelivr.net/pyodide/{{{pyodide_version}}}/full/pyodide.js"></script>
72
72
  {% endif %}
73
+ <script src="https://cdn.jsdelivr.net/npm/socket.io-client@2.0.4/dist/socket.io.js"></script>
73
74
  <script
74
75
  src="https://cdn.jsdelivr.net/gh/supervisely/js-bundle@{{{js_bundle_version}}}/sly-app-widgets-{{{js_bundle_version}}}.bundle.js"></script>
75
76
  <script type="module"
@@ -257,4 +258,4 @@ if state.get("app_initializing", False) == True:
257
258
  {% endif %}
258
259
  </body>
259
260
 
260
- </html>
261
+ </html>
@@ -14,7 +14,7 @@ from supervisely.app.widgets_context import JinjaWidgets
14
14
  js_bundle_version = "2.1.99"
15
15
 
16
16
  # https://github.com/supervisely-ecosystem/supervisely-app-frontend-js
17
- js_frontend_version = "v0.0.55"
17
+ js_frontend_version = "v0.0.56"
18
18
 
19
19
 
20
20
  pyodide_version = "v0.25.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.348
3
+ Version: 6.73.350
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely
@@ -50,7 +50,7 @@ supervisely/api/video_annotation_tool_api.py,sha256=3A9-U8WJzrTShP_n9T8U01M9FzGY
50
50
  supervisely/api/workspace_api.py,sha256=5KAxpI9DKBmgF_pyQaXHpGT30HZ9wRtR6DP3FoYFZtY,9228
51
51
  supervisely/api/entity_annotation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  supervisely/api/entity_annotation/entity_annotation_api.py,sha256=K79KdDyepQv4FiNQHBj9V4-zLIemxK9WG1ig1bfBKb8,3083
53
- supervisely/api/entity_annotation/figure_api.py,sha256=_v7Nhi63Ksw1OE38ZvFwj9F4O0GNmf96U3gFyGTs4Hs,27776
53
+ supervisely/api/entity_annotation/figure_api.py,sha256=rmsE3L_JfqN94sLN637pQ0syiBAXPd8RyAwhl41j1xs,35318
54
54
  supervisely/api/entity_annotation/object_api.py,sha256=gbcNvN_KY6G80Me8fHKQgryc2Co7VU_kfFd1GYILZ4E,8875
55
55
  supervisely/api/entity_annotation/tag_api.py,sha256=M-28m9h8R4k9Eqo6P1S0UH8_D5kqCwAvQLYY6_Yz4oM,11161
56
56
  supervisely/api/pointcloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -90,12 +90,12 @@ supervisely/app/development/sly-net.sh,sha256=-EVburj4NtS7Dw4OiwXXolYu9SXvui83F-
90
90
  supervisely/app/fastapi/__init__.py,sha256=kNhkaGuBKn9-GNnPOmikIHqhjL-j66xmZaBbjCmXfAo,460
91
91
  supervisely/app/fastapi/custom_static_files.py,sha256=5todaVIvUG9sAt6vu1IujJn8N7zTmFhVUfeCVbuXbvc,3391
92
92
  supervisely/app/fastapi/dialog_window.html,sha256=ffaAxjK0TQRa7RrY5oA4uE6RzFuS0VnRG1pfoIzTqVM,1183
93
- supervisely/app/fastapi/index.html,sha256=6K8akK7_k9Au-BpZ7cM2qocuiegLdXz8UFPnWg8TepI,7987
93
+ supervisely/app/fastapi/index.html,sha256=ympsbjKHauNbCqDNAb-hjFM6UnOMRJf2VYEkSQFYpPk,8084
94
94
  supervisely/app/fastapi/no_html_main.html,sha256=NhQP7noyORBx72lFh1CQKgBRupkWjiq6Gaw-9Hkvg7c,37
95
95
  supervisely/app/fastapi/offline.py,sha256=CwMMkJ1frD6wiZS-SEoNDtQ1UJcJe1Ob6ohE3r4CQL8,7414
96
96
  supervisely/app/fastapi/request.py,sha256=NU7rKmxJ1pfkDZ7_yHckRcRAueJRQIqCor11UO2OHr8,766
97
97
  supervisely/app/fastapi/subapp.py,sha256=5lMfFLYBfHzE1OmITHsogB9hScyTJFjGV45AKY67Hkg,45647
98
- supervisely/app/fastapi/templating.py,sha256=3X69vo-_RO2tlaVz5obifXjrNEIHVSNVQFNm_xjOMe4,2929
98
+ supervisely/app/fastapi/templating.py,sha256=pcghBW2OWVrNtplZuYa-mx04ektLiSvnBg-mhmyCoJc,2929
99
99
  supervisely/app/fastapi/utils.py,sha256=t_UquzlFrdkKtAJmH6eJ279pE8Aa3BaIu4XjX-SEaIE,946
100
100
  supervisely/app/fastapi/websocket.py,sha256=TlRSPOAhRItTv1HGvdukK1ZvhRjMUxRa-lJlsRR9rJw,1308
101
101
  supervisely/app/v1/__init__.py,sha256=OdU0PYv6hLwahYoyaLFO8m3cbJSchvPbqxuG1N3T734,848
@@ -1083,9 +1083,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
1083
1083
  supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
1084
1084
  supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
1085
1085
  supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
1086
- supervisely-6.73.348.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1087
- supervisely-6.73.348.dist-info/METADATA,sha256=Heozuyuf1zukvQzantkbHHyMXh1S4WnVFvWm0p4JIE8,33596
1088
- supervisely-6.73.348.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1089
- supervisely-6.73.348.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1090
- supervisely-6.73.348.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1091
- supervisely-6.73.348.dist-info/RECORD,,
1086
+ supervisely-6.73.350.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1087
+ supervisely-6.73.350.dist-info/METADATA,sha256=2hZYrEUmL3RyDgh_rG9RU5RdHEfNB7aRGMhQysvHAk4,33596
1088
+ supervisely-6.73.350.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1089
+ supervisely-6.73.350.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1090
+ supervisely-6.73.350.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1091
+ supervisely-6.73.350.dist-info/RECORD,,