anaplan-sdk 0.4.5__py3-none-any.whl → 0.5.0a1__py3-none-any.whl
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.
- anaplan_sdk/_async_clients/_alm.py +251 -40
- anaplan_sdk/_async_clients/_audit.py +6 -4
- anaplan_sdk/_async_clients/_bulk.py +130 -86
- anaplan_sdk/_async_clients/_cloud_works.py +23 -6
- anaplan_sdk/_async_clients/_cw_flow.py +12 -3
- anaplan_sdk/_async_clients/_transactional.py +231 -40
- anaplan_sdk/_base.py +115 -34
- anaplan_sdk/_clients/_alm.py +251 -41
- anaplan_sdk/_clients/_audit.py +6 -4
- anaplan_sdk/_clients/_bulk.py +136 -80
- anaplan_sdk/_clients/_cloud_works.py +24 -6
- anaplan_sdk/_clients/_cw_flow.py +12 -3
- anaplan_sdk/_clients/_transactional.py +223 -36
- anaplan_sdk/models/__init__.py +47 -2
- anaplan_sdk/models/_alm.py +64 -6
- anaplan_sdk/models/_bulk.py +14 -0
- anaplan_sdk/models/_transactional.py +221 -4
- {anaplan_sdk-0.4.5.dist-info → anaplan_sdk-0.5.0a1.dist-info}/METADATA +1 -1
- anaplan_sdk-0.5.0a1.dist-info/RECORD +30 -0
- anaplan_sdk-0.4.5.dist-info/RECORD +0 -30
- {anaplan_sdk-0.4.5.dist-info → anaplan_sdk-0.5.0a1.dist-info}/WHEEL +0 -0
- {anaplan_sdk-0.4.5.dist-info → anaplan_sdk-0.5.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,27 +1,70 @@
|
|
1
|
+
import logging
|
1
2
|
from concurrent.futures import ThreadPoolExecutor
|
2
3
|
from itertools import chain
|
3
|
-
from typing import Any
|
4
|
+
from typing import Any, Literal, overload
|
4
5
|
|
5
6
|
import httpx
|
6
7
|
|
7
|
-
from anaplan_sdk._base import
|
8
|
+
from anaplan_sdk._base import (
|
9
|
+
_BaseClient,
|
10
|
+
parse_calendar_response,
|
11
|
+
parse_insertion_response,
|
12
|
+
validate_dimension_id,
|
13
|
+
)
|
14
|
+
from anaplan_sdk.exceptions import InvalidIdentifierException
|
8
15
|
from anaplan_sdk.models import (
|
16
|
+
CurrentPeriod,
|
17
|
+
Dimension,
|
18
|
+
DimensionWithCode,
|
19
|
+
FiscalYear,
|
9
20
|
InsertionResult,
|
10
21
|
LineItem,
|
11
22
|
List,
|
12
23
|
ListItem,
|
13
24
|
ListMetadata,
|
25
|
+
Model,
|
26
|
+
ModelCalendar,
|
14
27
|
ModelStatus,
|
15
28
|
Module,
|
29
|
+
View,
|
30
|
+
ViewInfo,
|
16
31
|
)
|
32
|
+
from anaplan_sdk.models._transactional import ListDeletionResult
|
33
|
+
|
34
|
+
logger = logging.getLogger("anaplan_sdk")
|
17
35
|
|
18
36
|
|
19
37
|
class _TransactionalClient(_BaseClient):
|
20
38
|
def __init__(self, client: httpx.Client, model_id: str, retry_count: int) -> None:
|
21
39
|
self._url = f"https://api.anaplan.com/2/0/models/{model_id}"
|
40
|
+
self._model_id = model_id
|
22
41
|
super().__init__(retry_count, client)
|
23
42
|
|
24
|
-
def
|
43
|
+
def get_model_details(self) -> Model:
|
44
|
+
"""
|
45
|
+
Retrieves the Model details for the current Model.
|
46
|
+
:return: The Model details.
|
47
|
+
"""
|
48
|
+
return Model.model_validate(self._get(self._url, params={"modelDetails": "true"})["model"])
|
49
|
+
|
50
|
+
def get_model_status(self) -> ModelStatus:
|
51
|
+
"""
|
52
|
+
Gets the current status of the Model.
|
53
|
+
:return: The current status of the Mode.
|
54
|
+
"""
|
55
|
+
return ModelStatus.model_validate(self._get(f"{self._url}/status").get("requestStatus"))
|
56
|
+
|
57
|
+
def wake_model(self) -> None:
|
58
|
+
"""Wake up the current model."""
|
59
|
+
self._post_empty(f"{self._url}/open", headers={"Content-Type": "application/text"})
|
60
|
+
logger.info(f"Woke up model '{self._model_id}'.")
|
61
|
+
|
62
|
+
def close_model(self) -> None:
|
63
|
+
"""Close the current model."""
|
64
|
+
self._post_empty(f"{self._url}/close", headers={"Content-Type": "application/text"})
|
65
|
+
logger.info(f"Closed model '{self._model_id}'.")
|
66
|
+
|
67
|
+
def get_modules(self) -> list[Module]:
|
25
68
|
"""
|
26
69
|
Lists all the Modules in the Model.
|
27
70
|
:return: The List of Modules.
|
@@ -30,18 +73,27 @@ class _TransactionalClient(_BaseClient):
|
|
30
73
|
Module.model_validate(e) for e in self._get_paginated(f"{self._url}/modules", "modules")
|
31
74
|
]
|
32
75
|
|
33
|
-
def
|
76
|
+
def get_views(self) -> list[View]:
|
34
77
|
"""
|
35
|
-
|
36
|
-
|
78
|
+
Lists all the Views in the Model. This will include all Modules and potentially other saved
|
79
|
+
views.
|
80
|
+
:return: The List of Views.
|
37
81
|
"""
|
38
|
-
return
|
82
|
+
return [View.model_validate(e) for e in self._get_paginated(f"{self._url}/views", "views")]
|
83
|
+
|
84
|
+
def get_view_info(self, view_id: int) -> ViewInfo:
|
85
|
+
"""
|
86
|
+
Gets the detailed information about a View.
|
87
|
+
:param view_id: The ID of the View.
|
88
|
+
:return: The information about the View.
|
89
|
+
"""
|
90
|
+
return ViewInfo.model_validate(self._get(f"{self._url}/views/{view_id}"))
|
39
91
|
|
40
|
-
def
|
92
|
+
def get_line_items(self, only_module_id: int | None = None) -> list[LineItem]:
|
41
93
|
"""
|
42
94
|
Lists all the Line Items in the Model.
|
43
95
|
:param only_module_id: If provided, only Line Items from this Module will be returned.
|
44
|
-
:return:
|
96
|
+
:return: All Line Items on this Model or only from the specified Module.
|
45
97
|
"""
|
46
98
|
url = (
|
47
99
|
f"{self._url}/modules/{only_module_id}/lineItems?includeAll=true"
|
@@ -50,7 +102,7 @@ class _TransactionalClient(_BaseClient):
|
|
50
102
|
)
|
51
103
|
return [LineItem.model_validate(e) for e in self._get(url).get("items", [])]
|
52
104
|
|
53
|
-
def
|
105
|
+
def get_lists(self) -> list[List]:
|
54
106
|
"""
|
55
107
|
Lists all the Lists in the Model.
|
56
108
|
:return: All Lists on this model.
|
@@ -67,18 +119,31 @@ class _TransactionalClient(_BaseClient):
|
|
67
119
|
self._get(f"{self._url}/lists/{list_id}").get("metadata")
|
68
120
|
)
|
69
121
|
|
70
|
-
|
122
|
+
@overload
|
123
|
+
def get_list_items(
|
124
|
+
self, list_id: int, return_raw: Literal[False] = False
|
125
|
+
) -> list[ListItem]: ...
|
126
|
+
|
127
|
+
@overload
|
128
|
+
def get_list_items(
|
129
|
+
self, list_id: int, return_raw: Literal[True] = True
|
130
|
+
) -> list[dict[str, Any]]: ...
|
131
|
+
|
132
|
+
def get_list_items(
|
133
|
+
self, list_id: int, return_raw: bool = False
|
134
|
+
) -> list[ListItem] | list[dict[str, Any]]:
|
71
135
|
"""
|
72
136
|
Gets all the items in a List.
|
73
137
|
:param list_id: The ID of the List.
|
74
|
-
:
|
138
|
+
:param return_raw: If True, returns the items as a list of dictionaries instead of ListItem
|
139
|
+
objects. If you use the result of this call in a DataFrame or you simply pass on the
|
140
|
+
data, you will want to set this to avoid unnecessary (de-)serialization.
|
141
|
+
:return: All items in the List.
|
75
142
|
"""
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
)
|
81
|
-
]
|
143
|
+
res = self._get(f"{self._url}/lists/{list_id}/items?includeAll=true")
|
144
|
+
if return_raw:
|
145
|
+
return res.get("listItems", [])
|
146
|
+
return [ListItem.model_validate(e) for e in res.get("listItems", [])]
|
82
147
|
|
83
148
|
def insert_list_items(
|
84
149
|
self, list_id: int, items: list[dict[str, str | int | dict]]
|
@@ -100,10 +165,14 @@ class _TransactionalClient(_BaseClient):
|
|
100
165
|
:return: The result of the insertion, indicating how many items were added,
|
101
166
|
ignored or failed.
|
102
167
|
"""
|
168
|
+
if not items:
|
169
|
+
return InsertionResult(added=0, ignored=0, failures=[], total=0)
|
103
170
|
if len(items) <= 100_000:
|
104
|
-
|
171
|
+
result = InsertionResult.model_validate(
|
105
172
|
self._post(f"{self._url}/lists/{list_id}/items?action=add", json={"items": items})
|
106
173
|
)
|
174
|
+
logger.info(f"Inserted {result.added} items into list '{list_id}'.")
|
175
|
+
return result
|
107
176
|
|
108
177
|
with ThreadPoolExecutor() as executor:
|
109
178
|
responses = list(
|
@@ -114,19 +183,13 @@ class _TransactionalClient(_BaseClient):
|
|
114
183
|
[items[i : i + 100_000] for i in range(0, len(items), 100_000)],
|
115
184
|
)
|
116
185
|
)
|
186
|
+
result = parse_insertion_response(responses)
|
187
|
+
logger.info(f"Inserted {result.added} items into list '{list_id}'.")
|
188
|
+
return result
|
117
189
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
added += res.get("added", 0)
|
122
|
-
total += res.get("total", 0)
|
123
|
-
ignored += res.get("ignored", 0)
|
124
|
-
|
125
|
-
return InsertionResult(
|
126
|
-
added=added, ignored=ignored, total=total, failures=list(chain.from_iterable(failures))
|
127
|
-
)
|
128
|
-
|
129
|
-
def delete_list_items(self, list_id: int, items: list[dict[str, str | int]]) -> int:
|
190
|
+
def delete_list_items(
|
191
|
+
self, list_id: int, items: list[dict[str, str | int]]
|
192
|
+
) -> ListDeletionResult:
|
130
193
|
"""
|
131
194
|
Deletes items from a List. If you pass a long list, it will be split into chunks of 100,000
|
132
195
|
items, the maximum allowed by the API.
|
@@ -140,12 +203,18 @@ class _TransactionalClient(_BaseClient):
|
|
140
203
|
|
141
204
|
:param list_id: The ID of the List.
|
142
205
|
:param items: The items to delete from the List. Must be a dict with either `code` or `id`
|
143
|
-
as the keys to identify the records to delete.
|
206
|
+
as the keys to identify the records to delete. Specifying both will error.
|
207
|
+
:return: The result of the deletion, indicating how many items were deleted or failed.
|
144
208
|
"""
|
209
|
+
if not items:
|
210
|
+
return ListDeletionResult(deleted=0, failures=[])
|
145
211
|
if len(items) <= 100_000:
|
146
|
-
|
212
|
+
res = self._post(
|
147
213
|
f"{self._url}/lists/{list_id}/items?action=delete", json={"items": items}
|
148
|
-
)
|
214
|
+
)
|
215
|
+
info = ListDeletionResult.model_validate(res)
|
216
|
+
logger.info(f"Deleted {info.deleted} items from list '{list_id}'.")
|
217
|
+
return info
|
149
218
|
|
150
219
|
with ThreadPoolExecutor() as executor:
|
151
220
|
responses = list(
|
@@ -156,8 +225,12 @@ class _TransactionalClient(_BaseClient):
|
|
156
225
|
[items[i : i + 100_000] for i in range(0, len(items), 100_000)],
|
157
226
|
)
|
158
227
|
)
|
159
|
-
|
160
|
-
|
228
|
+
info = ListDeletionResult(
|
229
|
+
deleted=sum(res.get("deleted", 0) for res in responses),
|
230
|
+
failures=list(chain.from_iterable(res.get("failures", []) for res in responses)),
|
231
|
+
)
|
232
|
+
logger.info(f"Deleted {info} items from list '{list_id}'.")
|
233
|
+
return info
|
161
234
|
|
162
235
|
def reset_list_index(self, list_id: int) -> None:
|
163
236
|
"""
|
@@ -165,6 +238,7 @@ class _TransactionalClient(_BaseClient):
|
|
165
238
|
:param list_id: The ID of the List.
|
166
239
|
"""
|
167
240
|
self._post_empty(f"{self._url}/lists/{list_id}/resetIndex")
|
241
|
+
logger.info(f"Reset index for list '{list_id}'.")
|
168
242
|
|
169
243
|
def update_module_data(
|
170
244
|
self, module_id: int, data: list[dict[str, Any]]
|
@@ -184,4 +258,117 @@ class _TransactionalClient(_BaseClient):
|
|
184
258
|
:return: The number of cells changed or the response with the according error details.
|
185
259
|
"""
|
186
260
|
res = self._post(f"{self._url}/modules/{module_id}/data", json=data)
|
261
|
+
if "failures" not in res:
|
262
|
+
logger.info(f"Updated {res['numberOfCellsChanged']} cells in module '{module_id}'.")
|
187
263
|
return res if "failures" in res else res["numberOfCellsChanged"]
|
264
|
+
|
265
|
+
def get_current_period(self) -> CurrentPeriod:
|
266
|
+
"""
|
267
|
+
Gets the current period of the model.
|
268
|
+
:return: The current period of the model.
|
269
|
+
"""
|
270
|
+
res = self._get(f"{self._url}/currentPeriod")
|
271
|
+
return CurrentPeriod.model_validate(res["currentPeriod"])
|
272
|
+
|
273
|
+
def set_current_period(self, date: str) -> CurrentPeriod:
|
274
|
+
"""
|
275
|
+
Sets the current period of the model to the given date.
|
276
|
+
:param date: The date to set the current period to, in the format 'YYYY-MM-DD'.
|
277
|
+
:return: The updated current period of the model.
|
278
|
+
"""
|
279
|
+
res = self._put(f"{self._url}/currentPeriod", {"date": date})
|
280
|
+
logger.info(f"Set current period to '{date}'.")
|
281
|
+
return CurrentPeriod.model_validate(res["currentPeriod"])
|
282
|
+
|
283
|
+
def set_current_fiscal_year(self, year: str) -> FiscalYear:
|
284
|
+
"""
|
285
|
+
Sets the current fiscal year of the model.
|
286
|
+
:param year: The fiscal year to set, in the format specified in the model, e.g. FY24.
|
287
|
+
:return: The updated fiscal year of the model.
|
288
|
+
"""
|
289
|
+
res = self._put(f"{self._url}/modelCalendar/fiscalYear", {"year": year})
|
290
|
+
logger.info(f"Set current fiscal year to '{year}'.")
|
291
|
+
return FiscalYear.model_validate(res["modelCalendar"]["fiscalYear"])
|
292
|
+
|
293
|
+
def get_model_calendar(self) -> ModelCalendar:
|
294
|
+
"""
|
295
|
+
Get the calendar settings of the model.
|
296
|
+
:return: The calendar settings of the model.
|
297
|
+
"""
|
298
|
+
return parse_calendar_response(self._get(f"{self._url}/modelCalendar"))
|
299
|
+
|
300
|
+
def get_dimension_items(self, dimension_id: int) -> list[DimensionWithCode]:
|
301
|
+
"""
|
302
|
+
Get all items in a dimension. This will fail if the dimensions holds more than 1_000_000
|
303
|
+
items. Valid Dimensions are:
|
304
|
+
|
305
|
+
- Lists (101xxxxxxxxx)
|
306
|
+
- List Subsets (109xxxxxxxxx)
|
307
|
+
- Line Item Subsets (114xxxxxxxxx)
|
308
|
+
- Users (101999999999)
|
309
|
+
For lists and users, you should prefer using the `get_list_items` and `get_users` methods,
|
310
|
+
respectively, instead.
|
311
|
+
:param dimension_id: The ID of the dimension to list items for.
|
312
|
+
:return: A list of Dimension items.
|
313
|
+
"""
|
314
|
+
res = self._get(f"{self._url}/dimensions/{validate_dimension_id(dimension_id)}/items")
|
315
|
+
return [DimensionWithCode.model_validate(e) for e in res.get("items", [])]
|
316
|
+
|
317
|
+
def lookup_dimension_items(
|
318
|
+
self, dimension_id: int, codes: list[str] = None, names: list[str] = None
|
319
|
+
) -> list[DimensionWithCode]:
|
320
|
+
"""
|
321
|
+
Looks up items in a dimension by their codes or names. If both are provided, both will be
|
322
|
+
searched for. You must provide at least one of `codes` or `names`. Valid Dimensions to
|
323
|
+
lookup are:
|
324
|
+
|
325
|
+
- Lists (101xxxxxxxxx)
|
326
|
+
- Time (20000000003)
|
327
|
+
- Version (20000000020)
|
328
|
+
- Users (101999999999)
|
329
|
+
:param dimension_id: The ID of the dimension to lookup items for.
|
330
|
+
:param codes: A list of codes to lookup in the dimension.
|
331
|
+
:param names: A list of names to lookup in the dimension.
|
332
|
+
:return: A list of Dimension items that match the provided codes or names.
|
333
|
+
"""
|
334
|
+
if not codes and not names:
|
335
|
+
raise ValueError("At least one of 'codes' or 'names' must be provided.")
|
336
|
+
if not (
|
337
|
+
dimension_id == 101999999999
|
338
|
+
or 101000000000 <= dimension_id < 102000000000
|
339
|
+
or dimension_id == 20000000003
|
340
|
+
or dimension_id == 20000000020
|
341
|
+
):
|
342
|
+
raise InvalidIdentifierException(
|
343
|
+
"Invalid dimension_id. Must be a List (101xxxxxxxxx), Time (20000000003), "
|
344
|
+
"Version (20000000020), or Users (101999999999)."
|
345
|
+
)
|
346
|
+
res = self._post(
|
347
|
+
f"{self._url}/dimensions/{dimension_id}/items", json={"codes": codes, "names": names}
|
348
|
+
)
|
349
|
+
return [DimensionWithCode.model_validate(e) for e in res.get("items", [])]
|
350
|
+
|
351
|
+
def get_view_dimension_items(self, view_id: int, dimension_id: int) -> list[Dimension]:
|
352
|
+
"""
|
353
|
+
Get the members of a dimension that are part of the given View. This call returns data as
|
354
|
+
filtered by the page builder when they configure the view. This call respects hidden items,
|
355
|
+
filtering selections, and Selective Access. If the view contains hidden or filtered items,
|
356
|
+
these do not display in the response. This will fail if the dimensions holds more than
|
357
|
+
1_000_000 items. The response returns Items within a flat list (no hierarchy) and order
|
358
|
+
is not guaranteed.
|
359
|
+
:param view_id: The ID of the View.
|
360
|
+
:param dimension_id: The ID of the Dimension to get items for.
|
361
|
+
:return: A list of Dimensions used in the View.
|
362
|
+
"""
|
363
|
+
res = self._get(f"{self._url}/views/{view_id}/dimensions/{dimension_id}/items")
|
364
|
+
return [Dimension.model_validate(e) for e in res.get("items", [])]
|
365
|
+
|
366
|
+
def get_line_item_dimensions(self, line_item_id: int) -> list[Dimension]:
|
367
|
+
"""
|
368
|
+
Get the dimensions of a Line Item. This will return all dimensions that are used in the
|
369
|
+
Line Item.
|
370
|
+
:param line_item_id: The ID of the Line Item.
|
371
|
+
:return: A list of Dimensions used in the Line Item.
|
372
|
+
"""
|
373
|
+
res = self._get(f"{self._url}/lineItems/{line_item_id}/dimensions")
|
374
|
+
return [Dimension.model_validate(e) for e in res.get("dimensions", [])]
|
anaplan_sdk/models/__init__.py
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
from ._alm import
|
1
|
+
from ._alm import (
|
2
|
+
ModelRevision,
|
3
|
+
ReportTask,
|
4
|
+
ReportTaskResult,
|
5
|
+
Revision,
|
6
|
+
SummaryReport,
|
7
|
+
SyncTask,
|
8
|
+
SyncTaskResult,
|
9
|
+
)
|
2
10
|
from ._base import AnaplanModel
|
3
11
|
from ._bulk import (
|
4
12
|
Action,
|
@@ -10,6 +18,7 @@ from ._bulk import (
|
|
10
18
|
List,
|
11
19
|
ListMetadata,
|
12
20
|
Model,
|
21
|
+
ModelDeletionResult,
|
13
22
|
Process,
|
14
23
|
TaskResult,
|
15
24
|
TaskResultDetail,
|
@@ -17,7 +26,26 @@ from ._bulk import (
|
|
17
26
|
TaskSummary,
|
18
27
|
Workspace,
|
19
28
|
)
|
20
|
-
from ._transactional import
|
29
|
+
from ._transactional import (
|
30
|
+
CurrentPeriod,
|
31
|
+
Dimension,
|
32
|
+
DimensionWithCode,
|
33
|
+
Failure,
|
34
|
+
FiscalYear,
|
35
|
+
InsertionResult,
|
36
|
+
LineItem,
|
37
|
+
ListItem,
|
38
|
+
ModelCalendar,
|
39
|
+
ModelStatus,
|
40
|
+
Module,
|
41
|
+
MonthsQuartersYearsCalendar,
|
42
|
+
User,
|
43
|
+
View,
|
44
|
+
ViewInfo,
|
45
|
+
WeeksGeneralCalendar,
|
46
|
+
WeeksGroupingCalendar,
|
47
|
+
WeeksPeriodsCalendar,
|
48
|
+
)
|
21
49
|
|
22
50
|
__all__ = [
|
23
51
|
"AnaplanModel",
|
@@ -41,9 +69,26 @@ __all__ = [
|
|
41
69
|
"TaskResult",
|
42
70
|
"TaskResultDetail",
|
43
71
|
"TaskStatus",
|
72
|
+
"TaskSummary",
|
73
|
+
"SyncTaskResult",
|
74
|
+
"ReportTask",
|
75
|
+
"ReportTaskResult",
|
76
|
+
"SummaryReport",
|
44
77
|
"SyncTask",
|
45
78
|
"User",
|
46
79
|
"Failure",
|
47
80
|
"InsertionResult",
|
48
81
|
"Revision",
|
82
|
+
"CurrentPeriod",
|
83
|
+
"FiscalYear",
|
84
|
+
"MonthsQuartersYearsCalendar",
|
85
|
+
"WeeksGeneralCalendar",
|
86
|
+
"WeeksGroupingCalendar",
|
87
|
+
"WeeksPeriodsCalendar",
|
88
|
+
"Dimension",
|
89
|
+
"View",
|
90
|
+
"ViewInfo",
|
91
|
+
"ModelCalendar",
|
92
|
+
"ModelDeletionResult",
|
93
|
+
"DimensionWithCode",
|
49
94
|
]
|
anaplan_sdk/models/_alm.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from pydantic import Field
|
2
2
|
|
3
3
|
from ._base import AnaplanModel
|
4
|
+
from ._bulk import TaskSummary
|
4
5
|
|
5
6
|
|
6
7
|
class Revision(AnaplanModel):
|
@@ -34,8 +35,8 @@ class ModelRevision(AnaplanModel):
|
|
34
35
|
"workspace."
|
35
36
|
),
|
36
37
|
)
|
37
|
-
workspace_id: str = Field(
|
38
|
-
description="The unique identifier of the workspace this revision belongs to."
|
38
|
+
workspace_id: str | None = Field(
|
39
|
+
None, description="The unique identifier of the workspace this revision belongs to."
|
39
40
|
)
|
40
41
|
applied_by: str = Field(
|
41
42
|
description="The unique identifier of the user who applied this revision."
|
@@ -49,7 +50,64 @@ class ModelRevision(AnaplanModel):
|
|
49
50
|
)
|
50
51
|
|
51
52
|
|
52
|
-
class
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
class SyncTaskResult(AnaplanModel):
|
54
|
+
source_revision_id: str = Field(description="The ID of the source revision.")
|
55
|
+
target_revision_id: str = Field(description="The ID of the target revision.")
|
56
|
+
successful: bool = Field(description="Whether the sync task was successful or not.")
|
57
|
+
|
58
|
+
|
59
|
+
class SyncTask(TaskSummary):
|
60
|
+
current_step: str = Field(description="The current step of the sync task.")
|
61
|
+
result: SyncTaskResult | None = Field(None, description="The result of the sync task.")
|
62
|
+
|
63
|
+
|
64
|
+
class ReportTaskResult(SyncTaskResult):
|
65
|
+
report_file_url: str = Field(
|
66
|
+
description="The URL of the report file generated by the sync task."
|
67
|
+
)
|
68
|
+
|
69
|
+
|
70
|
+
class ReportTaskError(AnaplanModel):
|
71
|
+
title: str = Field(description="The title of the error.")
|
72
|
+
message: str = Field(validation_alias="messageText", description="The message of the error.")
|
73
|
+
|
74
|
+
|
75
|
+
class ReportTaskFailureResult(AnaplanModel):
|
76
|
+
successful: bool = Field(description="Whether the sync task was successful or not.")
|
77
|
+
error: ReportTaskError = Field(description="The error that occurred during the sync task.")
|
78
|
+
|
79
|
+
|
80
|
+
class ReportTask(SyncTask):
|
81
|
+
result: ReportTaskResult | ReportTaskFailureResult | None = Field(
|
82
|
+
None, description="The result of the comparison report task, including the report file URL."
|
83
|
+
)
|
84
|
+
|
85
|
+
|
86
|
+
class SummaryTotals(AnaplanModel):
|
87
|
+
modified: int = Field(0, description="The number of modified items.")
|
88
|
+
deleted: int = Field(0, description="The number of deleted items.")
|
89
|
+
created: int = Field(0, description="The number of created items.")
|
90
|
+
|
91
|
+
|
92
|
+
class SummaryDifferences(AnaplanModel):
|
93
|
+
line_items: SummaryTotals = Field(
|
94
|
+
SummaryTotals(modified=0, deleted=0, created=0), description="Changes in line items."
|
95
|
+
)
|
96
|
+
roles_contents: SummaryTotals = Field(
|
97
|
+
SummaryTotals(modified=0, deleted=0, created=0), description="Changes in roles contents."
|
98
|
+
)
|
99
|
+
lists: SummaryTotals = Field(
|
100
|
+
SummaryTotals(modified=0, deleted=0, created=0), description="Changes in lists."
|
101
|
+
)
|
102
|
+
modules: SummaryTotals = Field(
|
103
|
+
SummaryTotals(modified=0, deleted=0, created=0), description="Changes in modules."
|
104
|
+
)
|
105
|
+
|
106
|
+
|
107
|
+
class SummaryReport(AnaplanModel):
|
108
|
+
target_revision_id: str = Field(description="The ID of the target revision.")
|
109
|
+
source_revision_id: str = Field(description="The ID of the source revision.")
|
110
|
+
totals: SummaryTotals = Field(description="The total counts of changes.")
|
111
|
+
differences: SummaryDifferences = Field(
|
112
|
+
description="The detailed breakdown of changes by category."
|
113
|
+
)
|
anaplan_sdk/models/_bulk.py
CHANGED
@@ -174,3 +174,17 @@ class TaskStatus(AnaplanModel):
|
|
174
174
|
progress: float = Field(description="The progress of this task as a float between 0 and 1.")
|
175
175
|
current_step: str | None = Field(None, description="The current step of this task.")
|
176
176
|
result: TaskResult | None = Field(None)
|
177
|
+
|
178
|
+
|
179
|
+
class DeletionFailure(AnaplanModel):
|
180
|
+
model_id: str = Field(description="The unique identifier of the model that failed to delete.")
|
181
|
+
message: str = Field(description="The error message explaining why the deletion failed.")
|
182
|
+
|
183
|
+
|
184
|
+
class ModelDeletionResult(AnaplanModel):
|
185
|
+
models_deleted: int = Field(description="The number of models that were successfully deleted.")
|
186
|
+
failures: list[DeletionFailure] = Field(
|
187
|
+
[],
|
188
|
+
validation_alias="bulkDeleteModelsFailures",
|
189
|
+
description="List of models that failed to delete with their error messages.",
|
190
|
+
)
|