Ayon-Python-Api 0.3.1__tar.gz → 0.3.3__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 (24) hide show
  1. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/PKG-INFO +1 -1
  2. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/__init__.py +9 -0
  3. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/_api.py +30 -34
  4. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/server_api.py +196 -35
  5. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/utils.py +151 -8
  6. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/version.py +1 -1
  7. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_python_api.egg-info/PKG-INFO +1 -1
  8. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/pyproject.toml +1 -1
  9. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/LICENSE +0 -0
  10. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/README.md +0 -0
  11. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/constants.py +0 -0
  12. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/entity_hub.py +0 -0
  13. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/events.py +0 -0
  14. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/exceptions.py +0 -0
  15. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/graphql.py +0 -0
  16. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/graphql_queries.py +0 -0
  17. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/operations.py +0 -0
  18. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_api/thumbnails.py +0 -0
  19. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  20. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  21. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_python_api.egg-info/requires.txt +0 -0
  22. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/ayon_python_api.egg-info/top_level.txt +0 -0
  23. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/setup.cfg +0 -0
  24. {ayon-python-api-0.3.1 → ayon-python-api-0.3.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -1,6 +1,8 @@
1
+ from .version import __version__
1
2
  from .utils import (
2
3
  TransferProgress,
3
4
  slugify_string,
5
+ create_dependency_package_basename,
4
6
  )
5
7
  from .server_api import (
6
8
  ServerAPI,
@@ -76,6 +78,8 @@ from ._api import (
76
78
  download_dependency_package,
77
79
  upload_dependency_package,
78
80
 
81
+ upload_addon_zip,
82
+
79
83
  get_bundles,
80
84
  create_bundle,
81
85
  update_bundle,
@@ -183,8 +187,11 @@ from ._api import (
183
187
 
184
188
 
185
189
  __all__ = (
190
+ "__version__",
191
+
186
192
  "TransferProgress",
187
193
  "slugify_string",
194
+ "create_dependency_package_basename",
188
195
 
189
196
  "ServerAPI",
190
197
 
@@ -257,6 +264,8 @@ __all__ = (
257
264
  "download_dependency_package",
258
265
  "upload_dependency_package",
259
266
 
267
+ "upload_addon_zip",
268
+
260
269
  "get_bundles",
261
270
  "create_bundle",
262
271
  "update_bundle",
@@ -25,12 +25,29 @@ class GlobalServerAPI(ServerAPI):
25
25
  but that can be filled afterwards with calling 'login' method.
26
26
  """
27
27
 
28
- def __init__(self, site_id=None, client_version=None):
28
+ def __init__(
29
+ self,
30
+ site_id=None,
31
+ client_version=None,
32
+ default_settings_variant=None,
33
+ ssl_verify=None,
34
+ cert=None,
35
+ ):
29
36
  url = self.get_url()
30
37
  token = self.get_token()
31
38
 
32
- super(GlobalServerAPI, self).__init__(url, token, site_id, client_version)
33
-
39
+ super(GlobalServerAPI, self).__init__(
40
+ url,
41
+ token,
42
+ site_id,
43
+ client_version,
44
+ default_settings_variant,
45
+ ssl_verify,
46
+ cert,
47
+ # We want to make sure that server and api key validation
48
+ # happens all the time in 'GlobalServerAPI'.
49
+ create_session=False,
50
+ )
34
51
  self.validate_server_availability()
35
52
  self.create_session()
36
53
 
@@ -129,17 +146,6 @@ class ServiceContext:
129
146
  addon_version = None
130
147
  service_name = None
131
148
 
132
- @staticmethod
133
- def get_value_from_envs(env_keys, value=None):
134
- if value:
135
- return value
136
-
137
- for env_key in env_keys:
138
- value = os.environ.get(env_key)
139
- if value:
140
- break
141
- return value
142
-
143
149
  @classmethod
144
150
  def init_service(
145
151
  cls,
@@ -150,14 +156,8 @@ class ServiceContext:
150
156
  service_name=None,
151
157
  connect=True
152
158
  ):
153
- token = cls.get_value_from_envs(
154
- ("AY_API_KEY", "AYON_API_KEY"),
155
- token
156
- )
157
- server_url = cls.get_value_from_envs(
158
- ("AY_SERVER_URL", "AYON_SERVER_URL"),
159
- server_url
160
- )
159
+ token = token or os.environ.get("AYON_API_KEY")
160
+ server_url = server_url or os.environ.get("AYON_SERVER_URL")
161
161
  if not server_url:
162
162
  raise FailedServiceInit("URL to server is not set")
163
163
 
@@ -166,18 +166,9 @@ class ServiceContext:
166
166
  "Token to server {} is not set".format(server_url)
167
167
  )
168
168
 
169
- addon_name = cls.get_value_from_envs(
170
- ("AY_ADDON_NAME", "AYON_ADDON_NAME"),
171
- addon_name
172
- )
173
- addon_version = cls.get_value_from_envs(
174
- ("AY_ADDON_VERSION", "AYON_ADDON_VERSION"),
175
- addon_version
176
- )
177
- service_name = cls.get_value_from_envs(
178
- ("AY_SERVICE_NAME", "AYON_SERVICE_NAME"),
179
- service_name
180
- )
169
+ addon_name = addon_name or os.environ.get("AYON_ADDON_NAME")
170
+ addon_version = addon_version or os.environ.get("AYON_ADDON_VERSION")
171
+ service_name = service_name or os.environ.get("AYON_SERVICE_NAME")
181
172
 
182
173
  cls.token = token
183
174
  cls.server_url = server_url
@@ -618,6 +609,11 @@ def delete_dependency_package(*args, **kwargs):
618
609
  return con.delete_dependency_package(*args, **kwargs)
619
610
 
620
611
 
612
+ def upload_addon_zip(*args, **kwargs):
613
+ con = get_server_api_connection()
614
+ return con.upload_addon_zip(*args, **kwargs)
615
+
616
+
621
617
  def get_project_anatomy_presets(*args, **kwargs):
622
618
  con = get_server_api_connection()
623
619
  return con.get_project_anatomy_presets(*args, **kwargs)
@@ -4,7 +4,6 @@ import io
4
4
  import json
5
5
  import logging
6
6
  import collections
7
- import datetime
8
7
  import platform
9
8
  import copy
10
9
  import uuid
@@ -61,6 +60,7 @@ from .utils import (
61
60
  entity_data_json_default,
62
61
  failed_json_default,
63
62
  TransferProgress,
63
+ create_dependency_package_basename,
64
64
  )
65
65
 
66
66
  PatternType = type(re.compile(""))
@@ -114,6 +114,10 @@ class RestApiResponse(object):
114
114
  self.status = status_code
115
115
  self._data = data
116
116
 
117
+ @property
118
+ def text(self):
119
+ return self._response.text
120
+
117
121
  @property
118
122
  def orig_response(self):
119
123
  return self._response
@@ -150,11 +154,13 @@ class RestApiResponse(object):
150
154
  def status_code(self):
151
155
  return self.status
152
156
 
153
- def raise_for_status(self):
157
+ def raise_for_status(self, message=None):
154
158
  try:
155
159
  self._response.raise_for_status()
156
160
  except requests.exceptions.HTTPError as exc:
157
- raise HTTPRequestError(str(exc), exc.response)
161
+ if message is None:
162
+ message = str(exc)
163
+ raise HTTPRequestError(message, exc.response)
158
164
 
159
165
  def __enter__(self, *args, **kwargs):
160
166
  return self._response.__enter__(*args, **kwargs)
@@ -318,6 +324,8 @@ class ServerAPI(object):
318
324
  available then 'True' is used.
319
325
  cert (Optional[str]): Path to certificate file. Looks for env
320
326
  variable value 'AYON_CERT_FILE' by default.
327
+ create_session (Optional[bool]): Create session for connection if
328
+ token is available. Default is True.
321
329
  """
322
330
 
323
331
  def __init__(
@@ -329,6 +337,7 @@ class ServerAPI(object):
329
337
  default_settings_variant=None,
330
338
  ssl_verify=None,
331
339
  cert=None,
340
+ create_session=True,
332
341
  ):
333
342
  if not base_url:
334
343
  raise ValueError("Invalid server URL {}".format(str(base_url)))
@@ -360,6 +369,7 @@ class ServerAPI(object):
360
369
 
361
370
  self._access_token_is_service = None
362
371
  self._token_is_valid = None
372
+ self._token_validation_started = False
363
373
  self._server_available = None
364
374
  self._server_version = None
365
375
  self._server_version_tuple = None
@@ -382,6 +392,11 @@ class ServerAPI(object):
382
392
  self._as_user_stack = _AsUserStack()
383
393
  self._thumbnail_cache = ThumbnailCache(True)
384
394
 
395
+ # Create session
396
+ if self._access_token and create_session:
397
+ self.validate_server_availability()
398
+ self.create_session()
399
+
385
400
  @property
386
401
  def log(self):
387
402
  if self._log is None:
@@ -645,6 +660,7 @@ class ServerAPI(object):
645
660
 
646
661
  def validate_token(self):
647
662
  try:
663
+ self._token_validation_started = True
648
664
  # TODO add other possible validations
649
665
  # - existence of 'user' key in info
650
666
  # - validate that 'site_id' is in 'sites' in info
@@ -654,6 +670,9 @@ class ServerAPI(object):
654
670
 
655
671
  except UnauthorizedError:
656
672
  self._token_is_valid = False
673
+
674
+ finally:
675
+ self._token_validation_started = False
657
676
  return self._token_is_valid
658
677
 
659
678
  def set_token(self, token):
@@ -666,8 +685,25 @@ class ServerAPI(object):
666
685
  self._token_is_valid = None
667
686
  self.close_session()
668
687
 
669
- def create_session(self):
688
+ def create_session(self, ignore_existing=True, force=False):
689
+ """Create a connection session.
690
+
691
+ Session helps to keep connection with server without
692
+ need to reconnect on each call.
693
+
694
+ Args:
695
+ ignore_existing (bool): If session already exists,
696
+ ignore creation.
697
+ force (bool): If session already exists, close it and
698
+ create new.
699
+ """
700
+
701
+ if force and self._session is not None:
702
+ self.close_session()
703
+
670
704
  if self._session is not None:
705
+ if ignore_existing:
706
+ return
671
707
  raise ValueError("Session is already created.")
672
708
 
673
709
  self._as_user_stack.clear()
@@ -834,7 +870,19 @@ class ServerAPI(object):
834
870
  self._access_token)
835
871
  return headers
836
872
 
837
- def login(self, username, password):
873
+ def login(self, username, password, create_session=True):
874
+ """Login to server.
875
+
876
+ Args:
877
+ username (str): Username.
878
+ password (str): Password.
879
+ create_session (Optional[bool]): Create session after login.
880
+ Default: True.
881
+
882
+ Raises:
883
+ AuthenticationError: Login failed.
884
+ """
885
+
838
886
  if self.has_valid_token:
839
887
  try:
840
888
  user_info = self.get_user()
@@ -844,7 +892,8 @@ class ServerAPI(object):
844
892
  current_username = user_info.get("name")
845
893
  if current_username == username:
846
894
  self.close_session()
847
- self.create_session()
895
+ if create_session:
896
+ self.create_session()
848
897
  return
849
898
 
850
899
  self.reset_token()
@@ -868,7 +917,9 @@ class ServerAPI(object):
868
917
 
869
918
  if not self.has_valid_token:
870
919
  raise AuthenticationError("Invalid credentials")
871
- self.create_session()
920
+
921
+ if create_session:
922
+ self.create_session()
872
923
 
873
924
  def logout(self, soft=False):
874
925
  if self._access_token:
@@ -881,6 +932,15 @@ class ServerAPI(object):
881
932
 
882
933
  def _do_rest_request(self, function, url, **kwargs):
883
934
  if self._session is None:
935
+ # Validate token if was not yet validated
936
+ # - ignore validation if we're in middle of
937
+ # validation
938
+ if (
939
+ self._token_is_valid is None
940
+ and not self._token_validation_started
941
+ ):
942
+ self.validate_token()
943
+
884
944
  if "headers" not in kwargs:
885
945
  kwargs["headers"] = self.get_headers()
886
946
 
@@ -1303,13 +1363,15 @@ class ServerAPI(object):
1303
1363
  progress.set_transfer_done()
1304
1364
  return progress
1305
1365
 
1306
- def _upload_file(self, url, filepath, progress):
1366
+ def _upload_file(self, url, filepath, progress, request_type=None):
1367
+ if request_type is None:
1368
+ request_type = RequestTypes.put
1307
1369
  kwargs = {}
1308
1370
  if self._session is None:
1309
1371
  kwargs["headers"] = self.get_headers()
1310
- post_func = self._base_functions_mapping[RequestTypes.post]
1372
+ post_func = self._base_functions_mapping[request_type]
1311
1373
  else:
1312
- post_func = self._session_functions_mapping[RequestTypes.post]
1374
+ post_func = self._session_functions_mapping[request_type]
1313
1375
 
1314
1376
  with open(filepath, "rb") as stream:
1315
1377
  stream.seek(0, io.SEEK_END)
@@ -1319,8 +1381,11 @@ class ServerAPI(object):
1319
1381
  response = post_func(url, data=stream, **kwargs)
1320
1382
  response.raise_for_status()
1321
1383
  progress.set_transferred_size(size)
1384
+ return response
1322
1385
 
1323
- def upload_file(self, endpoint, filepath, progress=None):
1386
+ def upload_file(
1387
+ self, endpoint, filepath, progress=None, request_type=None
1388
+ ):
1324
1389
  """Upload file to server.
1325
1390
 
1326
1391
  Todos:
@@ -1331,6 +1396,11 @@ class ServerAPI(object):
1331
1396
  filepath (str): Source filepath.
1332
1397
  progress (Optional[TransferProgress]): Object that gives ability
1333
1398
  to track upload progress.
1399
+ request_type (Optional[RequestType]): Type of request that will
1400
+ be used to upload file.
1401
+
1402
+ Returns:
1403
+ requests.Response: Response object.
1334
1404
  """
1335
1405
 
1336
1406
  if endpoint.startswith(self._base_url):
@@ -1349,7 +1419,7 @@ class ServerAPI(object):
1349
1419
  progress.set_started()
1350
1420
 
1351
1421
  try:
1352
- self._upload_file(url, filepath, progress)
1422
+ return self._upload_file(url, filepath, progress, request_type)
1353
1423
 
1354
1424
  except Exception as exc:
1355
1425
  progress.set_failed(str(exc))
@@ -1486,13 +1556,11 @@ class ServerAPI(object):
1486
1556
  """
1487
1557
 
1488
1558
  response = self.delete("attributes/{}".format(attribute_name))
1489
- if response.status_code != 204:
1490
- # TODO raise different exception
1491
- raise ValueError(
1492
- "Attribute \"{}\" was not created/updated. {}".format(
1493
- attribute_name, response.detail
1494
- )
1559
+ response.raise_for_status(
1560
+ "Attribute \"{}\" was not created/updated. {}".format(
1561
+ attribute_name, response.detail
1495
1562
  )
1563
+ )
1496
1564
 
1497
1565
  self.reset_attributes_schema()
1498
1566
 
@@ -1629,7 +1697,7 @@ class ServerAPI(object):
1629
1697
  Args:
1630
1698
  addon_name (str): Name of addon.
1631
1699
  addon_version (str): Version of addon.
1632
- subpaths (tuple[str]): Any amount of subpaths that are added to
1700
+ *subpaths (str): Any amount of subpaths that are added to
1633
1701
  addon url.
1634
1702
 
1635
1703
  Returns:
@@ -1732,8 +1800,9 @@ class ServerAPI(object):
1732
1800
  python_version,
1733
1801
  platform_name,
1734
1802
  python_modules,
1803
+ runtime_python_modules,
1735
1804
  checksum,
1736
- checksum_type,
1805
+ checksum_algorithm,
1737
1806
  file_size,
1738
1807
  sources=None,
1739
1808
  ):
@@ -1742,6 +1811,10 @@ class ServerAPI(object):
1742
1811
  This step will create only metadata. Make sure to upload installer
1743
1812
  to the server using 'upload_installer' method.
1744
1813
 
1814
+ Runtime python modules are modules that are required to run AYON
1815
+ desktop application, but are not added to PYTHONPATH for any
1816
+ subprocess.
1817
+
1745
1818
  Args:
1746
1819
  filename (str): Installer filename.
1747
1820
  version (str): Version of installer.
@@ -1749,8 +1822,10 @@ class ServerAPI(object):
1749
1822
  platform_name (str): Name of platform.
1750
1823
  python_modules (dict[str, str]): Python modules that are available
1751
1824
  in installer.
1825
+ runtime_python_modules (dict[str, str]): Runtime python modules
1826
+ that are available in installer.
1752
1827
  checksum (str): Installer file checksum.
1753
- checksum_type (str): Type of checksum used to create checksum.
1828
+ checksum_algorithm (str): Type of checksum used to create checksum.
1754
1829
  file_size (int): File size.
1755
1830
  sources (Optional[list[dict[str, Any]]]): List of sources that
1756
1831
  can be used to download file.
@@ -1762,8 +1837,9 @@ class ServerAPI(object):
1762
1837
  "pythonVersion": python_version,
1763
1838
  "platform": platform_name,
1764
1839
  "pythonModules": python_modules,
1840
+ "runtimePythonModules": runtime_python_modules,
1765
1841
  "checksum": checksum,
1766
- "checksumType": checksum_type,
1842
+ "checksumAlgorithm": checksum_algorithm,
1767
1843
  "size": file_size,
1768
1844
  }
1769
1845
  if sources:
@@ -1781,7 +1857,7 @@ class ServerAPI(object):
1781
1857
  can be used to download file. Fully replaces existing sources.
1782
1858
  """
1783
1859
 
1784
- response = self.post(
1860
+ response = self.patch(
1785
1861
  "desktop/installers/{}".format(filename),
1786
1862
  sources=sources
1787
1863
  )
@@ -1794,7 +1870,7 @@ class ServerAPI(object):
1794
1870
  filename (str): Installer filename.
1795
1871
  """
1796
1872
 
1797
- response = self.delete("dekstop/installers/{}".format(filename))
1873
+ response = self.delete("desktop/installers/{}".format(filename))
1798
1874
  response.raise_for_status()
1799
1875
 
1800
1876
  def download_installer(
@@ -1829,9 +1905,12 @@ class ServerAPI(object):
1829
1905
  dst_filename (str): Destination filename.
1830
1906
  progress (Optional[TransferProgress]): Object that gives ability
1831
1907
  to track download progress.
1908
+
1909
+ Returns:
1910
+ requests.Response: Response object.
1832
1911
  """
1833
1912
 
1834
- self.upload_file(
1913
+ return self.upload_file(
1835
1914
  "desktop/installers/{}".format(dst_filename),
1836
1915
  src_filepath,
1837
1916
  progress=progress
@@ -1929,8 +2008,7 @@ class ServerAPI(object):
1929
2008
  checksum=checksum,
1930
2009
  **kwargs
1931
2010
  )
1932
- if response.status not in (200, 201, 204):
1933
- raise ServerError("Failed to create/update dependency")
2011
+ response.raise_for_status("Failed to create/update dependency")
1934
2012
  return response.data
1935
2013
 
1936
2014
  def get_dependency_packages(self):
@@ -2065,8 +2143,7 @@ class ServerAPI(object):
2065
2143
 
2066
2144
  route = self._get_dependency_package_route(filename, platform_name)
2067
2145
  response = self.delete(route)
2068
- if response.status != 200:
2069
- raise ServerError("Failed to delete dependency file")
2146
+ response.raise_for_status("Failed to delete dependency file")
2070
2147
  return response.data
2071
2148
 
2072
2149
  def download_dependency_package(
@@ -2131,6 +2208,10 @@ class ServerAPI(object):
2131
2208
  def create_dependency_package_basename(self, platform_name=None):
2132
2209
  """Create basename for dependency package file.
2133
2210
 
2211
+ Deprecated:
2212
+ Use 'create_dependency_package_basename' from `ayon_api` or
2213
+ `ayon_api.utils` instead.
2214
+
2134
2215
  Args:
2135
2216
  platform_name (Optional[str]): Name of platform for which the
2136
2217
  bundle is targeted. Default value is current platform.
@@ -2139,12 +2220,34 @@ class ServerAPI(object):
2139
2220
  str: Dependency package name with timestamp and platform.
2140
2221
  """
2141
2222
 
2142
- if platform_name is None:
2143
- platform_name = platform.system().lower()
2223
+ return create_dependency_package_basename(platform_name)
2224
+
2225
+ def upload_addon_zip(self, src_filepath, progress=None):
2226
+ """Upload addon zip file to server.
2227
+
2228
+ File is validated on server. If it is valid, it is installed. It will
2229
+ create an event job which can be tracked (tracking part is not
2230
+ implemented yet).
2144
2231
 
2145
- now_date = datetime.datetime.now()
2146
- time_stamp = now_date.strftime("%y%m%d%H%M")
2147
- return "ayon_{}_{}".format(time_stamp, platform_name)
2232
+ Example output:
2233
+ {'eventId': 'a1bfbdee27c611eea7580242ac120003'}
2234
+
2235
+ Args:
2236
+ src_filepath (str): Path to a zip file.
2237
+ progress (Optional[TransferProgress]): Object to keep track about
2238
+ upload state.
2239
+
2240
+ Returns:
2241
+ dict[str, Any]: Response data from server.
2242
+ """
2243
+
2244
+ response = self.upload_file(
2245
+ "addons/install",
2246
+ src_filepath,
2247
+ progress=progress,
2248
+ request_type=RequestTypes.post,
2249
+ )
2250
+ return response.json()
2148
2251
 
2149
2252
  def _get_bundles_route(self):
2150
2253
  major, minor, patch, _, _ = self.server_version_tuple
@@ -3035,6 +3138,65 @@ class ServerAPI(object):
3035
3138
  fill_own_attribs(project)
3036
3139
  return project
3037
3140
 
3141
+ def get_folders_hierarchy(
3142
+ self,
3143
+ project_name,
3144
+ search_string=None,
3145
+ folder_types=None
3146
+ ):
3147
+ """Get project hierarchy.
3148
+
3149
+ All folders in project in hierarchy data structure.
3150
+
3151
+ Example output:
3152
+ {
3153
+ "hierarchy": [
3154
+ {
3155
+ "id": "...",
3156
+ "name": "...",
3157
+ "label": "...",
3158
+ "status": "...",
3159
+ "folderType": "...",
3160
+ "hasTasks": False,
3161
+ "taskNames": [],
3162
+ "parents": [],
3163
+ "parentId": None,
3164
+ "children": [...children folders...]
3165
+ },
3166
+ ...
3167
+ ]
3168
+ }
3169
+
3170
+ Args:
3171
+ project_name (str): Project where to look for folders.
3172
+ search_string (Optional[str]): Search string to filter folders.
3173
+ folder_types (Optional[Iterable[str]]): Folder types to filter.
3174
+
3175
+ Returns:
3176
+ dict[str, Any]: Response data from server.
3177
+ """
3178
+
3179
+ if folder_types:
3180
+ folder_types = ",".join(folder_types)
3181
+
3182
+ query_fields = [
3183
+ "{}={}".format(key, value)
3184
+ for key, value in (
3185
+ ("search", search_string),
3186
+ ("types", folder_types),
3187
+ )
3188
+ if value
3189
+ ]
3190
+ query = ""
3191
+ if query_fields:
3192
+ query = "?{}".format(",".join(query_fields))
3193
+
3194
+ response = self.get(
3195
+ "projects/{}/hierarchy{}".format(project_name, query)
3196
+ )
3197
+ response.raise_for_status()
3198
+ return response.data
3199
+
3038
3200
  def get_folders(
3039
3201
  self,
3040
3202
  project_name,
@@ -3606,7 +3768,6 @@ class ServerAPI(object):
3606
3768
  if filtered_product is not None:
3607
3769
  yield filtered_product
3608
3770
 
3609
-
3610
3771
  def get_product_by_id(
3611
3772
  self,
3612
3773
  project_name,
@@ -2,6 +2,7 @@ import re
2
2
  import datetime
3
3
  import uuid
4
4
  import string
5
+ import platform
5
6
  import collections
6
7
  try:
7
8
  # Python 3
@@ -358,7 +359,7 @@ class TransferProgress:
358
359
  def __init__(self):
359
360
  self._started = False
360
361
  self._transfer_done = False
361
- self._transfered = 0
362
+ self._transferred = 0
362
363
  self._content_size = None
363
364
 
364
365
  self._failed = False
@@ -368,25 +369,66 @@ class TransferProgress:
368
369
  self._destination_url = "N/A"
369
370
 
370
371
  def get_content_size(self):
372
+ """Content size in bytes.
373
+
374
+ Returns:
375
+ Union[int, None]: Content size in bytes or None
376
+ if is unknown.
377
+ """
378
+
371
379
  return self._content_size
372
380
 
373
381
  def set_content_size(self, content_size):
382
+ """Set content size in bytes.
383
+
384
+ Args:
385
+ content_size (int): Content size in bytes.
386
+
387
+ Raises:
388
+ ValueError: If content size was already set.
389
+ """
390
+
374
391
  if self._content_size is not None:
375
392
  raise ValueError("Content size was set more then once")
376
393
  self._content_size = content_size
377
394
 
378
395
  def get_started(self):
396
+ """Transfer was started.
397
+
398
+ Returns:
399
+ bool: True if transfer started.
400
+ """
401
+
379
402
  return self._started
380
403
 
381
404
  def set_started(self):
405
+ """Mark that transfer started.
406
+
407
+ Raises:
408
+ ValueError: If transfer was already started.
409
+ """
410
+
382
411
  if self._started:
383
412
  raise ValueError("Progress already started")
384
413
  self._started = True
385
414
 
386
415
  def get_transfer_done(self):
416
+ """Transfer finished.
417
+
418
+ Returns:
419
+ bool: Transfer finished.
420
+ """
421
+
387
422
  return self._transfer_done
388
423
 
389
424
  def set_transfer_done(self):
425
+ """Mark progress as transfer finished.
426
+
427
+ Raises:
428
+ ValueError: If progress was already marked as done
429
+ or wasn't started yet.
430
+ """
431
+
390
432
  if self._transfer_done:
391
433
  raise ValueError("Progress was already marked as done")
392
434
  if not self._started:
@@ -394,41 +436,117 @@ class TransferProgress:
394
436
  self._transfer_done = True
395
437
 
396
438
  def get_failed(self):
439
+ """Transfer failed.
440
+
441
+ Returns:
442
+ bool: True if transfer failed.
443
+ """
444
+
397
445
  return self._failed
398
446
 
399
447
  def get_fail_reason(self):
448
+ """Get reason why transfer failed.
449
+
450
+ Returns:
451
+ Union[str, None]: Reason why transfer
452
+ failed or None.
453
+ """
454
+
400
455
  return self._fail_reason
401
456
 
402
457
  def set_failed(self, reason):
458
+ """Mark progress as failed.
459
+
460
+ Args:
461
+ reason (str): Reason why transfer failed.
462
+ """
463
+
403
464
  self._fail_reason = reason
404
465
  self._failed = True
405
466
 
406
467
  def get_transferred_size(self):
407
- return self._transfered
468
+ """Already transferred size in bytes.
469
+
470
+ Returns:
471
+ int: Already transferred size in bytes.
472
+ """
473
+
474
+ return self._transferred
408
475
 
409
- def set_transferred_size(self, transfered):
410
- self._transfered = transfered
476
+ def set_transferred_size(self, transferred):
477
+ """Set already transferred size in bytes.
478
+
479
+ Args:
480
+ transferred (int): Already transferred size in bytes.
481
+ """
482
+
483
+ self._transferred = transferred
411
484
 
412
485
  def add_transferred_chunk(self, chunk_size):
413
- self._transfered += chunk_size
486
+ """Add transferred chunk size in bytes.
487
+
488
+ Args:
489
+ chunk_size (int): Add transferred chunk size
490
+ in bytes.
491
+ """
492
+
493
+ self._transferred += chunk_size
414
494
 
415
495
  def get_source_url(self):
496
+ """Source url from where transfer happens.
497
+
498
+ Note:
499
+ Consider this as title. Must be set using
500
+ 'set_source_url' or 'N/A' will be returned.
501
+
502
+ Returns:
503
+ str: Source url from where transfer happens.
504
+ """
505
+
416
506
  return self._source_url
417
507
 
418
508
  def set_source_url(self, url):
509
+ """Set source url from where transfer happens.
510
+
511
+ Args:
512
+ url (str): Source url from where transfer happens.
513
+ """
514
+
419
515
  self._source_url = url
420
516
 
421
517
  def get_destination_url(self):
518
+ """Destination url where transfer happens.
519
+
520
+ Note:
521
+ Consider this as title. Must be set using
522
+ 'set_source_url' or 'N/A' will be returned.
523
+
524
+ Returns:
525
+ str: Destination url where transfer happens.
526
+ """
527
+
422
528
  return self._destination_url
423
529
 
424
530
  def set_destination_url(self, url):
531
+ """Set destination url where transfer happens.
532
+
533
+ Args:
534
+ url (str): Destination url where transfer happens.
535
+ """
536
+
425
537
  self._destination_url = url
426
538
 
427
539
  @property
428
540
  def is_running(self):
541
+ """Check if transfer is running.
542
+
543
+ Returns:
544
+ bool: True if transfer is running.
545
+ """
546
+
429
547
  if (
430
548
  not self.started
431
- or self.done
549
+ or self.transfer_done
432
550
  or self.failed
433
551
  ):
434
552
  return False
@@ -436,9 +554,16 @@ class TransferProgress:
436
554
 
437
555
  @property
438
556
  def transfer_progress(self):
557
+ """Get transfer progress in percents.
558
+
559
+ Returns:
560
+ Union[float, None]: Transfer progress in percents or 'None'
561
+ if content size is unknown.
562
+ """
563
+
439
564
  if self._content_size is None:
440
565
  return None
441
- return (self._transfered * 100.0) / float(self._content_size)
566
+ return (self._transferred * 100.0) / float(self._content_size)
442
567
 
443
568
  content_size = property(get_content_size, set_content_size)
444
569
  started = property(get_started)
@@ -447,5 +572,23 @@ class TransferProgress:
447
572
  fail_reason = property(get_fail_reason)
448
573
  source_url = property(get_source_url, set_source_url)
449
574
  destination_url = property(get_destination_url, set_destination_url)
450
- content_size = property(get_content_size, set_content_size)
451
575
  transferred_size = property(get_transferred_size, set_transferred_size)
576
+
577
+
578
+ def create_dependency_package_basename(platform_name=None):
579
+ """Create basename for dependency package file.
580
+
581
+ Args:
582
+ platform_name (Optional[str]): Name of platform for which the
583
+ bundle is targeted. Default value is current platform.
584
+
585
+ Returns:
586
+ str: Dependency package name with timestamp and platform.
587
+ """
588
+
589
+ if platform_name is None:
590
+ platform_name = platform.system().lower()
591
+
592
+ now_date = datetime.datetime.now()
593
+ time_stamp = now_date.strftime("%y%m%d%H%M")
594
+ return "ayon_{}_{}".format(time_stamp, platform_name)
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for Ayon server."""
2
- __version__ = "0.3.1"
2
+ __version__ = "0.3.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ayon-python-api"
3
- version = "0.3.1"
3
+ version = "0.3.3"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes