ayon-python-api 1.2.8__tar.gz → 1.2.9__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 (49) hide show
  1. {ayon_python_api-1.2.8/ayon_python_api.egg-info → ayon_python_api-1.2.9}/PKG-INFO +1 -1
  2. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api.py +28 -1
  3. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/base.py +6 -1
  4. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/folders.py +9 -5
  5. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/products.py +14 -7
  6. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/representations.py +17 -9
  7. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/tasks.py +20 -7
  8. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/versions.py +14 -6
  9. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/graphql_queries.py +12 -0
  10. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/server_api.py +10 -0
  11. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/typing.py +20 -0
  12. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/version.py +1 -1
  13. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9/ayon_python_api.egg-info}/PKG-INFO +1 -1
  14. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/pyproject.toml +1 -1
  15. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/LICENSE +0 -0
  16. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/README.md +0 -0
  17. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/__init__.py +0 -0
  18. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/__init__.py +0 -0
  19. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/actions.py +0 -0
  20. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/activities.py +0 -0
  21. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/attributes.py +0 -0
  22. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/bundles_addons.py +0 -0
  23. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/dependency_packages.py +0 -0
  24. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/events.py +0 -0
  25. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/installers.py +0 -0
  26. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/links.py +0 -0
  27. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/lists.py +0 -0
  28. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/projects.py +0 -0
  29. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/secrets.py +0 -0
  30. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/thumbnails.py +0 -0
  31. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/_api_helpers/workfiles.py +0 -0
  32. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/constants.py +0 -0
  33. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/entity_hub.py +0 -0
  34. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/events.py +0 -0
  35. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/exceptions.py +0 -0
  36. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/graphql.py +0 -0
  37. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/operations.py +0 -0
  38. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_api/utils.py +0 -0
  39. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_python_api.egg-info/SOURCES.txt +0 -0
  40. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_python_api.egg-info/dependency_links.txt +0 -0
  41. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_python_api.egg-info/requires.txt +0 -0
  42. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/ayon_python_api.egg-info/top_level.txt +0 -0
  43. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/setup.cfg +0 -0
  44. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/setup.py +0 -0
  45. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/tests/test_entity_hub.py +0 -0
  46. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/tests/test_folder_hierarchy.py +0 -0
  47. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/tests/test_get_events.py +0 -0
  48. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/tests/test_graphql_queries.py +0 -0
  49. {ayon_python_api-1.2.8 → ayon_python_api-1.2.9}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.8
3
+ Version: 1.2.9
4
4
  Summary: AYON Python API
5
5
  Home-page: https://github.com/ynput/ayon-python-api
6
6
  Author: ynput.io
@@ -84,6 +84,7 @@ if typing.TYPE_CHECKING:
84
84
  ActionModeType,
85
85
  StreamType,
86
86
  EntityListAttributeDefinitionDict,
87
+ AdvancedFilterDict,
87
88
  )
88
89
  from ._api_helpers.links import CreateLinkData
89
90
 
@@ -4218,6 +4219,7 @@ def get_folders(
4218
4219
  tags: Optional[Iterable[str]] = None,
4219
4220
  active: Optional[bool] = True,
4220
4221
  has_links: Optional[bool] = None,
4222
+ filters: Optional[AdvancedFilterDict] = None,
4221
4223
  fields: Optional[Iterable[str]] = None,
4222
4224
  own_attributes: bool = False,
4223
4225
  ) -> Generator[FolderDict, None, None]:
