ayon-python-api 1.2.3__tar.gz → 1.2.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 (44) hide show
  1. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/PKG-INFO +1 -1
  2. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/__init__.py +4 -0
  3. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api.py +52 -0
  4. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/base.py +11 -0
  5. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/thumbnails.py +70 -0
  6. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/constants.py +4 -0
  7. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/graphql_queries.py +17 -1
  8. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/operations.py +68 -0
  9. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/typing.py +15 -0
  10. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/version.py +1 -1
  11. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_python_api.egg-info/PKG-INFO +1 -1
  12. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/pyproject.toml +1 -1
  13. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/LICENSE +0 -0
  14. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/README.md +0 -0
  15. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/__init__.py +0 -0
  16. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/actions.py +0 -0
  17. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/activities.py +0 -0
  18. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/attributes.py +0 -0
  19. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/bundles_addons.py +0 -0
  20. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  21. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/events.py +0 -0
  22. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/folders.py +0 -0
  23. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/installers.py +0 -0
  24. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/links.py +0 -0
  25. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/lists.py +0 -0
  26. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/products.py +0 -0
  27. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/projects.py +0 -0
  28. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/representations.py +0 -0
  29. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/secrets.py +0 -0
  30. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/tasks.py +0 -0
  31. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/versions.py +0 -0
  32. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/_api_helpers/workfiles.py +0 -0
  33. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/entity_hub.py +0 -0
  34. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/events.py +0 -0
  35. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/exceptions.py +0 -0
  36. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/graphql.py +0 -0
  37. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/server_api.py +0 -0
  38. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_api/utils.py +0 -0
  39. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  40. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  41. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_python_api.egg-info/requires.txt +0 -0
  42. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/ayon_python_api.egg-info/top_level.txt +0 -0
  43. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/setup.cfg +0 -0
  44. {ayon_python_api-1.2.3 → ayon_python_api-1.2.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon_python_api
3
- Version: 1.2.3
3
+ Version: 1.2.4
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -271,7 +271,9 @@ from ._api import (
271
271
  get_version_thumbnail,
272
272
  get_workfile_thumbnail,
273
273
  create_thumbnail,
274
+ create_thumbnail_with_stream,
274
275
  update_thumbnail,
276
+ update_thumbnail_from_stream,
275
277
  )
276
278
 
277
279
 
@@ -546,5 +548,7 @@ __all__ = (
546
548
  "get_version_thumbnail",
547
549
  "get_workfile_thumbnail",
548
550
  "create_thumbnail",
551
+ "create_thumbnail_with_stream",
549
552
  "update_thumbnail",
553
+ "update_thumbnail_from_stream",
550
554
  )
@@ -7726,6 +7726,34 @@ def create_thumbnail(
7726
7726
  )
7727
7727
 
7728
7728
 
7729
+ def create_thumbnail_with_stream(
7730
+ project_name: str,
7731
+ stream: StreamType,
7732
+ thumbnail_id: Optional[str] = None,
7733
+ ) -> str:
7734
+ """Create new thumbnail on server from byte stream.
7735
+
7736
+ Args:
7737
+ project_name (str): Project where the thumbnail will be created
7738
+ and can be used.
7739
+ stream (StreamType): Thumbnail content stream.
7740
+ thumbnail_id (Optional[str]): Prepared if of thumbnail.
7741
+
7742
+ Returns:
7743
+ str: Created thumbnail id.
7744
+
7745
+ Raises:
7746
+ ValueError: When a thumbnail source cannot be processed.
7747
+
7748
+ """
7749
+ con = get_server_api_connection()
7750
+ return con.create_thumbnail_with_stream(
7751
+ project_name=project_name,
7752
+ stream=stream,
7753
+ thumbnail_id=thumbnail_id,
7754
+ )
7755
+
7756
+
7729
7757
  def update_thumbnail(
7730
7758
  project_name: str,
7731
7759
  thumbnail_id: str,
@@ -7751,3 +7779,27 @@ def update_thumbnail(
7751
7779
  thumbnail_id=thumbnail_id,
7752
7780
  src_filepath=src_filepath,
7753
7781
  )
7782
+
7783
+
7784
+ def update_thumbnail_from_stream(
7785
+ project_name: str,
7786
+ thumbnail_id: str,
7787
+ stream: StreamType,
7788
+ ) -> None:
7789
+ """Change thumbnail content by id.
7790
+
7791
+ Update can be also used to create new thumbnail.
7792
+
7793
+ Args:
7794
+ project_name (str): Project where the thumbnail will be created
7795
+ and can be used.
7796
+ thumbnail_id (str): Thumbnail id to update.
7797
+ stream (StreamType): Thumbnail content stream.
7798
+
7799
+ """
7800
+ con = get_server_api_connection()
7801
+ return con.update_thumbnail_from_stream(
7802
+ project_name=project_name,
7803
+ thumbnail_id=thumbnail_id,
7804
+ stream=stream,
7805
+ )
@@ -13,6 +13,7 @@ if typing.TYPE_CHECKING:
13
13
  AnyEntityDict,
14
14
  ServerVersion,
15
15
  ProjectDict,
16
+ StreamType,
16
17
  )
17
18
 
18
19
  _PLACEHOLDER = object()
@@ -84,6 +85,16 @@ class BaseServerAPI:
84
85
  ) -> requests.Response:
85
86
  raise NotImplementedError()
86
87
 
88
+ def upload_file_from_stream(
89
+ self,
90
+ endpoint: str,
91
+ stream: StreamType,
92
+ progress: Optional[TransferProgress] = None,
93
+ request_type: Optional[RequestType] = None,
94
+ **kwargs
95
+ ) -> requests.Response:
96
+ raise NotImplementedError()
97
+
87
98
  def download_file(
88
99
  self,
89
100
  endpoint: str,
@@ -2,9 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import warnings
5
+ import typing
5
6
  from typing import Optional
6
7
 
7
8
  from ayon_api.utils import (
9
+ get_media_mime_type_for_stream,
8
10
  get_media_mime_type,
9
11
  ThumbnailContent,
10
12
  RequestTypes,
@@ -13,6 +15,9 @@ from ayon_api.utils import (
13
15
 
14
16
  from .base import BaseServerAPI
15
17
 
18
+ if typing.TYPE_CHECKING:
19
+ from .typing import StreamType
20
+
16
21
 
17
22
  class ThumbnailsAPI(BaseServerAPI):
18
23
  def get_thumbnail_by_id(
@@ -259,6 +264,45 @@ class ThumbnailsAPI(BaseServerAPI):
259
264
  response.raise_for_status()
260
265
  return response.json()["id"]
261
266
 
267
+ def create_thumbnail_with_stream(
268
+ self,
269
+ project_name: str,
270
+ stream: StreamType,
271
+ thumbnail_id: Optional[str] = None,
272
+ ) -> str:
273
+ """Create new thumbnail on server from byte stream.
274
+
275
+ Args:
276
+ project_name (str): Project where the thumbnail will be created
277
+ and can be used.
278
+ stream (StreamType): Thumbnail content stream.
279
+ thumbnail_id (Optional[str]): Prepared if of thumbnail.
280
+
281
+ Returns:
282
+ str: Created thumbnail id.
283
+
284
+ Raises:
285
+ ValueError: When a thumbnail source cannot be processed.
286
+
287
+ """
288
+ if thumbnail_id:
289
+ self.update_thumbnail_from_stream(
290
+ project_name,
291
+ thumbnail_id,
292
+ stream
293
+ )
294
+ return thumbnail_id
295
+
296
+ mime_type = get_media_mime_type_for_stream(stream)
297
+ response = self.upload_file_from_stream(
298
+ f"projects/{project_name}/thumbnails",
299
+ stream,
300
+ request_type=RequestTypes.post,
301
+ headers={"Content-Type": mime_type},
302
+ )
303
+ response.raise_for_status()
304
+ return response.json()["id"]
305
+
262
306
  def update_thumbnail(
263
307
  self, project_name: str, thumbnail_id: str, src_filepath: str
264
308
  ) -> None:
@@ -288,6 +332,32 @@ class ThumbnailsAPI(BaseServerAPI):
288
332
  )
289
333
  response.raise_for_status()
290
334
 
335
+ def update_thumbnail_from_stream(
336
+ self,
337
+ project_name: str,
338
+ thumbnail_id: str,
339
+ stream: StreamType,
340
+ ) -> None:
341
+ """Change thumbnail content by id.
342
+
343
+ Update can be also used to create new thumbnail.
344
+
345
+ Args:
346
+ project_name (str): Project where the thumbnail will be created
347
+ and can be used.
348
+ thumbnail_id (str): Thumbnail id to update.
349
+ stream (StreamType): Thumbnail content stream.
350
+
351
+ """
352
+ mime_type = get_media_mime_type_for_stream(stream)
353
+ response = self.upload_file_from_stream(
354
+ f"projects/{project_name}/thumbnails/{thumbnail_id}",
355
+ stream,
356
+ request_type=RequestTypes.put,
357
+ headers={"Content-Type": mime_type},
358
+ )
359
+ response.raise_for_status()
360
+
291
361
  def _prepare_thumbnail_content(
292
362
  self,
293
363
  project_name: str,
@@ -252,4 +252,8 @@ DEFAULT_ENTITY_LIST_FIELDS = {
252
252
  "tags",
253
253
  "updatedAt",
254
254
  "updatedBy",
255
+ "items.id",
256
+ "items.entityId",
257
+ "items.entityType",
258
+ "items.position",
255
259
  }
@@ -680,9 +680,25 @@ def entity_lists_graphql_query(fields):
680
680
  entity_lists_field = project_field.add_field_with_edges("entityLists")
681
681
  entity_lists_field.set_filter("ids", entity_list_ids)
682
682
 
683
- nested_fields = fields_to_dict(set(fields))
683
+ fields = set(fields)
684
+ items_field_names = set()
685
+ for field_name in set(fields):
686
+ field_name.removeprefix("items")
687
+ if not field_name.startswith("items"):
688
+ continue
689
+
690
+ fields.discard(field_name)
691
+ field_name = field_name.removeprefix("items").lstrip(".")
692
+ if field_name:
693
+ items_field_names.add(field_name)
684
694
 
685
695
  query_queue = collections.deque()
696
+ if items_field_names:
697
+ items_field = entity_lists_field.add_field_with_edges("items")
698
+ for field_name in items_field_names:
699
+ items_field.add_edge_field(field_name)
700
+
701
+ nested_fields = fields_to_dict(set(fields))
686
702
  for key, value in nested_fields.items():
687
703
  query_queue.append((key, value, entity_lists_field))
688
704
 
@@ -15,6 +15,7 @@ if typing.TYPE_CHECKING:
15
15
  from .server_api import ServerAPI
16
16
  from .typing import (
17
17
  NewFolderDict,
18
+ NewTaskDict,
18
19
  NewProductDict,
19
20
  NewVersionDict,
20
21
  NewRepresentationDict,
@@ -76,6 +77,7 @@ def new_folder_entity(
76
77
  folder_type: str,
77
78
  parent_id: Optional[str] = None,
78
79
  status: Optional[str] = None,
80
+ active: Optional[bool] = None,
79
81
  tags: Optional[list[str]] = None,
80
82
  attribs: Optional[dict[str, Any]] = None,
81
83
  data: Optional[dict[str, Any]] = None,
@@ -89,6 +91,7 @@ def new_folder_entity(
89
91
  folder_type (str): Type of folder.
90
92
  parent_id (Optional[str]): Parent folder id.
91
93
  status (Optional[str]): Product status.
94
+ active (Optional[bool]): Active status..
92
95
  tags (Optional[list[str]]): List of tags.
93
96
  attribs (Optional[dict[str, Any]]): Explicitly set attributes
94
97
  of folder.
@@ -125,6 +128,67 @@ def new_folder_entity(
125
128
  output["status"] = status
126
129
  if tags:
127
130
  output["tags"] = tags
131
+ if active is not None:
132
+ output["active"] = active
133
+ return output
134
+
135
+
136
+ def new_task_entity(
137
+ name: str,
138
+ task_type: str,
139
+ folder_id: str,
140
+ *,
141
+ label: Optional[str] = None,
142
+ assignees: Optional[list[str]] = None,
143
+ attrib: Optional[dict[str, Any]] = None,
144
+ data: Optional[dict[str, Any]] = None,
145
+ tags: Optional[list[str]] = None,
146
+ status: Optional[str] = None,
147
+ active: Optional[bool] = None,
148
+ thumbnail_id: Optional[str] = None,
149
+ task_id: Optional[str] = None,
150
+ ) -> NewTaskDict:
151
+ """Create skeleton data of task entity.
152
+
153
+ Args:
154
+ name (str): Folder name.
155
+ task_type (str): Task type.
156
+ folder_id (str): Parent folder id.
157
+ label (Optional[str]): Label of folder.
158
+ assignees (Optional[list[str]]): Task assignees.
159
+ attrib (Optional[dict[str, Any]]): Task attributes.
160
+ data (Optional[dict[str, Any]]): Task data.
161
+ tags (Optional[list[str]]): Task tags.
162
+ status (Optional[str]): Task status.
163
+ active (Optional[bool]): Task active state.
164
+ thumbnail_id (Optional[str]): Task thumbnail id.
165
+ task_id (Optional[str]): Task id. If not passed new id is
166
+ generated.
167
+
168
+ Returns:
169
+ NewTaskDict: Skeleton of task entity.
170
+
171
+ """
172
+ if not task_id:
173
+ task_id = create_entity_id()
174
+ output = {
175
+ "id": task_id,
176
+ "name": name,
177
+ "taskType": task_type,
178
+ "folderId": folder_id,
179
+ }
180
+ for key, value in (
181
+ ("label", label),
182
+ ("attrib", attrib),
183
+ ("data", data),
184
+ ("tags", tags),
185
+ ("status", status),
186
+ ("assignees", assignees),
187
+ ("active", active),
188
+ ("thumbnailId", thumbnail_id),
189
+ ):
190
+ if value is not None:
191
+ output[key] = value
128
192
  return output
129
193
 
130
194
 
@@ -1046,6 +1110,10 @@ class OperationsSession(object):
1046
1110
  "taskType": task_type,
1047
1111
  "folderId": folder_id,
1048
1112
  }
1113
+ if tags is not None:
1114
+ tags = list(tags)
1115
+ if assignees is not None:
1116
+ assignees = list(assignees)
1049
1117
  for key, value in (
1050
1118
  ("label", label),
1051
1119
  ("attrib", attrib),
@@ -362,6 +362,21 @@ class NewFolderDict(TypedDict):
362
362
  tags: NotRequired[list[str]]
363
363
 
364
364
 
365
+ class NewTaskDict(TypedDict):
366
+ id: str
367
+ name: str
368
+ task_type: str
369
+ folder_id: str
370
+ label: NotRequired[str]
371
+ assignees: NotRequired[list[str]]
372
+ attrib: NotRequired[dict[str, Any]]
373
+ data: NotRequired[dict[str, Any]]
374
+ thumbnailId: NotRequired[str]
375
+ active: NotRequired[bool]
376
+ status: NotRequired[str]
377
+ tags: NotRequired[list[str]]
378
+
379
+
365
380
  class NewProductDict(TypedDict):
366
381
  id: str
367
382
  name: str
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.3"
2
+ __version__ = "1.2.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ayon-python-api
3
- Version: 1.2.3
3
+ Version: 1.2.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 = "1.2.3"
3
+ version = "1.2.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