ayon-python-api 1.2.0__tar.gz → 1.2.1__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.
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/PKG-INFO +1 -1
- ayon-python-api-1.2.1/ayon_api/_api_helpers/__init__.py +42 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/actions.py +303 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/activities.py +295 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/attributes.py +159 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/base.py +130 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/bundles_addons.py +885 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/dependency_packages.py +236 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/events.py +440 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/folders.py +638 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/installers.py +174 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/links.py +661 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/lists.py +417 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/products.py +504 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/projects.py +737 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/representations.py +747 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/secrets.py +81 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/tasks.py +515 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/thumbnails.py +307 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/versions.py +640 -0
- ayon-python-api-1.2.1/ayon_api/_api_helpers/workfiles.py +265 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/version.py +1 -1
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_python_api.egg-info/PKG-INFO +1 -1
- ayon-python-api-1.2.1/ayon_python_api.egg-info/SOURCES.txt +42 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/pyproject.toml +4 -3
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/setup.py +1 -1
- ayon-python-api-1.2.0/ayon_python_api.egg-info/SOURCES.txt +0 -22
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/LICENSE +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/README.md +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/__init__.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/_api.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/constants.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/entity_hub.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/events.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/exceptions.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/graphql.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/graphql_queries.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/operations.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/server_api.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/typing.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_api/utils.py +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_python_api.egg-info/dependency_links.txt +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_python_api.egg-info/requires.txt +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/ayon_python_api.egg-info/top_level.txt +0 -0
- {ayon-python-api-1.2.0 → ayon-python-api-1.2.1}/setup.cfg +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .base import BaseServerAPI
|
|
2
|
+
from .installers import InstallersAPI
|
|
3
|
+
from .dependency_packages import DependencyPackagesAPI
|
|
4
|
+
from .secrets import SecretsAPI
|
|
5
|
+
from .bundles_addons import BundlesAddonsAPI
|
|
6
|
+
from .events import EventsAPI
|
|
7
|
+
from .attributes import AttributesAPI
|
|
8
|
+
from .projects import ProjectsAPI
|
|
9
|
+
from .folders import FoldersAPI
|
|
10
|
+
from .tasks import TasksAPI
|
|
11
|
+
from .products import ProductsAPI
|
|
12
|
+
from .versions import VersionsAPI
|
|
13
|
+
from .representations import RepresentationsAPI
|
|
14
|
+
from .workfiles import WorkfilesAPI
|
|
15
|
+
from .thumbnails import ThumbnailsAPI
|
|
16
|
+
from .activities import ActivitiesAPI
|
|
17
|
+
from .actions import ActionsAPI
|
|
18
|
+
from .links import LinksAPI
|
|
19
|
+
from .lists import ListsAPI
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = (
|
|
23
|
+
"BaseServerAPI",
|
|
24
|
+
"InstallersAPI",
|
|
25
|
+
"DependencyPackagesAPI",
|
|
26
|
+
"SecretsAPI",
|
|
27
|
+
"BundlesAddonsAPI",
|
|
28
|
+
"EventsAPI",
|
|
29
|
+
"AttributesAPI",
|
|
30
|
+
"ProjectsAPI",
|
|
31
|
+
"FoldersAPI",
|
|
32
|
+
"TasksAPI",
|
|
33
|
+
"ProductsAPI",
|
|
34
|
+
"VersionsAPI",
|
|
35
|
+
"RepresentationsAPI",
|
|
36
|
+
"WorkfilesAPI",
|
|
37
|
+
"ThumbnailsAPI",
|
|
38
|
+
"ActivitiesAPI",
|
|
39
|
+
"ActionsAPI",
|
|
40
|
+
"LinksAPI",
|
|
41
|
+
"ListsAPI",
|
|
42
|
+
)
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from typing import Optional, Any
|
|
5
|
+
|
|
6
|
+
from ayon_api.utils import prepare_query_string
|
|
7
|
+
|
|
8
|
+
from .base import BaseServerAPI
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from ayon_api.typing import (
|
|
12
|
+
ActionEntityTypes,
|
|
13
|
+
ActionManifestDict,
|
|
14
|
+
ActionTriggerResponse,
|
|
15
|
+
ActionTakeResponse,
|
|
16
|
+
ActionConfigResponse,
|
|
17
|
+
ActionModeType,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ActionsAPI(BaseServerAPI):
|
|
22
|
+
"""Implementation of actions API for ServerAPI."""
|
|
23
|
+
def get_actions(
|
|
24
|
+
self,
|
|
25
|
+
project_name: Optional[str] = None,
|
|
26
|
+
entity_type: Optional[ActionEntityTypes] = None,
|
|
27
|
+
entity_ids: Optional[list[str]] = None,
|
|
28
|
+
entity_subtypes: Optional[list[str]] = None,
|
|
29
|
+
form_data: Optional[dict[str, Any]] = None,
|
|
30
|
+
*,
|
|
31
|
+
variant: Optional[str] = None,
|
|
32
|
+
mode: Optional[ActionModeType] = None,
|
|
33
|
+
) -> list[ActionManifestDict]:
|
|
34
|
+
"""Get actions for a context.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
project_name (Optional[str]): Name of the project. None for global
|
|
38
|
+
actions.
|
|
39
|
+
entity_type (Optional[ActionEntityTypes]): Entity type where the
|
|
40
|
+
action is triggered. None for global actions.
|
|
41
|
+
entity_ids (Optional[list[str]]): list of entity ids where the
|
|
42
|
+
action is triggered. None for global actions.
|
|
43
|
+
entity_subtypes (Optional[list[str]]): list of entity subtypes
|
|
44
|
+
folder types for folder ids, task types for tasks ids.
|
|
45
|
+
form_data (Optional[dict[str, Any]]): Form data of the action.
|
|
46
|
+
variant (Optional[str]): Settings variant.
|
|
47
|
+
mode (Optional[ActionModeType]): Action modes.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
list[ActionManifestDict]: list of action manifests.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
if variant is None:
|
|
54
|
+
variant = self.get_default_settings_variant()
|
|
55
|
+
query_data = {"variant": variant}
|
|
56
|
+
if mode:
|
|
57
|
+
query_data["mode"] = mode
|
|
58
|
+
query = prepare_query_string(query_data)
|
|
59
|
+
kwargs = {
|
|
60
|
+
key: value
|
|
61
|
+
for key, value in (
|
|
62
|
+
("projectName", project_name),
|
|
63
|
+
("entityType", entity_type),
|
|
64
|
+
("entityIds", entity_ids),
|
|
65
|
+
("entitySubtypes", entity_subtypes),
|
|
66
|
+
("formData", form_data),
|
|
67
|
+
)
|
|
68
|
+
if value is not None
|
|
69
|
+
}
|
|
70
|
+
response = self.post(f"actions/list{query}", **kwargs)
|
|
71
|
+
response.raise_for_status()
|
|
72
|
+
return response.data["actions"]
|
|
73
|
+
|
|
74
|
+
def trigger_action(
|
|
75
|
+
self,
|
|
76
|
+
identifier: str,
|
|
77
|
+
addon_name: str,
|
|
78
|
+
addon_version: str,
|
|
79
|
+
project_name: Optional[str] = None,
|
|
80
|
+
entity_type: Optional[ActionEntityTypes] = None,
|
|
81
|
+
entity_ids: Optional[list[str]] = None,
|
|
82
|
+
entity_subtypes: Optional[list[str]] = None,
|
|
83
|
+
form_data: Optional[dict[str, Any]] = None,
|
|
84
|
+
*,
|
|
85
|
+
variant: Optional[str] = None,
|
|
86
|
+
) -> ActionTriggerResponse:
|
|
87
|
+
"""Trigger action.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
identifier (str): Identifier of the action.
|
|
91
|
+
addon_name (str): Name of the addon.
|
|
92
|
+
addon_version (str): Version of the addon.
|
|
93
|
+
project_name (Optional[str]): Name of the project. None for global
|
|
94
|
+
actions.
|
|
95
|
+
entity_type (Optional[ActionEntityTypes]): Entity type where the
|
|
96
|
+
action is triggered. None for global actions.
|
|
97
|
+
entity_ids (Optional[list[str]]): list of entity ids where the
|
|
98
|
+
action is triggered. None for global actions.
|
|
99
|
+
entity_subtypes (Optional[list[str]]): list of entity subtypes
|
|
100
|
+
folder types for folder ids, task types for tasks ids.
|
|
101
|
+
form_data (Optional[dict[str, Any]]): Form data of the action.
|
|
102
|
+
variant (Optional[str]): Settings variant.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
if variant is None:
|
|
106
|
+
variant = self.get_default_settings_variant()
|
|
107
|
+
query_data = {
|
|
108
|
+
"addonName": addon_name,
|
|
109
|
+
"addonVersion": addon_version,
|
|
110
|
+
"identifier": identifier,
|
|
111
|
+
"variant": variant,
|
|
112
|
+
}
|
|
113
|
+
query = prepare_query_string(query_data)
|
|
114
|
+
|
|
115
|
+
kwargs = {
|
|
116
|
+
key: value
|
|
117
|
+
for key, value in (
|
|
118
|
+
("projectName", project_name),
|
|
119
|
+
("entityType", entity_type),
|
|
120
|
+
("entityIds", entity_ids),
|
|
121
|
+
("entitySubtypes", entity_subtypes),
|
|
122
|
+
("formData", form_data),
|
|
123
|
+
)
|
|
124
|
+
if value is not None
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
response = self.post(f"actions/execute{query}", **kwargs)
|
|
128
|
+
response.raise_for_status()
|
|
129
|
+
return response.data
|
|
130
|
+
|
|
131
|
+
def get_action_config(
|
|
132
|
+
self,
|
|
133
|
+
identifier: str,
|
|
134
|
+
addon_name: str,
|
|
135
|
+
addon_version: str,
|
|
136
|
+
project_name: Optional[str] = None,
|
|
137
|
+
entity_type: Optional[ActionEntityTypes] = None,
|
|
138
|
+
entity_ids: Optional[list[str]] = None,
|
|
139
|
+
entity_subtypes: Optional[list[str]] = None,
|
|
140
|
+
form_data: Optional[dict[str, Any]] = None,
|
|
141
|
+
*,
|
|
142
|
+
variant: Optional[str] = None,
|
|
143
|
+
) -> ActionConfigResponse:
|
|
144
|
+
"""Get action configuration.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
identifier (str): Identifier of the action.
|
|
148
|
+
addon_name (str): Name of the addon.
|
|
149
|
+
addon_version (str): Version of the addon.
|
|
150
|
+
project_name (Optional[str]): Name of the project. None for global
|
|
151
|
+
actions.
|
|
152
|
+
entity_type (Optional[ActionEntityTypes]): Entity type where the
|
|
153
|
+
action is triggered. None for global actions.
|
|
154
|
+
entity_ids (Optional[list[str]]): list of entity ids where the
|
|
155
|
+
action is triggered. None for global actions.
|
|
156
|
+
entity_subtypes (Optional[list[str]]): list of entity subtypes
|
|
157
|
+
folder types for folder ids, task types for tasks ids.
|
|
158
|
+
form_data (Optional[dict[str, Any]]): Form data of the action.
|
|
159
|
+
variant (Optional[str]): Settings variant.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
ActionConfigResponse: Action configuration data.
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
return self._send_config_request(
|
|
166
|
+
identifier,
|
|
167
|
+
addon_name,
|
|
168
|
+
addon_version,
|
|
169
|
+
None,
|
|
170
|
+
project_name,
|
|
171
|
+
entity_type,
|
|
172
|
+
entity_ids,
|
|
173
|
+
entity_subtypes,
|
|
174
|
+
form_data,
|
|
175
|
+
variant,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def set_action_config(
|
|
179
|
+
self,
|
|
180
|
+
identifier: str,
|
|
181
|
+
addon_name: str,
|
|
182
|
+
addon_version: str,
|
|
183
|
+
value: dict[str, Any],
|
|
184
|
+
project_name: Optional[str] = None,
|
|
185
|
+
entity_type: Optional[ActionEntityTypes] = None,
|
|
186
|
+
entity_ids: Optional[list[str]] = None,
|
|
187
|
+
entity_subtypes: Optional[list[str]] = None,
|
|
188
|
+
form_data: Optional[dict[str, Any]] = None,
|
|
189
|
+
*,
|
|
190
|
+
variant: Optional[str] = None,
|
|
191
|
+
) -> ActionConfigResponse:
|
|
192
|
+
"""Set action configuration.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
identifier (str): Identifier of the action.
|
|
196
|
+
addon_name (str): Name of the addon.
|
|
197
|
+
addon_version (str): Version of the addon.
|
|
198
|
+
value (Optional[dict[str, Any]]): Value of the action
|
|
199
|
+
configuration.
|
|
200
|
+
project_name (Optional[str]): Name of the project. None for global
|
|
201
|
+
actions.
|
|
202
|
+
entity_type (Optional[ActionEntityTypes]): Entity type where the
|
|
203
|
+
action is triggered. None for global actions.
|
|
204
|
+
entity_ids (Optional[list[str]]): list of entity ids where the
|
|
205
|
+
action is triggered. None for global actions.
|
|
206
|
+
entity_subtypes (Optional[list[str]]): list of entity subtypes
|
|
207
|
+
folder types for folder ids, task types for tasks ids.
|
|
208
|
+
form_data (Optional[dict[str, Any]]): Form data of the action.
|
|
209
|
+
variant (Optional[str]): Settings variant.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
ActionConfigResponse: New action configuration data.
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
return self._send_config_request(
|
|
216
|
+
identifier,
|
|
217
|
+
addon_name,
|
|
218
|
+
addon_version,
|
|
219
|
+
value,
|
|
220
|
+
project_name,
|
|
221
|
+
entity_type,
|
|
222
|
+
entity_ids,
|
|
223
|
+
entity_subtypes,
|
|
224
|
+
form_data,
|
|
225
|
+
variant,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def take_action(self, action_token: str) -> ActionTakeResponse:
|
|
229
|
+
"""Take action metadata using an action token.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
action_token (str): AYON launcher action token.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
ActionTakeResponse: Action metadata describing how to launch
|
|
236
|
+
action.
|
|
237
|
+
|
|
238
|
+
"""
|
|
239
|
+
response = self.get(f"actions/abort/{action_token}")
|
|
240
|
+
response.raise_for_status()
|
|
241
|
+
return response.data
|
|
242
|
+
|
|
243
|
+
def abort_action(
|
|
244
|
+
self,
|
|
245
|
+
action_token: str,
|
|
246
|
+
message: Optional[str] = None,
|
|
247
|
+
) -> None:
|
|
248
|
+
"""Abort action using an action token.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
action_token (str): AYON launcher action token.
|
|
252
|
+
message (Optional[str]): Message to display in the UI.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
if message is None:
|
|
256
|
+
message = "Action aborted"
|
|
257
|
+
response = self.post(
|
|
258
|
+
f"actions/abort/{action_token}",
|
|
259
|
+
message=message,
|
|
260
|
+
)
|
|
261
|
+
response.raise_for_status()
|
|
262
|
+
|
|
263
|
+
def _send_config_request(
|
|
264
|
+
self,
|
|
265
|
+
identifier: str,
|
|
266
|
+
addon_name: str,
|
|
267
|
+
addon_version: str,
|
|
268
|
+
value: Optional[dict[str, Any]],
|
|
269
|
+
project_name: Optional[str],
|
|
270
|
+
entity_type: Optional[ActionEntityTypes],
|
|
271
|
+
entity_ids: Optional[list[str]],
|
|
272
|
+
entity_subtypes: Optional[list[str]],
|
|
273
|
+
form_data: Optional[dict[str, Any]],
|
|
274
|
+
variant: Optional[str],
|
|
275
|
+
) -> ActionConfigResponse:
|
|
276
|
+
"""Set and get action configuration."""
|
|
277
|
+
if variant is None:
|
|
278
|
+
variant = self.get_default_settings_variant()
|
|
279
|
+
query_data = {
|
|
280
|
+
"addonName": addon_name,
|
|
281
|
+
"addonVersion": addon_version,
|
|
282
|
+
"identifier": identifier,
|
|
283
|
+
"variant": variant,
|
|
284
|
+
}
|
|
285
|
+
query = prepare_query_string(query_data)
|
|
286
|
+
|
|
287
|
+
kwargs = {
|
|
288
|
+
query_key: query_value
|
|
289
|
+
for query_key, query_value in (
|
|
290
|
+
("projectName", project_name),
|
|
291
|
+
("entityType", entity_type),
|
|
292
|
+
("entityIds", entity_ids),
|
|
293
|
+
("entitySubtypes", entity_subtypes),
|
|
294
|
+
("formData", form_data),
|
|
295
|
+
)
|
|
296
|
+
if query_value is not None
|
|
297
|
+
}
|
|
298
|
+
if value is not None:
|
|
299
|
+
kwargs["value"] = value
|
|
300
|
+
|
|
301
|
+
response = self.post(f"actions/config{query}", **kwargs)
|
|
302
|
+
response.raise_for_status()
|
|
303
|
+
return response.data
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import typing
|
|
5
|
+
from typing import Optional, Iterable, Generator, Any
|
|
6
|
+
|
|
7
|
+
from ayon_api.utils import (
|
|
8
|
+
SortOrder,
|
|
9
|
+
prepare_list_filters,
|
|
10
|
+
)
|
|
11
|
+
from ayon_api.graphql_queries import activities_graphql_query
|
|
12
|
+
|
|
13
|
+
from .base import BaseServerAPI
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from ayon_api.typing import (
|
|
17
|
+
ActivityType,
|
|
18
|
+
ActivityReferenceType,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ActivitiesAPI(BaseServerAPI):
|
|
23
|
+
def get_activities(
|
|
24
|
+
self,
|
|
25
|
+
project_name: str,
|
|
26
|
+
activity_ids: Optional[Iterable[str]] = None,
|
|
27
|
+
activity_types: Optional[Iterable[ActivityType]] = None,
|
|
28
|
+
entity_ids: Optional[Iterable[str]] = None,
|
|
29
|
+
entity_names: Optional[Iterable[str]] = None,
|
|
30
|
+
entity_type: Optional[str] = None,
|
|
31
|
+
changed_after: Optional[str] = None,
|
|
32
|
+
changed_before: Optional[str] = None,
|
|
33
|
+
reference_types: Optional[Iterable[ActivityReferenceType]] = None,
|
|
34
|
+
fields: Optional[Iterable[str]] = None,
|
|
35
|
+
limit: Optional[int] = None,
|
|
36
|
+
order: Optional[SortOrder] = None,
|
|
37
|
+
) -> Generator[dict[str, Any], None, None]:
|
|
38
|
+
"""Get activities from server with filtering options.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
project_name (str): Project on which activities happened.
|
|
42
|
+
activity_ids (Optional[Iterable[str]]): Activity ids.
|
|
43
|
+
activity_types (Optional[Iterable[ActivityType]]): Activity types.
|
|
44
|
+
entity_ids (Optional[Iterable[str]]): Entity ids.
|
|
45
|
+
entity_names (Optional[Iterable[str]]): Entity names.
|
|
46
|
+
entity_type (Optional[str]): Entity type.
|
|
47
|
+
changed_after (Optional[str]): Return only activities changed
|
|
48
|
+
after given iso datetime string.
|
|
49
|
+
changed_before (Optional[str]): Return only activities changed
|
|
50
|
+
before given iso datetime string.
|
|
51
|
+
reference_types (Optional[Iterable[ActivityReferenceType]]):
|
|
52
|
+
Reference types filter. Defaults to `['origin']`.
|
|
53
|
+
fields (Optional[Iterable[str]]): Fields that should be received
|
|
54
|
+
for each activity.
|
|
55
|
+
limit (Optional[int]): Limit number of activities to be fetched.
|
|
56
|
+
order (Optional[SortOrder]): Order activities in ascending
|
|
57
|
+
or descending order. It is recommended to set 'limit'
|
|
58
|
+
when used descending.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Generator[dict[str, Any]]: Available activities matching filters.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
if not project_name:
|
|
65
|
+
return
|
|
66
|
+
filters = {
|
|
67
|
+
"projectName": project_name,
|
|
68
|
+
}
|
|
69
|
+
if reference_types is None:
|
|
70
|
+
reference_types = {"origin"}
|
|
71
|
+
|
|
72
|
+
if not prepare_list_filters(
|
|
73
|
+
filters,
|
|
74
|
+
("activityIds", activity_ids),
|
|
75
|
+
("activityTypes", activity_types),
|
|
76
|
+
("entityIds", entity_ids),
|
|
77
|
+
("entityNames", entity_names),
|
|
78
|
+
("referenceTypes", reference_types),
|
|
79
|
+
):
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
for filter_key, filter_value in (
|
|
83
|
+
("entityType", entity_type),
|
|
84
|
+
("changedAfter", changed_after),
|
|
85
|
+
("changedBefore", changed_before),
|
|
86
|
+
):
|
|
87
|
+
if filter_value is not None:
|
|
88
|
+
filters[filter_key] = filter_value
|
|
89
|
+
|
|
90
|
+
if not fields:
|
|
91
|
+
fields = self.get_default_fields_for_type("activity")
|
|
92
|
+
|
|
93
|
+
query = activities_graphql_query(set(fields), order)
|
|
94
|
+
for attr, filter_value in filters.items():
|
|
95
|
+
query.set_variable_value(attr, filter_value)
|
|
96
|
+
|
|
97
|
+
if limit:
|
|
98
|
+
activities_field = query.get_field_by_path("activities")
|
|
99
|
+
activities_field.set_limit(limit)
|
|
100
|
+
|
|
101
|
+
for parsed_data in query.continuous_query(self):
|
|
102
|
+
for activity in parsed_data["project"]["activities"]:
|
|
103
|
+
activity_data = activity.get("activityData")
|
|
104
|
+
if isinstance(activity_data, str):
|
|
105
|
+
activity["activityData"] = json.loads(activity_data)
|
|
106
|
+
yield activity
|
|
107
|
+
|
|
108
|
+
def get_activity_by_id(
|
|
109
|
+
self,
|
|
110
|
+
project_name: str,
|
|
111
|
+
activity_id: str,
|
|
112
|
+
reference_types: Optional[Iterable[ActivityReferenceType]] = None,
|
|
113
|
+
fields: Optional[Iterable[str]] = None,
|
|
114
|
+
) -> Optional[dict[str, Any]]:
|
|
115
|
+
"""Get activity by id.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
project_name (str): Project on which activity happened.
|
|
119
|
+
activity_id (str): Activity id.
|
|
120
|
+
reference_types: Optional[Iterable[ActivityReferenceType]]: Filter
|
|
121
|
+
by reference types.
|
|
122
|
+
fields (Optional[Iterable[str]]): Fields that should be received
|
|
123
|
+
for each activity.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Optional[dict[str, Any]]: Activity data or None if activity is not
|
|
127
|
+
found.
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
for activity in self.get_activities(
|
|
131
|
+
project_name=project_name,
|
|
132
|
+
activity_ids={activity_id},
|
|
133
|
+
reference_types=reference_types,
|
|
134
|
+
fields=fields,
|
|
135
|
+
):
|
|
136
|
+
return activity
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
def create_activity(
|
|
140
|
+
self,
|
|
141
|
+
project_name: str,
|
|
142
|
+
entity_id: str,
|
|
143
|
+
entity_type: str,
|
|
144
|
+
activity_type: ActivityType,
|
|
145
|
+
activity_id: Optional[str] = None,
|
|
146
|
+
body: Optional[str] = None,
|
|
147
|
+
file_ids: Optional[list[str]] = None,
|
|
148
|
+
timestamp: Optional[str] = None,
|
|
149
|
+
data: Optional[dict[str, Any]] = None,
|
|
150
|
+
) -> str:
|
|
151
|
+
"""Create activity on a project.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
project_name (str): Project on which activity happened.
|
|
155
|
+
entity_id (str): Entity id.
|
|
156
|
+
entity_type (str): Entity type.
|
|
157
|
+
activity_type (ActivityType): Activity type.
|
|
158
|
+
activity_id (Optional[str]): Activity id.
|
|
159
|
+
body (Optional[str]): Activity body.
|
|
160
|
+
file_ids (Optional[list[str]]): List of file ids attached
|
|
161
|
+
to activity.
|
|
162
|
+
timestamp (Optional[str]): Activity timestamp.
|
|
163
|
+
data (Optional[dict[str, Any]]): Additional data.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
str: Activity id.
|
|
167
|
+
|
|
168
|
+
"""
|
|
169
|
+
post_data = {
|
|
170
|
+
"activityType": activity_type,
|
|
171
|
+
}
|
|
172
|
+
for key, value in (
|
|
173
|
+
("id", activity_id),
|
|
174
|
+
("body", body),
|
|
175
|
+
("files", file_ids),
|
|
176
|
+
("timestamp", timestamp),
|
|
177
|
+
("data", data),
|
|
178
|
+
):
|
|
179
|
+
if value is not None:
|
|
180
|
+
post_data[key] = value
|
|
181
|
+
|
|
182
|
+
response = self.post(
|
|
183
|
+
f"projects/{project_name}/{entity_type}/{entity_id}/activities",
|
|
184
|
+
**post_data
|
|
185
|
+
)
|
|
186
|
+
response.raise_for_status()
|
|
187
|
+
return response.data["id"]
|
|
188
|
+
|
|
189
|
+
def update_activity(
|
|
190
|
+
self,
|
|
191
|
+
project_name: str,
|
|
192
|
+
activity_id: str,
|
|
193
|
+
body: Optional[str] = None,
|
|
194
|
+
file_ids: Optional[list[str]] = None,
|
|
195
|
+
append_file_ids: Optional[bool] = False,
|
|
196
|
+
data: Optional[dict[str, Any]] = None,
|
|
197
|
+
) -> None:
|
|
198
|
+
"""Update activity by id.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
project_name (str): Project on which activity happened.
|
|
202
|
+
activity_id (str): Activity id.
|
|
203
|
+
body (str): Activity body.
|
|
204
|
+
file_ids (Optional[list[str]]): List of file ids attached
|
|
205
|
+
to activity.
|
|
206
|
+
append_file_ids (Optional[bool]): Append file ids to existing
|
|
207
|
+
list of file ids.
|
|
208
|
+
data (Optional[dict[str, Any]]): Update data in activity.
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
update_data = {}
|
|
212
|
+
major, minor, patch, _, _ = self.get_server_version_tuple()
|
|
213
|
+
new_patch_model = (major, minor, patch) > (1, 5, 6)
|
|
214
|
+
if body is None and not new_patch_model:
|
|
215
|
+
raise ValueError(
|
|
216
|
+
"Update without 'body' is supported"
|
|
217
|
+
" after server version 1.5.6."
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if body is not None:
|
|
221
|
+
update_data["body"] = body
|
|
222
|
+
|
|
223
|
+
if file_ids is not None:
|
|
224
|
+
update_data["files"] = file_ids
|
|
225
|
+
if new_patch_model:
|
|
226
|
+
update_data["appendFiles"] = append_file_ids
|
|
227
|
+
elif append_file_ids:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
"Append file ids is supported after server version 1.5.6."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if data is not None:
|
|
233
|
+
if not new_patch_model:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
"Update of data is supported after server version 1.5.6."
|
|
236
|
+
)
|
|
237
|
+
update_data["data"] = data
|
|
238
|
+
|
|
239
|
+
response = self.patch(
|
|
240
|
+
f"projects/{project_name}/activities/{activity_id}",
|
|
241
|
+
**update_data
|
|
242
|
+
)
|
|
243
|
+
response.raise_for_status()
|
|
244
|
+
|
|
245
|
+
def delete_activity(self, project_name: str, activity_id: str) -> None:
|
|
246
|
+
"""Delete activity by id.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
project_name (str): Project on which activity happened.
|
|
250
|
+
activity_id (str): Activity id to remove.
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
response = self.delete(
|
|
254
|
+
f"projects/{project_name}/activities/{activity_id}"
|
|
255
|
+
)
|
|
256
|
+
response.raise_for_status()
|
|
257
|
+
|
|
258
|
+
def send_activities_batch_operations(
|
|
259
|
+
self,
|
|
260
|
+
project_name: str,
|
|
261
|
+
operations: list[dict[str, Any]],
|
|
262
|
+
can_fail: bool = False,
|
|
263
|
+
raise_on_fail: bool = True
|
|
264
|
+
) -> list[dict[str, Any]]:
|
|
265
|
+
"""Post multiple CRUD activities operations to server.
|
|
266
|
+
|
|
267
|
+
When multiple changes should be made on server side this is the best
|
|
268
|
+
way to go. It is possible to pass multiple operations to process on a
|
|
269
|
+
server side and do the changes in a transaction.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
project_name (str): On which project should be operations
|
|
273
|
+
processed.
|
|
274
|
+
operations (list[dict[str, Any]]): Operations to be processed.
|
|
275
|
+
can_fail (Optional[bool]): Server will try to process all
|
|
276
|
+
operations even if one of them fails.
|
|
277
|
+
raise_on_fail (Optional[bool]): Raise exception if an operation
|
|
278
|
+
fails. You can handle failed operations on your own
|
|
279
|
+
when set to 'False'.
|
|
280
|
+
|
|
281
|
+
Raises:
|
|
282
|
+
ValueError: Operations can't be converted to json string.
|
|
283
|
+
FailedOperations: When output does not contain server operations
|
|
284
|
+
or 'raise_on_fail' is enabled and any operation fails.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
list[dict[str, Any]]: Operations result with process details.
|
|
288
|
+
|
|
289
|
+
"""
|
|
290
|
+
return self._send_batch_operations(
|
|
291
|
+
f"projects/{project_name}/operations/activities",
|
|
292
|
+
operations,
|
|
293
|
+
can_fail,
|
|
294
|
+
raise_on_fail,
|
|
295
|
+
)
|