@@ -4259,6 +4261,7 @@ def get_folders(
4259
4261
  Both are returned if is set to None.
4260
4262
  has_links (Optional[Literal[IN, OUT, ANY]]): Filter
4261
4263
  representations with IN/OUT/ANY links.
4264
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
4262
4265
  fields (Optional[Iterable[str]]): Fields to be queried for
4263
4266
  folder. All possible folder fields are returned
4264
4267
  if 'None' is passed.
@@ -4286,6 +4289,7 @@ def get_folders(
4286
4289
  tags=tags,
4287
4290
  active=active,
4288
4291
  has_links=has_links,
4292
+ filters=filters,
4289
4293
  fields=fields,
4290
4294
  own_attributes=own_attributes,
4291
4295
  )
@@ -4571,6 +4575,7 @@ def get_tasks(
4571
4575
  statuses: Optional[Iterable[str]] = None,
4572
4576
  tags: Optional[Iterable[str]] = None,
4573
4577
  active: Optional[bool] = True,
4578
+ filters: Optional[AdvancedFilterDict] = None,
4574
4579
  fields: Optional[Iterable[str]] = None,
4575
4580
  own_attributes: bool = False,
4576
4581
  ) -> Generator[TaskDict, None, None]:
@@ -4595,6 +4600,7 @@ def get_tasks(
4595
4600
  filtering.
4596
4601
  active (Optional[bool]): Filter active/inactive tasks.
4597
4602
  Both are returned if is set to None.
4603
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
4598
4604
  fields (Optional[Iterable[str]]): Fields to be queried for
4599
4605
  folder. All possible folder fields are returned
4600
4606
  if 'None' is passed.
@@ -4617,6 +4623,7 @@ def get_tasks(
4617
4623
  statuses=statuses,
4618
4624
  tags=tags,
4619
4625
  active=active,
4626
+ filters=filters,
4620
4627
  fields=fields,
4621
4628
  own_attributes=own_attributes,
4622
4629
  )
@@ -4695,6 +4702,7 @@ def get_tasks_by_folder_paths(
4695
4702
  statuses: Optional[Iterable[str]] = None,
4696
4703
  tags: Optional[Iterable[str]] = None,
4697
4704
  active: Optional[bool] = True,
4705
+ filters: Optional[AdvancedFilterDict] = None,
4698
4706
  fields: Optional[Iterable[str]] = None,
4699
4707
  own_attributes: bool = False,
4700
4708
  ) -> dict[str, list[TaskDict]]:
@@ -4717,6 +4725,7 @@ def get_tasks_by_folder_paths(
4717
4725
  filtering.
4718
4726
  active (Optional[bool]): Filter active/inactive tasks.
4719
4727
  Both are returned if is set to None.
4728
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
4720
4729
  fields (Optional[Iterable[str]]): Fields to be queried for
4721
4730
  folder. All possible folder fields are returned
4722
4731
  if 'None' is passed.
@@ -4739,6 +4748,7 @@ def get_tasks_by_folder_paths(
4739
4748
  statuses=statuses,
4740
4749
  tags=tags,
4741
4750
  active=active,
4751
+ filters=filters,
4742
4752
  fields=fields,
4743
4753
  own_attributes=own_attributes,
4744
4754
  )
@@ -4988,6 +4998,7 @@ def get_products(
4988
4998
  statuses: Optional[Iterable[str]] = None,
4989
4999
  tags: Optional[Iterable[str]] = None,
4990
5000
  active: Optional[bool] = True,
5001
+ filters: Optional[AdvancedFilterDict] = None,
4991
5002
  fields: Optional[Iterable[str]] = None,
4992
5003
  own_attributes=_PLACEHOLDER,
4993
5004
  ) -> Generator[ProductDict, None, None]:
@@ -5019,6 +5030,7 @@ def get_products(
5019
5030
  for filtering.
5020
5031
  active (Optional[bool]): Filter active/inactive products.
5021
5032
  Both are returned if is set to None.
5033
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
5022
5034
  fields (Optional[Iterable[str]]): Fields to be queried for
5023
5035
  folder. All possible folder fields are returned
5024
5036
  if 'None' is passed.
@@ -5043,6 +5055,7 @@ def get_products(
5043
5055
  statuses=statuses,
5044
5056
  tags=tags,
5045
5057
  active=active,
5058
+ filters=filters,
5046
5059
  fields=fields,
5047
5060
  own_attributes=own_attributes,
5048
5061
  )
@@ -5325,6 +5338,7 @@ def get_versions(
5325
5338
  statuses: Optional[Iterable[str]] = None,
5326
5339
  tags: Optional[Iterable[str]] = None,
5327
5340
  active: Optional[bool] = True,
5341
+ filters: Optional[AdvancedFilterDict] = None,
5328
5342
  fields: Optional[Iterable[str]] = None,
5329
5343
  own_attributes=_PLACEHOLDER,
5330
5344
  ) -> Generator[VersionDict, None, None]:
@@ -5351,6 +5365,7 @@ def get_versions(
5351
5365
  for filtering.
5352
5366
  active (Optional[bool]): Receive active/inactive entities.
5353
5367
  Both are returned when 'None' is passed.
5368
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
5354
5369
  fields (Optional[Iterable[str]]): Fields to be queried
5355
5370
  for version. All possible folder fields are returned
5356
5371
  if 'None' is passed.
@@ -5374,6 +5389,7 @@ def get_versions(
5374
5389
  statuses=statuses,
5375
5390
  tags=tags,
5376
5391
  active=active,
5392
+ filters=filters,
5377
5393
  fields=fields,
5378
5394
  own_attributes=own_attributes,
5379
5395
  )
@@ -5819,6 +5835,7 @@ def get_representations(
5819
5835
  tags: Optional[Iterable[str]] = None,
5820
5836
  active: Optional[bool] = True,
5821
5837
  has_links: Optional[str] = None,
5838
+ filters: Optional[AdvancedFilterDict] = None,
5822
5839
  fields: Optional[Iterable[str]] = None,
5823
5840
  own_attributes=_PLACEHOLDER,
5824
5841
  ) -> Generator[RepresentationDict, None, None]:
@@ -5849,6 +5866,7 @@ def get_representations(
5849
5866
  Both are returned when 'None' is passed.
5850
5867
  has_links (Optional[Literal[IN, OUT, ANY]]): Filter
5851
5868
  representations with IN/OUT/ANY links.
5869
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
5852
5870
  fields (Optional[Iterable[str]]): Fields to be queried for
5853
5871
  representation. All possible fields are returned if 'None' is
5854
5872
  passed.
@@ -5871,6 +5889,7 @@ def get_representations(
5871
5889
  tags=tags,
5872
5890
  active=active,
5873
5891
  has_links=has_links,
5892
+ filters=filters,
5874
5893
  fields=fields,
5875
5894
  own_attributes=own_attributes,
5876
5895
  )
@@ -7282,7 +7301,15 @@ def get_entity_lists(
7282
7301
  active: Optional[bool] = None,
7283
7302
  fields: Optional[Iterable[str]] = None,
7284
7303
  ) -> Generator[dict[str, Any], None, None]:
7285
- """Fetch entity lists from server.
7304
+ """Fetch entity lists from AYON server.
7305
+
7306
+ Warnings:
7307
+ You can't get list items for lists with different 'entityType' in
7308
+ one call.
7309
+
7310
+ Notes:
7311
+ To get list items, you have to pass 'items' field or
7312
+ 'items.{sub-fields you want}' to 'fields' argument.
7286
7313
 
7287
7314
  Args:
7288
7315
  project_name (str): Project name where entity lists are.
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  import typing
5
- from typing import Optional, Any, Iterable
5
+ from typing import Optional, Any, Iterable, Union
6
6
 
7
7
  import requests
8
8
 
@@ -142,6 +142,11 @@ class BaseServerAPI:
142
142
  ):
143
143
  raise NotImplementedError()
144
144
 
145
+ def _prepare_advanced_filters(
146
+ self, filters: Union[str, dict[str, Any], None]
147
+ ) -> Optional[str]:
148
+ raise NotImplementedError()
149
+
145
150
  def _convert_entity_data(self, entity: AnyEntityDict):
146
151
  raise NotImplementedError()
147
152
 
@@ -21,6 +21,7 @@ if typing.TYPE_CHECKING:
21
21
  FolderDict,
22
22
  FlatFolderDict,
23
23
  ProjectHierarchyDict,
24
+ AdvancedFilterDict,
24
25
  )
25
26
 
26
27
 
@@ -216,6 +217,7 @@ class FoldersAPI(BaseServerAPI):
216
217
  tags: Optional[Iterable[str]] = None,
217
218
  active: Optional[bool] = True,
218
219
  has_links: Optional[bool] = None,
220
+ filters: Optional[AdvancedFilterDict] = None,
219
221
  fields: Optional[Iterable[str]] = None,
220
222
  own_attributes: bool = False
221
223
  ) -> Generator[FolderDict, None, None]:
@@ -257,6 +259,7 @@ class FoldersAPI(BaseServerAPI):
257
259
  Both are returned if is set to None.
258
260
  has_links (Optional[Literal[IN, OUT, ANY]]): Filter
259
261
  representations with IN/OUT/ANY links.
262
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
260
263
  fields (Optional[Iterable[str]]): Fields to be queried for
261
264
  folder. All possible folder fields are returned
262
265
  if 'None' is passed.
@@ -270,11 +273,11 @@ class FoldersAPI(BaseServerAPI):
270
273
  if not project_name:
271
274
  return
272
275
 
273
- filters = {
276
+ graphql_filters = {
274
277
  "projectName": project_name
275
278
  }
276
279
  if not prepare_list_filters(
277
- filters,
280
+ graphql_filters,
278
281
  ("folderIds", folder_ids),
279
282
  ("folderPaths", folder_paths),
280
283
  ("folderNames", folder_names),
@@ -291,9 +294,10 @@ class FoldersAPI(BaseServerAPI):
291
294
  ("folderHasTasks", has_tasks),
292
295
  ("folderHasLinks", has_links),
293
296
  ("folderHasChildren", has_children),
297
+ ("filter", self._prepare_advanced_filters(filters)),
294
298
  ):
295
299
  if filter_value is not None:
296
- filters[filter_key] = filter_value
300
+ graphql_filters[filter_key] = filter_value
297
301
 
298
302
  if parent_ids is not None:
299
303
  parent_ids = set(parent_ids)
@@ -313,7 +317,7 @@ class FoldersAPI(BaseServerAPI):
313
317
  parent_ids.remove(project_name)
314
318
  parent_ids.add("root")
315
319
 
316
- filters["parentFolderIds"] = list(parent_ids)
320
+ graphql_filters["parentFolderIds"] = list(parent_ids)
317
321
 
318
322
  if not fields:
319
323
  fields = self.get_default_fields_for_type("folder")
@@ -328,7 +332,7 @@ class FoldersAPI(BaseServerAPI):
328
332
  fields.add("ownAttrib")
329
333
 
330
334
  query = folders_graphql_query(fields)
331
- for attr, filter_value in filters.items():
335
+ for attr, filter_value in graphql_filters.items():
332
336
  query.set_variable_value(attr, filter_value)
333
337
 
334
338
  for parsed_data in query.continuous_query(self):
@@ -18,7 +18,11 @@ from ayon_api.graphql_queries import (
18
18
  from .base import BaseServerAPI, _PLACEHOLDER
19
19
 
20
20
  if typing.TYPE_CHECKING:
21
- from ayon_api.typing import ProductDict, ProductTypeDict
21
+ from ayon_api.typing import (
22
+ ProductDict,
23
+ ProductTypeDict,
24
+ AdvancedFilterDict,
25
+ )
22
26
 
23
27
 
24
28
  class ProductsAPI(BaseServerAPI):
@@ -43,6 +47,7 @@ class ProductsAPI(BaseServerAPI):
43
47
  statuses: Optional[Iterable[str]] = None,
44
48
  tags: Optional[Iterable[str]] = None,
45
49
  active: Optional[bool] = True,
50
+ filters: Optional[AdvancedFilterDict] = None,
46
51
  fields: Optional[Iterable[str]] = None,
47
52
  own_attributes=_PLACEHOLDER
48
53
  ) -> Generator[ProductDict, None, None]:
@@ -74,6 +79,7 @@ class ProductsAPI(BaseServerAPI):
74
79
  for filtering.
75
80
  active (Optional[bool]): Filter active/inactive products.
76
81
  Both are returned if is set to None.
82
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
77
83
  fields (Optional[Iterable[str]]): Fields to be queried for
78
84
  folder. All possible folder fields are returned
79
85
  if 'None' is passed.
@@ -145,18 +151,18 @@ class ProductsAPI(BaseServerAPI):
145
151
  fields.add("folderId")
146
152
 
147
153
  # Prepare filters for query
148
- filters = {
154
+ graphql_filters = {
149
155
  "projectName": project_name
150
156
  }
151
157
 
152
158
  if filter_folder_ids:
153
- filters["folderIds"] = list(filter_folder_ids)
159
+ graphql_filters["folderIds"] = list(filter_folder_ids)
154
160
 
155
161
  if filter_product_names:
156
- filters["productNames"] = list(filter_product_names)
162
+ graphql_filters["productNames"] = list(filter_product_names)
157
163
 
158
164
  if not prepare_list_filters(
159
- filters,
165
+ graphql_filters,
160
166
  ("productIds", product_ids),
161
167
  ("productTypes", product_types),
162
168
  ("productBaseTypes", product_base_types),
@@ -168,12 +174,13 @@ class ProductsAPI(BaseServerAPI):
168
174
  for filter_key, filter_value in (
169
175
  ("productNameRegex", product_name_regex),
170
176
  ("productPathRegex", product_path_regex),
177
+ ("filter", self._prepare_advanced_filters(filters)),
171
178
  ):
172
179
  if filter_value:
173
- filters[filter_key] = filter_value
180
+ graphql_filters[filter_key] = filter_value
174
181
 
175
182
  query = products_graphql_query(fields)
176
- for attr, filter_value in filters.items():
183
+ for attr, filter_value in graphql_filters.items():
177
184
  query.set_variable_value(attr, filter_value)
178
185
 
179
186
  parsed_data = query.query(self)
@@ -20,7 +20,7 @@ from ayon_api.graphql_queries import (
20
20
  from .base import BaseServerAPI, _PLACEHOLDER
21
21
 
22
22
  if typing.TYPE_CHECKING:
23
- from ayon_api.typing import RepresentationDict
23
+ from ayon_api.typing import RepresentationDict, AdvancedFilterDict
24
24
 
25
25
 
26
26
  class RepresentationsAPI(BaseServerAPI):
@@ -42,6 +42,7 @@ class RepresentationsAPI(BaseServerAPI):
42
42
  tags: Optional[Iterable[str]] = None,
43
43
  active: Optional[bool] = True,
44
44
  has_links: Optional[str] = None,
45
+ filters: Optional[AdvancedFilterDict] = None,
45
46
  fields: Optional[Iterable[str]] = None,
46
47
  own_attributes=_PLACEHOLDER,
47
48
  ) -> Generator[RepresentationDict, None, None]:
@@ -72,6 +73,7 @@ class RepresentationsAPI(BaseServerAPI):
72
73
  Both are returned when 'None' is passed.
73
74
  has_links (Optional[Literal[IN, OUT, ANY]]): Filter
74
75
  representations with IN/OUT/ANY links.
76
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
75
77
  fields (Optional[Iterable[str]]): Fields to be queried for
76
78
  representation. All possible fields are returned if 'None' is
77
79
  passed.
@@ -106,7 +108,7 @@ class RepresentationsAPI(BaseServerAPI):
106
108
  fields.discard("files")
107
109
  fields |= REPRESENTATION_FILES_FIELDS
108
110
 
109
- filters = {
111
+ graphql_filters = {
110
112
  "projectName": project_name
111
113
  }
112
114
 
@@ -114,7 +116,7 @@ class RepresentationsAPI(BaseServerAPI):
114
116
  representation_ids = set(representation_ids)
115
117
  if not representation_ids:
116
118
  return
117
- filters["representationIds"] = list(representation_ids)
119
+ graphql_filters["representationIds"] = list(representation_ids)
118
120
 
119
121
  version_ids_filter = None
120
122
  representation_names_filter = None
@@ -140,29 +142,35 @@ class RepresentationsAPI(BaseServerAPI):
140
142
  return
141
143
 
142
144
  if version_ids_filter:
143
- filters["versionIds"] = list(version_ids_filter)
145
+ graphql_filters["versionIds"] = list(version_ids_filter)
144
146
 
145
147
  if representation_names_filter:
146
- filters["representationNames"] = list(representation_names_filter)
148
+ graphql_filters["representationNames"] = list(
149
+ representation_names_filter
150
+ )
147
151
 
148
152
  if statuses is not None:
149
153
  statuses = set(statuses)
150
154
  if not statuses:
151
155
  return
152
- filters["representationStatuses"] = list(statuses)
156
+ graphql_filters["representationStatuses"] = list(statuses)
153
157
 
154
158
  if tags is not None:
155
159
  tags = set(tags)
156
160
  if not tags:
157
161
  return
158
- filters["representationTags"] = list(tags)
162
+ graphql_filters["representationTags"] = list(tags)
159
163
 
160
164
  if has_links is not None:
161
- filters["representationHasLinks"] = has_links.upper()
165
+ graphql_filters["representationHasLinks"] = has_links.upper()
166
+
167
+ filters = self._prepare_advanced_filters(filters)
168
+ if filters:
169
+ graphql_filters["filter"] = filters
162
170
 
163
171
  query = representations_graphql_query(fields)
164
172
 
165
- for attr, filter_value in filters.items():
173
+ for attr, filter_value in graphql_filters.items():
166
174
  query.set_variable_value(attr, filter_value)
167
175
 
168
176
  for parsed_data in query.continuous_query(self):
@@ -17,7 +17,7 @@ from ayon_api.graphql_queries import (
17
17
  from .base import BaseServerAPI
18
18
 
19
19
  if typing.TYPE_CHECKING:
20
- from ayon_api.typing import TaskDict
20
+ from ayon_api.typing import TaskDict, AdvancedFilterDict
21
21
 
22
22
 
23
23
  class TasksAPI(BaseServerAPI):
@@ -38,6 +38,7 @@ class TasksAPI(BaseServerAPI):
38
38
  statuses: Optional[Iterable[str]] = None,
39
39
  tags: Optional[Iterable[str]] = None,
40
40
  active: Optional[bool] = True,
41
+ filters: Optional[AdvancedFilterDict] = None,
41
42
  fields: Optional[Iterable[str]] = None,
42
43
  own_attributes: bool = False
43
44
  ) -> Generator[TaskDict, None, None]:
@@ -62,6 +63,7 @@ class TasksAPI(BaseServerAPI):
62
63
  filtering.
63
64
  active (Optional[bool]): Filter active/inactive tasks.
64
65
  Both are returned if is set to None.
66
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
65
67
  fields (Optional[Iterable[str]]): Fields to be queried for
66
68
  folder. All possible folder fields are returned
67
69
  if 'None' is passed.
@@ -75,11 +77,11 @@ class TasksAPI(BaseServerAPI):
75
77
  if not project_name:
76
78
  return
77
79
 
78
- filters = {
80
+ graphql_filters = {
79
81
  "projectName": project_name
80
82
  }
81
83
  if not prepare_list_filters(
82
- filters,
84
+ graphql_filters,
83
85
  ("taskIds", task_ids),
84
86
  ("taskNames", task_names),
85
87
  ("taskTypes", task_types),
@@ -91,6 +93,10 @@ class TasksAPI(BaseServerAPI):
91
93
  ):
92
94
  return
93
95
 
96
+ filters = self._prepare_advanced_filters(filters)
97
+ if filters:
98
+ graphql_filters["filter"] = filters
99
+
94
100
  if not fields:
95
101
  fields = self.get_default_fields_for_type("task")
96
102
  else:
@@ -101,7 +107,7 @@ class TasksAPI(BaseServerAPI):
101
107
  fields.add("active")
102
108
 
103
109
  query = tasks_graphql_query(fields)
104
- for attr, filter_value in filters.items():
110
+ for attr, filter_value in graphql_filters.items():
105
111
  query.set_variable_value(attr, filter_value)
106
112
 
107
113
  for parsed_data in query.continuous_query(self):
@@ -193,6 +199,7 @@ class TasksAPI(BaseServerAPI):
193
199
  statuses: Optional[Iterable[str]] = None,
194
200
  tags: Optional[Iterable[str]] = None,
195
201
  active: Optional[bool] = True,
202
+ filters: Optional[AdvancedFilterDict] = None,
196
203
  fields: Optional[Iterable[str]] = None,
197
204
  own_attributes: bool = False
198
205
  ) -> dict[str, list[TaskDict]]:
@@ -215,6 +222,7 @@ class TasksAPI(BaseServerAPI):
215
222
  filtering.
216
223
  active (Optional[bool]): Filter active/inactive tasks.
217
224
  Both are returned if is set to None.
225
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
218
226
  fields (Optional[Iterable[str]]): Fields to be queried for
219
227
  folder. All possible folder fields are returned
220
228
  if 'None' is passed.
@@ -230,12 +238,13 @@ class TasksAPI(BaseServerAPI):
230
238
  if not project_name or not folder_paths:
231
239
  return {}
232
240
 
233
- filters = {
241
+ graphql_filters = {
234
242
  "projectName": project_name,
235
243
  "folderPaths": list(folder_paths),
236
244
  }
245
+
237
246
  if not prepare_list_filters(
238
- filters,
247
+ graphql_filters,
239
248
  ("taskNames", task_names),
240
249
  ("taskTypes", task_types),
241
250
  ("taskAssigneesAny", assignees),
@@ -245,6 +254,10 @@ class TasksAPI(BaseServerAPI):
245
254
  ):
246
255
  return {}
247
256
 
257
+ filters = self._prepare_advanced_filters(filters)
258
+ if filters:
259
+ graphql_filters["filter"] = filters
260
+
248
261
  if not fields:
249
262
  fields = self.get_default_fields_for_type("task")
250
263
  else:
@@ -255,7 +268,7 @@ class TasksAPI(BaseServerAPI):
255
268
  fields.add("active")
256
269
 
257
270
  query = tasks_by_folder_paths_graphql_query(fields)
258
- for attr, filter_value in filters.items():
271
+ for attr, filter_value in graphql_filters.items():
259
272
  query.set_variable_value(attr, filter_value)
260
273
 
261
274
  output = {
@@ -15,7 +15,7 @@ from ayon_api.graphql_queries import versions_graphql_query
15
15
  from .base import BaseServerAPI, _PLACEHOLDER
16
16
 
17
17
  if typing.TYPE_CHECKING:
18
- from ayon_api.typing import VersionDict
18
+ from ayon_api.typing import VersionDict, AdvancedFilterDict
19
19
 
20
20
 
21
21
  class VersionsAPI(BaseServerAPI):
@@ -37,6 +37,7 @@ class VersionsAPI(BaseServerAPI):
37
37
  statuses: Optional[Iterable[str]] = None,
38
38
  tags: Optional[Iterable[str]] = None,
39
39
  active: Optional[bool] = True,
40
+ filters: Optional[AdvancedFilterDict] = None,
40
41
  fields: Optional[Iterable[str]] = None,
41
42
  own_attributes=_PLACEHOLDER
42
43
  ) -> Generator[VersionDict, None, None]:
@@ -63,6 +64,7 @@ class VersionsAPI(BaseServerAPI):
63
64
  for filtering.
64
65
  active (Optional[bool]): Receive active/inactive entities.
65
66
  Both are returned when 'None' is passed.
67
+ filters (Optional[AdvancedFilterDict]): Advanced filtering options.
66
68
  fields (Optional[Iterable[str]]): Fields to be queried
67
69
  for version. All possible folder fields are returned
68
70
  if 'None' is passed.
@@ -98,11 +100,12 @@ class VersionsAPI(BaseServerAPI):
98
100
  if not hero and not standard:
99
101
  return
100
102
 
101
- filters = {
103
+ graphql_filters = {
102
104
  "projectName": project_name
103
105
  }
106
+
104
107
  if not prepare_list_filters(
105
- filters,
108
+ graphql_filters,
106
109
  ("taskIds", task_ids),
107
110
  ("versionIds", version_ids),
108
111
  ("productIds", product_ids),
@@ -113,6 +116,11 @@ class VersionsAPI(BaseServerAPI):
113
116
  ):
114
117
  return
115
118
 
119
+
120
+ filters = self._prepare_advanced_filters(filters)
121
+ if filters:
122
+ graphql_filters["filter"] = filters
123
+
116
124
  queries = []
117
125
  # Add filters based on 'hero' and 'standard'
118
126
  # NOTE: There is not a filter to "ignore" hero versions or to get
@@ -123,14 +131,14 @@ class VersionsAPI(BaseServerAPI):
123
131
  # This query all versions standard + hero
124
132
  # - hero must be filtered out if is not enabled during loop
125
133
  query = versions_graphql_query(fields)
126
- for attr, filter_value in filters.items():
134
+ for attr, filter_value in graphql_filters.items():
127
135
  query.set_variable_value(attr, filter_value)
128
136
  queries.append(query)
129
137
  else:
130
138
  if hero:
131
139
  # Add hero query if hero is enabled
132
140
  hero_query = versions_graphql_query(fields)
133
- for attr, filter_value in filters.items():
141
+ for attr, filter_value in graphql_filters.items():
134
142
  hero_query.set_variable_value(attr, filter_value)
135
143
 
136
144
  hero_query.set_variable_value("heroOnly", True)
@@ -138,7 +146,7 @@ class VersionsAPI(BaseServerAPI):
138
146
 
139
147
  if standard:
140
148
  standard_query = versions_graphql_query(fields)
141
- for attr, filter_value in filters.items():
149
+ for attr, filter_value in graphql_filters.items():
142
150
  standard_query.set_variable_value(attr, filter_value)
143
151
 
144
152
  if latest:
@@ -139,6 +139,7 @@ def folders_graphql_query(fields):
139
139
  "folderAssigneesAll", "[String!]"
140
140
  )
141
141
  tags_var = query.add_variable("folderTags", "[String!]")
142
+ filter_var = query.add_variable("filter", "String!")
142
143
 
143
144
  project_field = query.add_field("project")
144
145
  project_field.set_filter("name", project_name_var)
@@ -157,6 +158,7 @@ def folders_graphql_query(fields):
157
158
  folders_field.set_filter("hasTasks", has_tasks_var)
158
159
  folders_field.set_filter("hasLinks", has_links_var)
159
160
  folders_field.set_filter("hasChildren", has_children_var)
161
+ folders_field.set_filter("filter", filter_var)
160
162
 
161
163
  nested_fields = fields_to_dict(fields)
162
164
  add_links_fields(folders_field, nested_fields)
@@ -188,6 +190,7 @@ def tasks_graphql_query(fields):
188
190
  assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]")
189
191
  statuses_var = query.add_variable("taskStatuses", "[String!]")
190
192
  tags_var = query.add_variable("taskTags", "[String!]")
193
+ filter_var = query.add_variable("filter", "String!")
191
194
 
192
195
  project_field = query.add_field("project")
193
196
  project_field.set_filter("name", project_name_var)
@@ -203,6 +206,7 @@ def tasks_graphql_query(fields):
203
206
  tasks_field.set_filter("assignees", assignees_all_var)
204
207
  tasks_field.set_filter("statuses", statuses_var)
205
208
  tasks_field.set_filter("tags", tags_var)
209
+ tasks_field.set_filter("filter", filter_var)
206
210
 
207
211
  nested_fields = fields_to_dict(fields)
208
212
  add_links_fields(tasks_field, nested_fields)
@@ -233,6 +237,7 @@ def tasks_by_folder_paths_graphql_query(fields):
233
237
  assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]")
234
238
  statuses_var = query.add_variable("taskStatuses", "[String!]")
235
239
  tags_var = query.add_variable("taskTags", "[String!]")
240
+ filter_var = query.add_variable("filter", "String!")
236
241
 
237
242
  project_field = query.add_field("project")
238
243
  project_field.set_filter("name", project_name_var)
@@ -250,6 +255,7 @@ def tasks_by_folder_paths_graphql_query(fields):
250
255
  tasks_field.set_filter("assignees", assignees_all_var)
251
256
  tasks_field.set_filter("statuses", statuses_var)
252
257
  tasks_field.set_filter("tags", tags_var)
258
+ tasks_field.set_filter("filter", filter_var)
253
259
 
254
260
  nested_fields = fields_to_dict(fields)
255
261
  add_links_fields(tasks_field, nested_fields)
@@ -284,6 +290,7 @@ def products_graphql_query(fields):
284
290
  product_path_regex_var = query.add_variable("productPathRegex", "String!")
285
291
  statuses_var = query.add_variable("productStatuses", "[String!]")
286
292
  tags_var = query.add_variable("productTags", "[String!]")
293
+ filter_var = query.add_variable("filter", "String!")
287
294
 
288
295
  project_field = query.add_field("project")
289
296
  project_field.set_filter("name", project_name_var)
@@ -298,6 +305,7 @@ def products_graphql_query(fields):
298
305
  products_field.set_filter("tags", tags_var)
299
306
  products_field.set_filter("nameEx", product_name_regex_var)
300
307
  products_field.set_filter("pathEx", product_path_regex_var)
308
+ products_field.set_filter("filter", filter_var)
301
309
 
302
310
  nested_fields = fields_to_dict(set(fields))
303
311
  add_links_fields(products_field, nested_fields)
@@ -333,6 +341,7 @@ def versions_graphql_query(fields):
333
341
  )
334
342
  statuses_var = query.add_variable("versionStatuses", "[String!]")
335
343
  tags_var = query.add_variable("versionTags", "[String!]")
344
+ filter_var = query.add_variable("filter", "String!")
336
345
 
337
346
  project_field = query.add_field("project")
338
347
  project_field.set_filter("name", project_name_var)
@@ -347,6 +356,7 @@ def versions_graphql_query(fields):
347
356
  versions_field.set_filter("heroOrLatestOnly", hero_or_latest_only_var)
348
357
  versions_field.set_filter("statuses", statuses_var)
349
358
  versions_field.set_filter("tags", tags_var)
359
+ versions_field.set_filter("filter", filter_var)
350
360
 
351
361
  nested_fields = fields_to_dict(set(fields))
352
362
  add_links_fields(versions_field, nested_fields)
@@ -383,6 +393,7 @@ def representations_graphql_query(fields):
383
393
  tags_var = query.add_variable(
384
394
  "representationTags", "[String!]"
385
395
  )
396
+ filter_var = query.add_variable("filter", "String!")
386
397
 
387
398
  project_field = query.add_field("project")
388
399
  project_field.set_filter("name", project_name_var)
@@ -394,6 +405,7 @@ def representations_graphql_query(fields):
394
405
  repres_field.set_filter("hasLinks", has_links_var)
395
406
  repres_field.set_filter("statuses", statuses_var)
396
407
  repres_field.set_filter("tags", tags_var)
408
+ repres_field.set_filter("filter", filter_var)
397
409
 
398
410
  nested_fields = fields_to_dict(set(fields))
399
411
  add_links_fields(repres_field, nested_fields)
@@ -2265,6 +2265,16 @@ class ServerAPI(
2265
2265
  )
2266
2266
  }
2267
2267
 
2268
+ def _prepare_advanced_filters(
2269
+ self, filters: Union[str, dict[str, Any], None]
2270
+ ) -> Optional[str]:
2271
+ if not filters:
2272
+ return None
2273
+
2274
+ if isinstance(filters, dict):
2275
+ return json.dumps(filters)
2276
+ return filters
2277
+
2268
2278
  def _convert_entity_data(self, entity: AnyEntityDict):
2269
2279
  if not entity or "data" not in entity:
2270
2280
  return
@@ -602,3 +602,23 @@ StreamType = Union[io.BytesIO, BinaryIO]
602
602
  class EntityListAttributeDefinitionDict(TypedDict):
603
603
  name: str
604
604
  data: dict[str, Any]
605
+
606
+
607
+ AdvancedFilterOperator = Literal["and", "or"]
608
+ AdvancedFilterConditionOperator = Literal[
609
+ "eq", "lt", "gt", "lte", "gte", "ne",
610
+ "isnull", "notnull",
611
+ "in", "notin", "contains", "excludes",
612
+ "any", "like"
613
+ ]
614
+
615
+
616
+ class AdvancedFilterConditionDict(TypedDict):
617
+ key: str
618
+ value: Any
619
+ operator: AdvancedFilterConditionOperator
620
+
621
+
622
+ class AdvancedFilterDict(TypedDict):
623
+ conditions: list[Union[AdvancedFilterConditionDict, "AdvancedFilterDict"]]
624
+ operator: AdvancedFilterOperator
@@ -1,2 +1,2 @@
1
1
  """Package declaring Python API for AYON server."""
2
- __version__ = "1.2.8"
2
+ __version__ = "1.2.9"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayon_python_api
3
- Version: 1.2.8
3
+ Version: 1.2.9
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.8"
3
+ version = "1.2.9"
4
4
  description = "AYON Python API"
5
5
  license = {file = "LICENSE"}
6
6
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes