anatools 6.0.0__tar.gz → 6.0.2__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 (166) hide show
  1. {anatools-6.0.0/anatools.egg-info → anatools-6.0.2}/PKG-INFO +2 -2
  2. {anatools-6.0.0 → anatools-6.0.2}/anatools/__init__.py +1 -1
  3. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/api.py +5 -1
  4. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/datasets.py +21 -3
  5. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/preview.py +24 -3
  6. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/datasets.py +61 -1
  7. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/preview.py +26 -4
  8. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/annotations.py +39 -18
  9. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/draw.py +34 -18
  10. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anadeploy +3 -1
  11. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anarules +6 -4
  12. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/renderedai +1513 -46
  13. {anatools-6.0.0 → anatools-6.0.2/anatools.egg-info}/PKG-INFO +2 -2
  14. {anatools-6.0.0 → anatools-6.0.2}/anatools.egg-info/requires.txt +1 -1
  15. anatools-6.0.2/requirements.txt +6 -0
  16. anatools-6.0.0/requirements.txt +0 -6
  17. {anatools-6.0.0 → anatools-6.0.2}/LICENSE +0 -0
  18. {anatools-6.0.0 → anatools-6.0.2}/MANIFEST.in +0 -0
  19. {anatools-6.0.0 → anatools-6.0.2}/README.md +0 -0
  20. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/__init__.py +0 -0
  21. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/_menu.py +0 -0
  22. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/agents.py +0 -0
  23. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/anaclient.py +0 -0
  24. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/analytics.py +0 -0
  25. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/annotations.py +0 -0
  26. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/__init__.py +0 -0
  27. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/analytics.py +0 -0
  28. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/annotations.py +0 -0
  29. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/api_keys.py +0 -0
  30. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/channels.py +0 -0
  31. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/editor.py +0 -0
  32. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/gan.py +0 -0
  33. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/graphs.py +0 -0
  34. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/handlers.py +0 -0
  35. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/image.py +0 -0
  36. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/inpaint.py +0 -0
  37. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/introspection.py +0 -0
  38. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/llm.py +0 -0
  39. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/members.py +0 -0
  40. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/ml.py +0 -0
  41. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/organizations.py +0 -0
  42. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/rules.py +0 -0
  43. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/services.py +0 -0
  44. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/umap.py +0 -0
  45. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/volumes.py +0 -0
  46. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api/workspaces.py +0 -0
  47. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/api_keys.py +0 -0
  48. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/channels.py +0 -0
  49. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/editor.py +0 -0
  50. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/gan.py +0 -0
  51. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/graphs.py +0 -0
  52. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/helpers.py +0 -0
  53. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/image.py +0 -0
  54. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/inpaint.py +0 -0
  55. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/llm.py +0 -0
  56. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/ml.py +0 -0
  57. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/organizations.py +0 -0
  58. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/rules.py +0 -0
  59. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/services.py +0 -0
  60. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/__init__.py +0 -0
  61. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/agents_test.py +0 -0
  62. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/anaclient_test.py +0 -0
  63. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/analytics_test.py +0 -0
  64. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/annotations_test.py +0 -0
  65. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/api_keys_test.py +0 -0
  66. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/channels_test.py +0 -0
  67. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/datasets_test.py +0 -0
  68. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/editor_test.py +0 -0
  69. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/gan_test.py +0 -0
  70. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/graphs_test.py +0 -0
  71. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/image_test.py +0 -0
  72. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/inpaint_test.py +0 -0
  73. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/llm_test.py +0 -0
  74. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/ml_test.py +0 -0
  75. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/organizations_test.py +0 -0
  76. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/preview_test.py +0 -0
  77. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/umap_test.py +0 -0
  78. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/volumes_test.py +0 -0
  79. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/tests/workspaces_test.py +0 -0
  80. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/umap.py +0 -0
  81. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/volumes.py +0 -0
  82. {anatools-6.0.0 → anatools-6.0.2}/anatools/anaclient/workspaces.py +0 -0
  83. {anatools-6.0.0 → anatools-6.0.2}/anatools/anacreate.py +0 -0
  84. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/__init__.py +0 -0
  85. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_classification.py +0 -0
  86. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_coco.py +0 -0
  87. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_geococo.py +0 -0
  88. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_kitti.py +0 -0
  89. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_pascal.py +0 -0
  90. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_sagemaker.py +0 -0
  91. {anatools-6.0.0 → anatools-6.0.2}/anatools/annotations/convert_yolo.py +0 -0
  92. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/ana +0 -0
  93. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anamount +0 -0
  94. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anaprofile +0 -0
  95. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anaserver +0 -0
  96. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anatransfer +0 -0
  97. {anatools-6.0.0 → anatools-6.0.2}/anatools/bin/anautils +0 -0
  98. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/ConditionalSelector.md +0 -0
  99. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/RandomChoice.md +0 -0
  100. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/RandomNormal.md +0 -0
  101. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/RandomRandint.md +0 -0
  102. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/RandomTriangular.md +0 -0
  103. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/RandomUniform.md +0 -0
  104. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/SelectGenerator.md +0 -0
  105. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/SetInstanceCount.md +0 -0
  106. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/String.md +0 -0
  107. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/SweepArange.md +0 -0
  108. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/SweepLinspace.md +0 -0
  109. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/Value.md +0 -0
  110. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/Vector2D.md +0 -0
  111. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/Vector3D.md +0 -0
  112. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/Weight.md +0 -0
  113. {anatools-6.0.0 → anatools-6.0.2}/anatools/docs/__init__.py +0 -0
  114. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/__init__.py +0 -0
  115. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/ana_object.py +0 -0
  116. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/bbox.py +0 -0
  117. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/blender_main.py +0 -0
  118. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/camera_checks.py +0 -0
  119. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/channel.py +0 -0
  120. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/context.py +0 -0
  121. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/directory_object.py +0 -0
  122. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/download.py +0 -0
  123. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/file_handlers.py +0 -0
  124. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/file_metadata.py +0 -0
  125. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/file_object.py +0 -0
  126. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/generator.py +0 -0
  127. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/interp.py +0 -0
  128. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/load.py +0 -0
  129. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/mount.py +0 -0
  130. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/node.py +0 -0
  131. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/object_utils.py +0 -0
  132. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/package_utils.py +0 -0
  133. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/print.py +0 -0
  134. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/python_main.py +0 -0
  135. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/rigged_object.py +0 -0
  136. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/scene.py +0 -0
  137. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/search_utils.py +0 -0
  138. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/service.py +0 -0
  139. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/tools.py +0 -0
  140. {anatools-6.0.0 → anatools-6.0.2}/anatools/lib/transfer.py +0 -0
  141. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/__init__.py +0 -0
  142. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/constants.py +0 -0
  143. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/constants.yml +0 -0
  144. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/generators.py +0 -0
  145. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/generators.yml +0 -0
  146. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/logic.py +0 -0
  147. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/logic.yml +0 -0
  148. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/random_generator.py +0 -0
  149. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/random_generator.yml +0 -0
  150. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/sweep_arange.py +0 -0
  151. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/sweep_arange.yml +0 -0
  152. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/sweep_linspace.py +0 -0
  153. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/sweep_linspace.yml +0 -0
  154. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/vectors.py +0 -0
  155. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/vectors.yml +0 -0
  156. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/volume_directory.py +0 -0
  157. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/volume_directory.yml +0 -0
  158. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/volume_file.py +0 -0
  159. {anatools-6.0.0 → anatools-6.0.2}/anatools/nodes/volume_file.yml +0 -0
  160. {anatools-6.0.0 → anatools-6.0.2}/anatools.egg-info/SOURCES.txt +0 -0
  161. {anatools-6.0.0 → anatools-6.0.2}/anatools.egg-info/dependency_links.txt +0 -0
  162. {anatools-6.0.0 → anatools-6.0.2}/anatools.egg-info/entry_points.txt +0 -0
  163. {anatools-6.0.0 → anatools-6.0.2}/anatools.egg-info/top_level.txt +0 -0
  164. {anatools-6.0.0 → anatools-6.0.2}/pyproject.toml +0 -0
  165. {anatools-6.0.0 → anatools-6.0.2}/setup.cfg +0 -0
  166. {anatools-6.0.0 → anatools-6.0.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anatools
3
- Version: 6.0.0
3
+ Version: 6.0.2
4
4
  Summary: Tools for development with the Rendered.ai Platform.
5
5
  Home-page: https://rendered.ai
6
6
  Author: Rendered AI, Inc
@@ -14,7 +14,7 @@ Requires-Dist: docker
14
14
  Requires-Dist: numpy
15
15
  Requires-Dist: pillow
16
16
  Requires-Dist: pyyaml
17
- Requires-Dist: requests
17
+ Requires-Dist: requests<3.0.0,>=2.32.3
18
18
  Requires-Dist: colorama
19
19
  Dynamic: author
20
20
  Dynamic: author-email
@@ -16,4 +16,4 @@ View the `Introduction to Rendered.ai Documentation`_ to learn more.
16
16
  from .anaclient.anaclient import client
17
17
  from .annotations.annotations import annotations
18
18
 
19
- __version__ = '6.0.0'
19
+ __version__ = '6.0.2'
@@ -117,8 +117,12 @@ class api:
117
117
  responsedata = response.json()
118
118
  if self.verbose == 'debug': print(responsedata)
119
119
  try:
120
+ # Check for errors FIRST, even if data exists (partial response scenario in GraphQL)
121
+ # This ensures errors like "The Datasets limit has been reached by this workspace"
122
+ # are properly raised instead of being silently ignored when data is also present
123
+ if 'errors' in responsedata and responsedata['errors']:
124
+ raise Exception(responsedata['errors'][-1]['message'])
120
125
  if 'data' in responsedata and responsedata['data'] is not None and call in responsedata['data']: return responsedata['data'][call]
121
- elif 'errors' in responsedata: raise Exception(responsedata['errors'][-1]['message'])
122
126
  else: raise Exception()
123
127
  except Exception as e:
124
128
  raise Exception(f'There was an issue with the {call} API call: {e}')
@@ -118,21 +118,39 @@ def editDataset(self, workspaceId, datasetId, name=None, description=None, pause
118
118
 
119
119
  def downloadDataset(self, workspaceId, datasetId):
120
120
  response = self.session.post(
121
- url = self.url,
122
- headers = self.headers,
121
+ url = self.url,
122
+ headers = self.headers,
123
123
  json = {
124
124
  "operationName": "downloadDataset",
125
125
  "variables": {
126
126
  "workspaceId": workspaceId,
127
127
  "datasetId": datasetId
128
128
  },
129
- "query": """mutation
129
+ "query": """mutation
130
130
  downloadDataset($workspaceId: String!, $datasetId: String!) {
131
131
  downloadDataset(workspaceId: $workspaceId, datasetId: $datasetId)
132
132
  }"""})
133
133
  return self.errorhandler(response, "downloadDataset")
134
134
 
135
135
 
136
+ def downloadDatasetFile(self, workspaceId, datasetId, filepath):
137
+ response = self.session.post(
138
+ url = self.url,
139
+ headers = self.headers,
140
+ json = {
141
+ "operationName": "downloadDatasetFile",
142
+ "variables": {
143
+ "workspaceId": workspaceId,
144
+ "datasetId": datasetId,
145
+ "filepath": filepath
146
+ },
147
+ "query": """mutation
148
+ downloadDatasetFile($workspaceId: String!, $datasetId: String!, $filepath: String!) {
149
+ downloadDatasetFile(workspaceId: $workspaceId, datasetId: $datasetId, filepath: $filepath)
150
+ }"""})
151
+ return self.errorhandler(response, "downloadDatasetFile")
152
+
153
+
136
154
  def cancelDataset(self, workspaceId, datasetId):
137
155
  response = self.session.post(
138
156
  url = self.url,
@@ -25,17 +25,38 @@ def getPreview(self, workspaceId, previewId, fields=None):
25
25
 
26
26
  def createPreview(self, workspaceId, graphId):
27
27
  response = self.session.post(
28
- url = self.url,
29
- headers = self.headers,
28
+ url = self.url,
29
+ headers = self.headers,
30
30
  json = {
31
31
  "operationName": "createPreview",
32
32
  "variables": {
33
33
  "workspaceId": workspaceId,
34
34
  "graphId": graphId
35
35
  },
36
- "query": """mutation
36
+ "query": """mutation
37
37
  createPreview($workspaceId: String!, $graphId: String!) {
38
38
  createPreview(workspaceId: $workspaceId, graphId: $graphId)
39
39
  }"""})
40
40
  return self.errorhandler(response, "createPreview")
41
41
 
42
+
43
+ def getPreviewLog(self, workspaceId, previewId, fields=None):
44
+ if fields is None: fields = self.getTypeFields("PreviewLog")
45
+ fields = "\n".join(fields)
46
+ response = self.session.post(
47
+ url = self.url,
48
+ headers = self.headers,
49
+ json = {
50
+ "operationName": "getPreviewLog",
51
+ "variables": {
52
+ "workspaceId": workspaceId,
53
+ "previewId": previewId
54
+ },
55
+ "query": f"""query
56
+ getPreviewLog($workspaceId: String!, $previewId: String!) {{
57
+ getPreviewLog(workspaceId: $workspaceId, previewId: $previewId) {{
58
+ {fields}
59
+ }}
60
+ }}"""})
61
+ return self.errorhandler(response, "getPreviewLog")
62
+
@@ -219,7 +219,67 @@ def download_dataset(self, datasetId, workspaceId=None, localDir=None):
219
219
  raise Exception(f"Dataset '{dataset_name}' is not available for download (status: {dataset_status}). The dataset may still be processing or may have failed.")
220
220
 
221
221
  fname = dataset_name + '.zip'
222
- return download_file(url=url, fname=fname, localDir=localDir)
222
+ return download_file(url=url, fname=fname, localDir=localDir)
223
+
224
+
225
+ def download_dataset_file(self, datasetId, filepath, workspaceId=None, localDir=None):
226
+ """Download a single file from a dataset.
227
+
228
+ This allows downloading individual files from a dataset rather than
229
+ downloading the entire dataset archive. Use get_dataset_files() to
230
+ list available files in a dataset.
231
+
232
+ Parameters
233
+ ----------
234
+ datasetId : str
235
+ Dataset ID of the dataset containing the file.
236
+ filepath : str
237
+ Relative path to the file within the dataset (e.g., "images/000000-1-image.png").
238
+ workspaceId : str
239
+ Workspace ID that the dataset is in. If none is provided, the default workspace will get used.
240
+ localDir : str
241
+ Path for where to download the file. If none is provided, current working directory will be used.
242
+
243
+ Returns
244
+ -------
245
+ str
246
+ Returns the path the file was downloaded to.
247
+
248
+ Raises
249
+ ------
250
+ ValueError
251
+ If datasetId or filepath is not provided.
252
+ Exception
253
+ If the file cannot be downloaded (e.g., not found).
254
+
255
+ Examples
256
+ --------
257
+ >>> # First, list available files
258
+ >>> files = ana.get_dataset_files(datasetId='abc123', path='images')
259
+ >>> print(files)
260
+ ['000000-1-image.png', '000001-1-image.png', ...]
261
+ >>>
262
+ >>> # Then download a specific file
263
+ >>> path = ana.download_dataset_file(datasetId='abc123', filepath='images/000000-1-image.png')
264
+ >>> print(path)
265
+ '/home/user/000000-1-image.png'
266
+ """
267
+ import os
268
+ from anatools.lib.download import download_file
269
+
270
+ self.check_logout()
271
+ if datasetId is None: raise ValueError("The datasetId parameter is required!")
272
+ if filepath is None or filepath.strip() == '': raise ValueError("The filepath parameter is required!")
273
+ if workspaceId is None: workspaceId = self.workspace
274
+
275
+ url = self.ana_api.downloadDatasetFile(workspaceId=workspaceId, datasetId=datasetId, filepath=filepath)
276
+
277
+ if url is None:
278
+ raise Exception(f"File '{filepath}' not found in dataset '{datasetId}'.")
279
+
280
+ # Extract the filename from the filepath
281
+ fname = os.path.basename(filepath)
282
+ return download_file(url=url, fname=fname, localDir=localDir)
223
283
 
224
284
 
225
285
  def cancel_dataset(self, datasetId, workspaceId=None):
@@ -27,14 +27,14 @@ def get_preview(self, previewId, workspaceId=None, fields=None):
27
27
 
28
28
  def create_preview(self, graphId, workspaceId=None):
29
29
  """Creates a preview job.
30
-
30
+
31
31
  Parameters
32
32
  ----------
33
33
  graphId: str
34
34
  The unique identifier for the graph.
35
- workspaceId : str
36
- Workspace ID create the preview in. If none is provided, the default workspace will get used.
37
-
35
+ workspaceId : str
36
+ Workspace ID create the preview in. If none is provided, the default workspace will get used.
37
+
38
38
  Returns
39
39
  -------
40
40
  str
@@ -43,4 +43,26 @@ def create_preview(self, graphId, workspaceId=None):
43
43
  if self.check_logout(): return
44
44
  if workspaceId is None: workspaceId = self.workspace
45
45
  return self.ana_api.createPreview(workspaceId=workspaceId, graphId=graphId)
46
+
47
+
48
+ def get_preview_log(self, previewId, workspaceId=None, fields=None):
49
+ """Fetches the logs for a preview job.
50
+
51
+ Parameters
52
+ ----------
53
+ previewId : str
54
+ The unique identifier for the preview job.
55
+ workspaceId : str
56
+ Workspace the preview job was run in. If none is provided, the default workspace will get used.
57
+ fields : list
58
+ List of fields to return, leave empty to get all fields.
59
+
60
+ Returns
61
+ -------
62
+ dict
63
+ Preview log information containing previewId, status, and logs.
64
+ """
65
+ if self.check_logout(): return
66
+ if workspaceId is None: workspaceId = self.workspace
67
+ return self.ana_api.getPreviewLog(workspaceId=workspaceId, previewId=previewId, fields=fields)
46
68
 
@@ -14,15 +14,15 @@ class annotations:
14
14
  Examples of mapfiles are in the example channel at /ana/channels/example/mapfiles/.
15
15
  """
16
16
 
17
- def bounding_box_2d(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None):
17
+ def bounding_box_2d(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None, quiet=False):
18
18
  """
19
- Generates images annotated with 2d bounding boxes for datasets downloaded from the Platform. Optional filter on
19
+ Generates images annotated with 2d bounding boxes for datasets downloaded from the Platform. Optional filter on
20
20
  object_ids or object_types (must choose a single filter).
21
-
21
+
22
22
  Parameters
23
23
  ----------
24
24
  image_path : str
25
- Path to of specific image file to draw the boxes for.
25
+ Path to of specific image file to draw the boxes for.
26
26
  out_dir : str
27
27
  File path to directory where the image should be saved to.
28
28
  object_ids : list[int]
@@ -30,22 +30,29 @@ class annotations:
30
30
  object_types: list[str]
31
31
  Filter for the object types to annotate. If not provided, all object types will get annotated. Choose either id or type filter.
32
32
  line_thickness: int
33
- Desired line thickness for box outline.
34
- colors: dict
33
+ Desired line thickness for box outline.
34
+ colors: dict
35
35
  Dictionary of colors to use for each object type where each color is a tuple of 3 integers (R, G, B). For example: colors={'Car': (255, 0, 0)}
36
+ quiet: bool
37
+ If True, suppress print output. Useful for CLI/automation.
38
+
39
+ Returns
40
+ -------
41
+ str or None
42
+ Path to the saved annotated image, or None if an error occurred.
36
43
  """
37
- draw(image_path, out_dir, draw_type='box_2d', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors)
44
+ return draw(image_path, out_dir, draw_type='box_2d', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors, quiet=quiet)
38
45
 
39
46
 
40
- def bounding_box_3d(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None):
47
+ def bounding_box_3d(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None, quiet=False):
41
48
  """
42
- Generates images annotated with 3d bounding boxes for datasets downloaded from the Platform. Optional filter on
49
+ Generates images annotated with 3d bounding boxes for datasets downloaded from the Platform. Optional filter on
43
50
  object_ids or object_types (must choose a single filter).
44
-
51
+
45
52
  Parameters
46
53
  ----------
47
54
  image_path : str
48
- Path to of specific image file to draw the boxes for.
55
+ Path to of specific image file to draw the boxes for.
49
56
  out_dir : str
50
57
  File path to directory where the image should be saved to.
51
58
  object_ids : list[int]
@@ -53,22 +60,29 @@ class annotations:
53
60
  object_types: list[str]
54
61
  Filter for the object types to annotate. If not provided, all object types will get annotated. Choose either id or type filter.
55
62
  line_thickness: int
56
- Desired line thickness for box outline.
63
+ Desired line thickness for box outline.
57
64
  colors: dict
58
65
  Dictionary of colors to use for each object type where each color is a tuple of 3 integers (R, G, B). For example: colors={'Car': (255, 0, 0)}
66
+ quiet: bool
67
+ If True, suppress print output. Useful for CLI/automation.
68
+
69
+ Returns
70
+ -------
71
+ str or None
72
+ Path to the saved annotated image, or None if an error occurred.
59
73
  """
60
- draw(image_path, out_dir, draw_type='box_3d', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors)
74
+ return draw(image_path, out_dir, draw_type='box_3d', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors, quiet=quiet)
61
75
 
62
76
 
63
- def segmentation(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None):
77
+ def segmentation(self, image_path, out_dir, object_ids=None, object_types=None, line_thickness=1, colors=None, quiet=False):
64
78
  """
65
- Generates images annotated with outlines around objects for datasets downloaded from the Platform. Optional filter on
79
+ Generates images annotated with outlines around objects for datasets downloaded from the Platform. Optional filter on
66
80
  object_ids or object_types (must choose a single filter).
67
-
81
+
68
82
  Parameters
69
83
  ----------
70
84
  image_path : str
71
- Path to of specific image file to draw the boxes for.
85
+ Path to of specific image file to draw the boxes for.
72
86
  out_dir : str
73
87
  File path to directory where the image should be saved to.
74
88
  object_ids : list[int]
@@ -79,8 +93,15 @@ class annotations:
79
93
  Desired line thickness for object segmentation outline.
80
94
  colors: dict
81
95
  Dictionary of colors to use for each object type where each color is a tuple of 3 integers (R, G, B). For example: colors={'Car': (255, 0, 0)}
96
+ quiet: bool
97
+ If True, suppress print output. Useful for CLI/automation.
98
+
99
+ Returns
100
+ -------
101
+ str or None
102
+ Path to the saved annotated image, or None if an error occurred.
82
103
  """
83
- draw(image_path, out_dir, draw_type='segmentation', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors)
104
+ return draw(image_path, out_dir, draw_type='segmentation', object_ids=object_ids, object_types=object_types, line_thickness=line_thickness, colors=colors, quiet=quiet)
84
105
 
85
106
 
86
107
  def dump_classification(self, datadir, outdir, mapfile=None):
@@ -4,14 +4,14 @@ import json
4
4
  import os
5
5
 
6
6
 
7
- def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=None, line_thickness=1, colors=None):
7
+ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=None, line_thickness=1, colors=None, quiet=False):
8
8
  """
9
9
  This function handles the io and draws the right type of annotation on the image.
10
10
 
11
11
  Parameters
12
12
  ----------
13
13
  image_path : str
14
- Path to of specific image file to draw the boxes for.
14
+ Path to of specific image file to draw the boxes for.
15
15
  out_dir : str
16
16
  File path to directory where the image should be saved to.
17
17
  draw_type : str
@@ -24,15 +24,24 @@ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=
24
24
  Desired line thickness for box outline.
25
25
  colors: dict
26
26
  Dictionary of colors to use for each object type. Must provide a color for each object type specified in the filter.
27
+ quiet: bool
28
+ If True, suppress print output. Useful for CLI/automation.
29
+
30
+ Returns
31
+ -------
32
+ str or None
33
+ Path to the saved annotated image, or None if an error occurred.
27
34
  """
28
35
 
29
36
  if not os.path.exists(image_path):
30
- print('Incorrect path to images: ' + image_path)
31
- return
37
+ if not quiet:
38
+ print('Incorrect path to images: ' + image_path)
39
+ return None
32
40
 
33
41
  if object_types and object_ids:
34
- print('Provide either object_types OR object_ids. ')
35
- return
42
+ if not quiet:
43
+ print('Provide either object_types OR object_ids. ')
44
+ return None
36
45
 
37
46
  if not os.path.exists(out_dir):
38
47
  os.makedirs(out_dir)
@@ -47,8 +56,8 @@ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=
47
56
  file.close()
48
57
 
49
58
  annotation_ids = [data['id'] for data in annotations['annotations']]
50
- if object_ids is not None and not check_lists(annotation_ids, object_ids, 'object_ids'):
51
- return
59
+ if object_ids is not None and not check_lists(annotation_ids, object_ids, 'object_ids', quiet=quiet):
60
+ return None
52
61
 
53
62
 
54
63
  metadata_file = root_dir+'/metadata/'+image_name+'-metadata.json'
@@ -62,8 +71,8 @@ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=
62
71
  if object_ids:
63
72
  object_data = [data for data in metadata['objects'] if data['id'] in object_ids]
64
73
  elif object_types is not None:
65
- if not check_lists(metadata_types, object_types, 'object_types'):
66
- return
74
+ if not check_lists(metadata_types, object_types, 'object_types', quiet=quiet):
75
+ return None
67
76
  object_data = [data for data in metadata['objects'] if data['type'] in object_types]
68
77
  else:
69
78
  object_data = [data for data in metadata['objects']]
@@ -74,14 +83,15 @@ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=
74
83
  generate_color = [int(val) for val in np.random.randint(0, 255, 3)]
75
84
  type_colors[type] = tuple(generate_color)
76
85
  else: type_colors = colors
77
-
86
+
78
87
 
79
88
  draw_img = Image.open(image_path)
80
89
  for object in annotations['annotations']:
81
90
  if any(object['id'] == d['id'] for d in object_data):
82
91
  color_info = [data for data in object_data if object['id'] == data['id']]
83
92
  if color_info[0]['type'] not in type_colors:
84
- print(f'Color for {color_info[0]["type"]} not found in colors dictionary. Skipping object.')
93
+ if not quiet:
94
+ print(f'Color for {color_info[0]["type"]} not found in colors dictionary. Skipping object.')
85
95
  continue
86
96
  if draw_type == 'box_2d':
87
97
  draw_img = box_2d(object['bbox'], draw_img, line_thickness, type_colors[color_info[0]['type']])
@@ -90,11 +100,14 @@ def draw(image_path, out_dir, draw_type='box_2d', object_ids=None, object_types=
90
100
  elif draw_type == 'segmentation':
91
101
  draw_img = segmentation(object['segmentation'], draw_img, line_thickness, type_colors[color_info[0]['type']])
92
102
  else:
93
- print('Provide either box_2d, box_3d, or segmentation')
103
+ if not quiet:
104
+ print('Provide either box_2d, box_3d, or segmentation')
94
105
 
95
106
  outimg = out_dir+'/'+image_name+'-annotated-'+draw_type+'.'+image_ext
96
107
  draw_img.save(outimg)
97
- print(f'Image saved to {outimg}')
108
+ if not quiet:
109
+ print(f'Image saved to {outimg}')
110
+ return outimg
98
111
 
99
112
 
100
113
  def box_2d(coordinates, bbox_img, width, outline):
@@ -193,7 +206,7 @@ def segmentation(coordinates, draw_img, width, color):
193
206
  return draw_img
194
207
 
195
208
 
196
- def check_lists(actual, expected, name_to_check):
209
+ def check_lists(actual, expected, name_to_check, quiet=False):
197
210
  """
198
211
  Helper function that checks if one list is in another for validation on draw inputs.
199
212
 
@@ -205,16 +218,19 @@ def check_lists(actual, expected, name_to_check):
205
218
  The expected list that is provided from the user.
206
219
  name_to_check: str
207
220
  Name of parameter to check (either object_id or object_type).
221
+ quiet: bool
222
+ If True, suppress print output.
208
223
 
209
224
  Returns
210
225
  -------
211
226
  bool
212
227
  True if lists are matching, False otherwise.
213
228
  """
214
-
229
+
215
230
  if not set(expected).issubset(set(actual)):
216
231
  out_of_bounds_check= list(set(expected) - set(actual))
217
- print(f'Provided {name_to_check} list has the following out of bounds: {out_of_bounds_check}. Please rerun with valid list. \nHere are all the {name_to_check} that can get annotated: ')
218
- print(list(set(actual)))
232
+ if not quiet:
233
+ print(f'Provided {name_to_check} list has the following out of bounds: {out_of_bounds_check}. Please rerun with valid list. \nHere are all the {name_to_check} that can get annotated: ')
234
+ print(list(set(actual)))
219
235
  return False
220
236
  return True
@@ -347,6 +347,7 @@ try:
347
347
  if channels == False or len(channels) == 0: raise Exception(f"Channel {args.channelId} not found")
348
348
  remotechannel = channels[0]
349
349
  starttime = time.time()
350
+ client.interactive = True
350
351
  deploymentId = client.deploy_channel(channelId=remotechannel['channelId'], channelfile=args.channel)
351
352
  print('Registering Channel Image...', flush=True)
352
353
  registerstart = time.time()
@@ -512,6 +513,7 @@ try:
512
513
  client.edit_service(serviceId=remoteservice['serviceId'], volumes=volumes)
513
514
  remoteservice = client.get_services(serviceId=remoteservice['serviceId'])[0]
514
515
  starttime = time.time()
516
+ client.interactive = True
515
517
  deploymentId = client.deploy_service(serviceId=remoteservice['serviceId'], servicefile=args.service)
516
518
  print('Registering Service Image...', flush=True)
517
519
  registerstart = time.time()
@@ -521,7 +523,7 @@ try:
521
523
  print(f'\033[1F\033[FRegistering Service Image...done. [{time.time()-registerstart:.3f}s]\033[K\n\033[K')
522
524
  print_color(f"The service has been deployed and is ready to use!", color='brand')
523
525
  print(f'Deployment Time: {time.time()-starttime:.3f}s\n')
524
- client.edit_service(serviceId=remoteservice['serviceId'], schema=json.dumps(localservice.schemas))
526
+ client.edit_service(serviceId=remoteservice['serviceId'], description=localservice.description, schema=json.dumps(localservice.schemas))
525
527
 
526
528
  remoteconfig = {
527
529
  "serviceId": remoteservice['serviceId'],
@@ -107,10 +107,12 @@ if args.workspace is not None:
107
107
  try:
108
108
  schema = json.loads(service['schema'])
109
109
  if schema and isinstance(schema, dict):
110
- for tool in schema.keys():
111
- description = "No description"
112
- if isinstance(schema[tool], dict) and 'description' in schema[tool]: description = schema[tool]['description']
113
- rules+=f"\t\t- {tool} : {description}\n"
110
+ tools = schema.get('tools', schema.get('execs', schema))
111
+ if isinstance(tools, dict):
112
+ for tool in tools.keys():
113
+ description = "No description"
114
+ if isinstance(tools[tool], dict) and 'description' in tools[tool]: description = tools[tool]['description']
115
+ rules+=f"\t\t- {tool} : {description}\n"
114
116
  except (json.JSONDecodeError, TypeError) as e:
115
117
  if args.verbose: print(f"Warning: Failed to parse schema for service {service['name']}: {e}")
116
118
  pass