ayon-python-api 0.5.2__tar.gz → 0.5.4__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 (23) hide show
  1. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/PKG-INFO +1 -1
  2. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/_api.py +1 -1
  3. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/constants.py +11 -0
  4. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/entity_hub.py +9 -3
  5. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/server_api.py +196 -48
  6. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/utils.py +25 -1
  7. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/version.py +1 -1
  8. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/PKG-INFO +1 -1
  9. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/pyproject.toml +1 -1
  10. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/LICENSE +0 -0
  11. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/README.md +0 -0
  12. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/__init__.py +0 -0
  13. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/events.py +0 -0
  14. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/exceptions.py +0 -0
  15. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/graphql.py +0 -0
  16. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/graphql_queries.py +0 -0
  17. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_api/operations.py +0 -0
  18. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  19. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  20. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/requires.txt +0 -0
  21. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/ayon_python_api.egg-info/top_level.txt +0 -0
  22. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/setup.cfg +0 -0
  23. {ayon-python-api-0.5.2 → ayon-python-api-0.5.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -374,7 +374,7 @@ def get_default_settings_variant():
374
374
  """
375
375
 
376
376
  con = get_server_api_connection()
377
- return con.get_client_version()
377
+ return con.get_default_settings_variant()
378
378
 
379
379
 
380
380
  def set_default_settings_variant(variant):
@@ -3,6 +3,10 @@ SERVER_URL_ENV_KEY = "AYON_SERVER_URL"
3
3
  SERVER_API_ENV_KEY = "AYON_API_KEY"
4
4
  SERVER_TIMEOUT_ENV_KEY = "AYON_SERVER_TIMEOUT"
5
5
  SERVER_RETRIES_ENV_KEY = "AYON_SERVER_RETRIES"
6
+ # Default variant used for settings
7
+ DEFAULT_VARIANT_ENV_KEY = "AYON_DEFAULT_SETTINGS_VARIANT"
8
+ # Default site id used for connection
9
+ SITE_ID_ENV_KEY = "AYON_SITE_ID"
6
10
 
7
11
  # Backwards compatibility
8
12
  SERVER_TOKEN_ENV_KEY = SERVER_API_ENV_KEY
@@ -40,6 +44,7 @@ DEFAULT_PROJECT_FIELDS = {
40
44
  "code",
41
45
  "config",
42
46
  "createdAt",
47
+ "data",
43
48
  }
44
49
 
45
50
  # --- Folders ---
@@ -52,6 +57,7 @@ DEFAULT_FOLDER_FIELDS = {
52
57
  "parentId",
53
58
  "active",
54
59
  "thumbnailId",
60
+ "data",
55
61
  }
56
62
 
57
63
  # --- Tasks ---
@@ -63,6 +69,7 @@ DEFAULT_TASK_FIELDS = {
63
69
  "folderId",
64
70
  "active",
65
71
  "assignees",
72
+ "data",
66
73
  }
67
74
 
68
75
  # --- Products ---
@@ -72,6 +79,7 @@ DEFAULT_PRODUCT_FIELDS = {
72
79
  "folderId",
73
80
  "active",
74
81
  "productType",
82
+ "data",
75
83
  }
76
84
 
77
85
  # --- Versions ---
@@ -86,6 +94,7 @@ DEFAULT_VERSION_FIELDS = {
86
94
  "thumbnailId",
87
95
  "createdAt",
88
96
  "updatedAt",
97
+ "data",
89
98
  }
90
99
 
91
100
  # --- Representations ---
@@ -96,6 +105,7 @@ DEFAULT_REPRESENTATION_FIELDS = {
96
105
  "createdAt",
97
106
  "active",
98
107
  "versionId",
108
+ "data",
99
109
  }
100
110
 
101
111
  REPRESENTATION_FILES_FIELDS = {
@@ -119,6 +129,7 @@ DEFAULT_WORKFILE_INFO_FIELDS = {
119
129
  "thumbnailId",
120
130
  "updatedAt",
121
131
  "updatedBy",
132
+ "data",
122
133
  }
123
134
 
124
135
  DEFAULT_EVENT_FIELDS = {
@@ -36,15 +36,18 @@ class EntityHub(object):
36
36
  """
37
37
 
38
38
  def __init__(
39
- self, project_name, connection=None, allow_data_changes=False
39
+ self, project_name, connection=None, allow_data_changes=None
40
40
  ):
41
41
  if not connection:
42
42
  connection = get_server_api_connection()
43
- major, minor, _, _, _ = connection.server_version_tuple
43
+ major, minor, patch, _, _ = connection.server_version_tuple
44
44
  path_start_with_slash = True
45
45
  if (major, minor) < (0, 6):
46
46
  path_start_with_slash = False
47
47
 
48
+ if allow_data_changes is None:
49
+ allow_data_changes = connection.graphql_allows_data_in_query
50
+
48
51
  self._connection = connection
49
52
  self._path_start_with_slash = path_start_with_slash
50
53
 
@@ -2546,7 +2549,10 @@ class FolderEntity(BaseEntity):
2546
2549
  if self.thumbnail_id is not UNKNOWN_VALUE:
2547
2550
  output["thumbnailId"] = self.thumbnail_id
2548
2551
 
2549
- if self._entity_hub.allow_data_changes:
2552
+ if (
2553
+ self._entity_hub.allow_data_changes
2554
+ and self._data is not UNKNOWN_VALUE
2555
+ ):
2550
2556
  output["data"] = self._data
2551
2557
  return output
2552
2558
 
@@ -9,6 +9,9 @@ import platform
9
9
  import copy
10
10
  import uuid
11
11
  from contextlib import contextmanager
12
+
13
+ import six
14
+
12
15
  try:
13
16
  from http import HTTPStatus
14
17
  except ImportError:
@@ -27,7 +30,6 @@ except ImportError:
27
30
  from json import JSONDecodeError as RequestsJSONDecodeError
28
31
 
29
32
  from .constants import (
30
- SERVER_TIMEOUT_ENV_KEY,
31
33
  SERVER_RETRIES_ENV_KEY,
32
34
  DEFAULT_PRODUCT_TYPE_FIELDS,
33
35
  DEFAULT_PROJECT_FIELDS,
@@ -76,8 +78,11 @@ from .utils import (
76
78
  create_dependency_package_basename,
77
79
  ThumbnailContent,
78
80
  get_default_timeout,
81
+ get_default_settings_variant,
82
+ get_default_site_id,
79
83
  )
80
84
 
85
+ _PLACEHOLDER = object()
81
86
  PatternType = type(re.compile(""))
82
87
  JSONDecodeError = getattr(json, "JSONDecodeError", ValueError)
83
88
  # This should be collected from server schema
@@ -358,12 +363,16 @@ class ServerAPI(object):
358
363
  max_retries (Optional[int]): Number of retries for requests.
359
364
  """
360
365
  _default_max_retries = 3
366
+ # 1 MB chunk by default
367
+ # TODO find out if these are reasonable default value
368
+ default_download_chunk_size = 1024 * 1024
369
+ default_upload_chunk_size = 1024 * 1024
361
370
 
362
371
  def __init__(
363
372
  self,
364
373
  base_url,
365
374
  token=None,
366
- site_id=None,
375
+ site_id=_PLACEHOLDER,
367
376
  client_version=None,
368
377
  default_settings_variant=None,
369
378
  sender=None,
@@ -382,11 +391,14 @@ class ServerAPI(object):
382
391
  self._graphql_url = "{}/graphql".format(base_url)
383
392
  self._log = None
384
393
  self._access_token = token
394
+ # Allow to have 'site_id' to 'None'
395
+ if site_id is _PLACEHOLDER:
396
+ site_id = get_default_site_id()
385
397
  self._site_id = site_id
386
398
  self._client_version = client_version
387
399
  self._default_settings_variant = (
388
400
  default_settings_variant
389
- or "production"
401
+ or get_default_settings_variant()
390
402
  )
391
403
  self._sender = sender
392
404
 
@@ -416,6 +428,8 @@ class ServerAPI(object):
416
428
  self._server_version = None
417
429
  self._server_version_tuple = None
418
430
 
431
+ self._graphql_allows_data_in_query = None
432
+
419
433
  self._session = None
420
434
 
421
435
  self._base_functions_mapping = {
@@ -941,6 +955,26 @@ class ServerAPI(object):
941
955
  server_version = property(get_server_version)
942
956
  server_version_tuple = property(get_server_version_tuple)
943
957
 
958
+ @property
959
+ def graphql_allows_data_in_query(self):
960
+ """GraphlQl query can support 'data' field.
961
+
962
+ This applies only to project hierarchy entities 'project', 'folder',
963
+ 'task', 'product', 'version' and 'representation'. Others like 'user'
964
+ still require to use rest api to access 'data'.
965
+
966
+ Returns:
967
+ bool: True if server supports 'data' field in GraphQl query.
968
+ """
969
+
970
+ if self._graphql_allows_data_in_query is None:
971
+ major, minor, patch, _, _ = self.server_version_tuple
972
+ graphql_allows_data_in_query = True
973
+ if (major, minor, patch) < (0, 5, 5):
974
+ graphql_allows_data_in_query = False
975
+ self._graphql_allows_data_in_query = graphql_allows_data_in_query
976
+ return self._graphql_allows_data_in_query
977
+
944
978
  def _get_user_info(self):
945
979
  if self._access_token is None:
946
980
  return None
@@ -1566,6 +1600,10 @@ class ServerAPI(object):
1566
1600
  download happens in thread and other thread want to catch changes over
1567
1601
  time.
1568
1602
 
1603
+ Todos:
1604
+ Use retries and timeout.
1605
+ Return RestApiResponse.
1606
+
1569
1607
  Args:
1570
1608
  endpoint (str): Endpoint or URL to file that should be downloaded.
1571
1609
  filepath (str): Path where file will be downloaded.
@@ -1576,8 +1614,7 @@ class ServerAPI(object):
1576
1614
  """
1577
1615
 
1578
1616
  if not chunk_size:
1579
- # 1 MB chunk by default
1580
- chunk_size = 1024 * 1024
1617
+ chunk_size = self.default_download_chunk_size
1581
1618
 
1582
1619
  if endpoint.startswith(self._base_url):
1583
1620
  url = endpoint
@@ -1604,33 +1641,93 @@ class ServerAPI(object):
1604
1641
  progress.set_transfer_done()
1605
1642
  return progress
1606
1643
 
1607
- def _upload_file(self, url, filepath, progress, request_type=None):
1644
+ @staticmethod
1645
+ def _upload_chunks_iter(file_stream, progress, chunk_size):
1646
+ """Generator that yields chunks of file.
1647
+
1648
+ Args:
1649
+ file_stream (io.BinaryIO): Byte stream.
1650
+ progress (TransferProgress): Object to track upload progress.
1651
+ chunk_size (int): Size of chunks that are uploaded at once.
1652
+
1653
+ Yields:
1654
+ bytes: Chunk of file.
1655
+ """
1656
+
1657
+ # Get size of file
1658
+ file_stream.seek(0, io.SEEK_END)
1659
+ size = file_stream.tell()
1660
+ file_stream.seek(0)
1661
+ # Set content size to progress object
1662
+ progress.set_content_size(size)
1663
+
1664
+ while True:
1665
+ chunk = file_stream.read(chunk_size)
1666
+ if not chunk:
1667
+ break
1668
+ progress.add_transferred_chunk(len(chunk))
1669
+ yield chunk
1670
+
1671
+ def _upload_file(
1672
+ self,
1673
+ url,
1674
+ filepath,
1675
+ progress,
1676
+ request_type=None,
1677
+ chunk_size=None,
1678
+ **kwargs
1679
+ ):
1680
+ """
1681
+
1682
+ Args:
1683
+ url (str): Url where file will be uploaded.
1684
+ filepath (str): Source filepath.
1685
+ progress (TransferProgress): Object that gives ability to track
1686
+ progress.
1687
+ request_type (Optional[RequestType]): Type of request that will
1688
+ be used. Default is PUT.
1689
+ chunk_size (Optional[int]): Size of chunks that are uploaded
1690
+ at once.
1691
+ **kwargs (Any): Additional arguments that will be passed
1692
+ to request function.
1693
+
1694
+ Returns:
1695
+ RestApiResponse: Server response.
1696
+ """
1697
+
1608
1698
  if request_type is None:
1609
1699
  request_type = RequestTypes.put
1610
- kwargs = {}
1700
+
1611
1701
  if self._session is None:
1612
- kwargs["headers"] = self.get_headers()
1702
+ headers = kwargs.setdefault("headers", {})
1703
+ for key, value in self.get_headers().items():
1704
+ if key not in headers:
1705
+ headers[key] = value
1613
1706
  post_func = self._base_functions_mapping[request_type]
1614
1707
  else:
1615
1708
  post_func = self._session_functions_mapping[request_type]
1616
1709
 
1710
+ if not chunk_size:
1711
+ chunk_size = self.default_upload_chunk_size
1712
+
1617
1713
  with open(filepath, "rb") as stream:
1618
- stream.seek(0, io.SEEK_END)
1619
- size = stream.tell()
1620
- stream.seek(0)
1621
- progress.set_content_size(size)
1622
- response = post_func(url, data=stream, **kwargs)
1714
+ response = post_func(
1715
+ url,
1716
+ data=self._upload_chunks_iter(stream, progress, chunk_size),
1717
+ **kwargs
1718
+ )
1719
+
1623
1720
  response.raise_for_status()
1624
- progress.set_transferred_size(size)
1625
1721
  return response
1626
1722
 
1627
1723
  def upload_file(
1628
- self, endpoint, filepath, progress=None, request_type=None
1724
+ self, endpoint, filepath, progress=None, request_type=None, **kwargs
1629
1725
  ):
1630
1726
  """Upload file to server.
1631
1727
 
1632
1728
  Todos:
1633
- Uploading with more detailed progress.
1729
+ Use retries and timeout.
1730
+ Return RestApiResponse.
1634
1731
 
1635
1732
  Args:
1636
1733
  endpoint (str): Endpoint or url where file will be uploaded.
@@ -1639,6 +1736,8 @@ class ServerAPI(object):
1639
1736
  to track upload progress.
1640
1737
  request_type (Optional[RequestType]): Type of request that will
1641
1738
  be used to upload file.
1739
+ **kwargs (Any): Additional arguments that will be passed
1740
+ to request function.
1642
1741
 
1643
1742
  Returns:
1644
1743
  requests.Response: Response object.
@@ -1660,7 +1759,9 @@ class ServerAPI(object):
1660
1759
  progress.set_started()
1661
1760
 
1662
1761
  try:
1663
- return self._upload_file(url, filepath, progress, request_type)
1762
+ return self._upload_file(
1763
+ url, filepath, progress, request_type, **kwargs
1764
+ )
1664
1765
 
1665
1766
  except Exception as exc:
1666
1767
  progress.set_failed(str(exc))
@@ -1883,31 +1984,45 @@ class ServerAPI(object):
1883
1984
  return set(DEFAULT_EVENT_FIELDS)
1884
1985
 
1885
1986
  if entity_type == "project":
1886
- entity_type_defaults = DEFAULT_PROJECT_FIELDS
1987
+ entity_type_defaults = set(DEFAULT_PROJECT_FIELDS)
1988
+ if not self.graphql_allows_data_in_query:
1989
+ entity_type_defaults.discard("data")
1887
1990
 
1888
1991
  elif entity_type == "folder":
1889
- entity_type_defaults = DEFAULT_FOLDER_FIELDS
1992
+ entity_type_defaults = set(DEFAULT_FOLDER_FIELDS)
1993
+ if not self.graphql_allows_data_in_query:
1994
+ entity_type_defaults.discard("data")
1890
1995
 
1891
1996
  elif entity_type == "task":
1892
- entity_type_defaults = DEFAULT_TASK_FIELDS
1997
+ entity_type_defaults = set(DEFAULT_TASK_FIELDS)
1998
+ if not self.graphql_allows_data_in_query:
1999
+ entity_type_defaults.discard("data")
1893
2000
 
1894
2001
  elif entity_type == "product":
1895
- entity_type_defaults = DEFAULT_PRODUCT_FIELDS
2002
+ entity_type_defaults = set(DEFAULT_PRODUCT_FIELDS)
2003
+ if not self.graphql_allows_data_in_query:
2004
+ entity_type_defaults.discard("data")
1896
2005
 
1897
2006
  elif entity_type == "version":
1898
- entity_type_defaults = DEFAULT_VERSION_FIELDS
2007
+ entity_type_defaults = set(DEFAULT_VERSION_FIELDS)
2008
+ if not self.graphql_allows_data_in_query:
2009
+ entity_type_defaults.discard("data")
1899
2010
 
1900
2011
  elif entity_type == "representation":
1901
2012
  entity_type_defaults = (
1902
2013
  DEFAULT_REPRESENTATION_FIELDS
1903
2014
  | REPRESENTATION_FILES_FIELDS
1904
2015
  )
2016
+ if not self.graphql_allows_data_in_query:
2017
+ entity_type_defaults.discard("data")
1905
2018
 
1906
2019
  elif entity_type == "productType":
1907
- entity_type_defaults = DEFAULT_PRODUCT_TYPE_FIELDS
2020
+ entity_type_defaults = set(DEFAULT_PRODUCT_TYPE_FIELDS)
1908
2021
 
1909
2022
  elif entity_type == "workfile":
1910
- entity_type_defaults = DEFAULT_WORKFILE_INFO_FIELDS
2023
+ entity_type_defaults = set(DEFAULT_WORKFILE_INFO_FIELDS)
2024
+ if not self.graphql_allows_data_in_query:
2025
+ entity_type_defaults.discard("data")
1911
2026
 
1912
2027
  elif entity_type == "user":
1913
2028
  entity_type_defaults = set(DEFAULT_USER_FIELDS)
@@ -3659,7 +3774,7 @@ class ServerAPI(object):
3659
3774
  fields |= self.get_attributes_fields_for_type("folder")
3660
3775
 
3661
3776
  use_rest = False
3662
- if "data" in fields:
3777
+ if "data" in fields and not self.graphql_allows_data_in_query:
3663
3778
  use_rest = True
3664
3779
  fields = {"id"}
3665
3780
 
@@ -3680,6 +3795,8 @@ class ServerAPI(object):
3680
3795
 
3681
3796
  if use_rest:
3682
3797
  folder = self.get_rest_folder(project_name, folder["id"])
3798
+ else:
3799
+ self._convert_entity_data(folder)
3683
3800
 
3684
3801
  if own_attributes:
3685
3802
  fill_own_attribs(folder)
@@ -3897,7 +4014,7 @@ class ServerAPI(object):
3897
4014
  fields |= self.get_attributes_fields_for_type("task")
3898
4015
 
3899
4016
  use_rest = False
3900
- if "data" in fields:
4017
+ if "data" in fields and not self.graphql_allows_data_in_query:
3901
4018
  use_rest = True
3902
4019
  fields = {"id"}
3903
4020
 
@@ -3918,6 +4035,8 @@ class ServerAPI(object):
3918
4035
 
3919
4036
  if use_rest:
3920
4037
  task = self.get_rest_task(project_name, task["id"])
4038
+ else:
4039
+ self._convert_entity_data(task)
3921
4040
 
3922
4041
  if own_attributes:
3923
4042
  fill_own_attribs(task)
@@ -3998,6 +4117,8 @@ class ServerAPI(object):
3998
4117
 
3999
4118
  if use_rest:
4000
4119
  product = self.get_rest_product(project_name, product["id"])
4120
+ else:
4121
+ self._convert_entity_data(product)
4001
4122
 
4002
4123
  if own_attributes:
4003
4124
  fill_own_attribs(product)
@@ -4104,7 +4225,7 @@ class ServerAPI(object):
4104
4225
  fields = self.get_default_fields_for_type("product")
4105
4226
 
4106
4227
  use_rest = False
4107
- if "data" in fields:
4228
+ if "data" in fields and not self.graphql_allows_data_in_query:
4108
4229
  use_rest = True
4109
4230
  fields = {"id"}
4110
4231
 
@@ -4372,7 +4493,7 @@ class ServerAPI(object):
4372
4493
  fields |= {"id", "version"}
4373
4494
 
4374
4495
  use_rest = False
4375
- if "data" in fields:
4496
+ if "data" in fields and not self.graphql_allows_data_in_query:
4376
4497
  use_rest = True
4377
4498
  fields = {"id"}
4378
4499
 
@@ -4452,6 +4573,8 @@ class ServerAPI(object):
4452
4573
  version = self.get_rest_version(
4453
4574
  project_name, version["id"]
4454
4575
  )
4576
+ else:
4577
+ self._convert_entity_data(version)
4455
4578
 
4456
4579
  if own_attributes:
4457
4580
  fill_own_attribs(version)
@@ -4667,7 +4790,7 @@ class ServerAPI(object):
4667
4790
  own_attributes=own_attributes
4668
4791
  )
4669
4792
  return {
4670
- version["parent"]: version
4793
+ version["productId"]: version
4671
4794
  for version in versions
4672
4795
  }
4673
4796
 
@@ -4781,6 +4904,23 @@ class ServerAPI(object):
4781
4904
  )
4782
4905
  return latest_version["id"] == version_id
4783
4906
 
4907
+ def _representation_conversion(self, representation):
4908
+ if "context" in representation:
4909
+ orig_context = representation["context"]
4910
+ context = {}
4911
+ if orig_context and orig_context != "null":
4912
+ context = json.loads(orig_context)
4913
+ representation["context"] = context
4914
+
4915
+ repre_files = representation.get("files")
4916
+ if not repre_files:
4917
+ return
4918
+
4919
+ for repre_file in repre_files:
4920
+ repre_file_size = repre_file.get("size")
4921
+ if repre_file_size is not None:
4922
+ repre_file["size"] = int(repre_file["size"])
4923
+
4784
4924
  def get_representations(
4785
4925
  self,
4786
4926
  project_name,
@@ -4828,10 +4968,12 @@ class ServerAPI(object):
4828
4968
  fields = set(fields)
4829
4969
  if "attrib" in fields:
4830
4970
  fields.remove("attrib")
4831
- fields |= self.get_attributes_fields_for_type("representation")
4971
+ fields |= self.get_attributes_fields_for_type(
4972
+ "representation"
4973
+ )
4832
4974
 
4833
4975
  use_rest = False
4834
- if "data" in fields:
4976
+ if "data" in fields and not self.graphql_allows_data_in_query:
4835
4977
  use_rest = True
4836
4978
  fields = {"id"}
4837
4979
 
@@ -4894,13 +5036,10 @@ class ServerAPI(object):
4894
5036
  repre = self.get_rest_representation(
4895
5037
  project_name, repre["id"]
4896
5038
  )
5039
+ else:
5040
+ self._convert_entity_data(repre)
4897
5041
 
4898
- if "context" in repre:
4899
- orig_context = repre["context"]
4900
- context = {}
4901
- if orig_context and orig_context != "null":
4902
- context = json.loads(orig_context)
4903
- repre["context"] = context
5042
+ self._representation_conversion(repre)
4904
5043
 
4905
5044
  if own_attributes:
4906
5045
  fill_own_attribs(repre)
@@ -5013,6 +5152,9 @@ class ServerAPI(object):
5013
5152
  version = repre.pop("version")
5014
5153
  product = version.pop("product")
5015
5154
  folder = product.pop("folder")
5155
+ self._convert_entity_data(version)
5156
+ self._convert_entity_data(product)
5157
+ self._convert_entity_data(folder)
5016
5158
  output[repre_id] = RepresentationParents(
5017
5159
  version, product, folder, project
5018
5160
  )
@@ -5465,16 +5607,14 @@ class ServerAPI(object):
5465
5607
  return thumbnail_id
5466
5608
 
5467
5609
  mime_type = self._get_thumbnail_mime_type(src_filepath)
5468
- with open(src_filepath, "rb") as stream:
5469
- content = stream.read()
5470
-
5471
- response = self.raw_post(
5610
+ response = self.upload_file(
5472
5611
  "projects/{}/thumbnails".format(project_name),
5612
+ src_filepath,
5613
+ request_type=RequestTypes.post,
5473
5614
  headers={"Content-Type": mime_type},
5474
- data=content
5475
5615
  )
5476
5616
  response.raise_for_status()
5477
- return response.data["id"]
5617
+ return response.json()["id"]
5478
5618
 
5479
5619
  def update_thumbnail(self, project_name, thumbnail_id, src_filepath):
5480
5620
  """Change thumbnail content by id.
@@ -5495,13 +5635,11 @@ class ServerAPI(object):
5495
5635
  raise ValueError("Entered filepath does not exist.")
5496
5636
 
5497
5637
  mime_type = self._get_thumbnail_mime_type(src_filepath)
5498
- with open(src_filepath, "rb") as stream:
5499
- content = stream.read()
5500
-
5501
- response = self.raw_put(
5638
+ response = self.upload_file(
5502
5639
  "projects/{}/thumbnails/{}".format(project_name, thumbnail_id),
5640
+ src_filepath,
5641
+ request_type=RequestTypes.put,
5503
5642
  headers={"Content-Type": mime_type},
5504
- data=content
5505
5643
  )
5506
5644
  response.raise_for_status()
5507
5645
 
@@ -6323,3 +6461,13 @@ class ServerAPI(object):
6323
6461
  op_result["detail"],
6324
6462
  ))
6325
6463
  return op_results
6464
+
6465
+ def _convert_entity_data(self, entity):
6466
+ if not entity:
6467
+ return
6468
+ entity_data = entity.get("data")
6469
+ if (
6470
+ entity_data is not None
6471
+ and isinstance(entity_data, six.string_types)
6472
+ ):
6473
+ entity["data"] = json.loads(entity_data)
@@ -16,7 +16,11 @@ except ImportError:
16
16
  import requests
17
17
  import unidecode
18
18
 
19
- from .constants import SERVER_TIMEOUT_ENV_KEY
19
+ from .constants import (
20
+ SERVER_TIMEOUT_ENV_KEY,
21
+ DEFAULT_VARIANT_ENV_KEY,
22
+ SITE_ID_ENV_KEY,
23
+ )
20
24
  from .exceptions import UrlError
21
25
 
22
26
  REMOVED_VALUE = object()
@@ -46,6 +50,26 @@ def get_default_timeout():
46
50
  return 10.0
47
51
 
48
52
 
53
+ def get_default_settings_variant():
54
+ """Default settings variant.
55
+
56
+ Returns:
57
+ str: Settings variant from environment variable or 'production'.
58
+ """
59
+
60
+ return os.environ.get(DEFAULT_VARIANT_ENV_KEY) or "production"
61
+
62
+
63
+ def get_default_site_id():
64
+ """Site id used for server connection.
65
+
66
+ Returns:
67
+ Union[str, None]: Site id from environment variable or None.
68
+ """
69
+
70
+ return os.environ.get(SITE_ID_ENV_KEY)
71
+
72
+
49
73
  class ThumbnailContent:
50
74
  """Wrapper for thumbnail content.
51
75
 
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for Ayon server."""
2
- __version__ = "0.5.2"
2
+ __version__ = "0.5.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 0.5.2
3
+ Version: 0.5.4
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.5.2"
3
+ version = "0.5.4"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes