anaplan-sdk 0.5.0a5__py3-none-any.whl → 0.5.0a6__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.
@@ -1,7 +1,8 @@
1
1
  import logging
2
2
  from typing import Literal, overload
3
3
 
4
- from anaplan_sdk._services import _AsyncHttpService, sort_params
4
+ from anaplan_sdk._services import _AsyncHttpService
5
+ from anaplan_sdk._utils import sort_params
5
6
  from anaplan_sdk.exceptions import AnaplanActionError
6
7
  from anaplan_sdk.models import (
7
8
  ModelRevision,
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Literal
2
2
 
3
- from anaplan_sdk._services import _AsyncHttpService, sort_params
3
+ from anaplan_sdk._services import _AsyncHttpService
4
+ from anaplan_sdk._utils import sort_params
4
5
  from anaplan_sdk.models import User
5
6
 
6
7
  Event = Literal["all", "byok", "user_activity"]
@@ -7,7 +7,8 @@ import httpx
7
7
  from typing_extensions import Self
8
8
 
9
9
  from anaplan_sdk._auth import _create_auth
10
- from anaplan_sdk._services import _AsyncHttpService, action_url, models_url, sort_params
10
+ from anaplan_sdk._services import _AsyncHttpService
11
+ from anaplan_sdk._utils import action_url, models_url, sort_params
11
12
  from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
12
13
  from anaplan_sdk.models import (
13
14
  Action,
@@ -1,8 +1,8 @@
1
1
  import logging
2
2
  from typing import Any, Literal
3
3
 
4
- from anaplan_sdk._services import (
5
- _AsyncHttpService,
4
+ from anaplan_sdk._services import _AsyncHttpService
5
+ from anaplan_sdk._utils import (
6
6
  connection_body_payload,
7
7
  construct_payload,
8
8
  integration_payload,
@@ -1,7 +1,8 @@
1
1
  import logging
2
2
  from typing import Any
3
3
 
4
- from anaplan_sdk._services import _AsyncHttpService, construct_payload
4
+ from anaplan_sdk._services import _AsyncHttpService
5
+ from anaplan_sdk._utils import construct_payload
5
6
  from anaplan_sdk.models.flows import Flow, FlowInput, FlowSummary
6
7
 
7
8
  logger = logging.getLogger("anaplan_sdk")
@@ -3,8 +3,8 @@ from asyncio import gather
3
3
  from itertools import chain
4
4
  from typing import Any, Literal, overload
5
5
 
6
- from anaplan_sdk._services import (
7
- _AsyncHttpService,
6
+ from anaplan_sdk._services import _AsyncHttpService
7
+ from anaplan_sdk._utils import (
8
8
  parse_calendar_response,
9
9
  parse_insertion_response,
10
10
  sort_params,
@@ -1,7 +1,8 @@
1
1
  import logging
2
2
  from typing import Literal, overload
3
3
 
4
- from anaplan_sdk._services import _HttpService, sort_params
4
+ from anaplan_sdk._services import _HttpService
5
+ from anaplan_sdk._utils import sort_params
5
6
  from anaplan_sdk.exceptions import AnaplanActionError
6
7
  from anaplan_sdk.models import (
7
8
  ModelRevision,
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Literal
2
2
 
3
- from anaplan_sdk._services import _HttpService, sort_params
3
+ from anaplan_sdk._services import _HttpService
4
+ from anaplan_sdk._utils import sort_params
4
5
  from anaplan_sdk.models import User
5
6
 
6
7
  Event = Literal["all", "byok", "user_activity"]
@@ -8,7 +8,8 @@ import httpx
8
8
  from typing_extensions import Self
9
9
 
10
10
  from anaplan_sdk._auth import _create_auth
11
- from anaplan_sdk._services import _HttpService, action_url, models_url, sort_params
11
+ from anaplan_sdk._services import _HttpService
12
+ from anaplan_sdk._utils import action_url, models_url, sort_params
12
13
  from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierException
13
14
  from anaplan_sdk.models import (
14
15
  Action,
@@ -1,8 +1,8 @@
1
1
  import logging
2
2
  from typing import Any, Literal
3
3
 
4
- from anaplan_sdk._services import (
5
- _HttpService,
4
+ from anaplan_sdk._services import _HttpService
5
+ from anaplan_sdk._utils import (
6
6
  connection_body_payload,
7
7
  construct_payload,
8
8
  integration_payload,
@@ -1,7 +1,8 @@
1
1
  import logging
2
2
  from typing import Any
3
3
 
4
- from anaplan_sdk._services import _HttpService, construct_payload
4
+ from anaplan_sdk._services import _HttpService
5
+ from anaplan_sdk._utils import construct_payload
5
6
  from anaplan_sdk.models.flows import Flow, FlowInput, FlowSummary
6
7
 
7
8
  logger = logging.getLogger("anaplan_sdk")
@@ -3,8 +3,8 @@ from concurrent.futures import ThreadPoolExecutor
3
3
  from itertools import chain
4
4
  from typing import Any, Literal, overload
5
5
 
6
- from anaplan_sdk._services import (
7
- _HttpService,
6
+ from anaplan_sdk._services import _HttpService
7
+ from anaplan_sdk._utils import (
8
8
  parse_calendar_response,
9
9
  parse_insertion_response,
10
10
  sort_params,
anaplan_sdk/_services.py CHANGED
@@ -6,32 +6,13 @@ from concurrent.futures import ThreadPoolExecutor
6
6
  from gzip import compress
7
7
  from itertools import chain
8
8
  from math import ceil
9
- from typing import Any, Awaitable, Callable, Coroutine, Iterator, Literal, Type, TypeVar
9
+ from typing import Any, Awaitable, Callable, Coroutine, Iterator, TypeVar
10
10
 
11
11
  import httpx
12
12
  from httpx import HTTPError, Response
13
- from pydantic.alias_generators import to_camel
14
13
 
15
14
  from .exceptions import AnaplanException, AnaplanTimeoutException, InvalidIdentifierException
16
- from .models import (
17
- AnaplanModel,
18
- InsertionResult,
19
- ModelCalendar,
20
- MonthsQuartersYearsCalendar,
21
- TaskSummary,
22
- WeeksGeneralCalendar,
23
- WeeksGroupingCalendar,
24
- WeeksPeriodsCalendar,
25
- )
26
- from .models.cloud_works import (
27
- AmazonS3ConnectionInput,
28
- AzureBlobConnectionInput,
29
- ConnectionBody,
30
- GoogleBigQueryConnectionInput,
31
- IntegrationInput,
32
- IntegrationProcessInput,
33
- ScheduleInput,
34
- )
15
+ from .models import TaskSummary
35
16
 
36
17
  SORT_WARNING = (
37
18
  "If you are sorting by a field that is potentially ambiguous (e.g., name), the order of "
@@ -47,7 +28,6 @@ logger = logging.getLogger("anaplan_sdk")
47
28
  _json_header = {"Content-Type": "application/json"}
48
29
  _gzip_header = {"Content-Type": "application/x-gzip"}
49
30
 
50
- T = TypeVar("T", bound=AnaplanModel)
51
31
  Task = TypeVar("Task", bound=TaskSummary)
52
32
 
53
33
 
@@ -134,15 +114,7 @@ class _HttpService:
134
114
  logger.debug(f"Fetching first page with limit={self._page_size} from {url}.")
135
115
  kwargs["params"] = (kwargs.get("params") or {}) | {"limit": self._page_size}
136
116
  res = self.get(url, **kwargs)
137
- total_items, first_page = res["meta"]["paging"]["totalSize"], res.get(result_key, [])
138
- actual_page_size = res["meta"]["paging"]["currentPageSize"]
139
- if actual_page_size < self._page_size and not actual_page_size == total_items:
140
- logger.warning(
141
- f"Page size {self._page_size} was silently truncated to {actual_page_size}."
142
- f"Using the server-side enforced page size {actual_page_size} for further requests."
143
- )
144
- logger.debug(f"Found {total_items} total items, retrieved {len(first_page)} in first page.")
145
- return first_page, total_items, actual_page_size
117
+ return _extract_first_page(res, result_key, self._page_size)
146
118
 
147
119
  def __run_with_retry(self, func: Callable[..., Response], *args, **kwargs) -> Response:
148
120
  for i in range(max(self._retry_count, 1)):
@@ -159,7 +131,7 @@ class _HttpService:
159
131
  return response
160
132
  except HTTPError as error:
161
133
  if i >= self._retry_count - 1:
162
- raise_error(error)
134
+ _raise_error(error)
163
135
  url = args[0] or kwargs.get("url")
164
136
  logger.info(f"Retrying for: {url}")
165
137
 
@@ -252,15 +224,7 @@ class _AsyncHttpService:
252
224
  logger.debug(f"Fetching first page with limit={self._page_size} from {url}.")
253
225
  kwargs["params"] = (kwargs.get("params") or {}) | {"limit": self._page_size}
254
226
  res = await self.get(url, **kwargs)
255
- total_items, first_page = res["meta"]["paging"]["totalSize"], res.get(result_key, [])
256
- actual_page_size = res["meta"]["paging"]["currentPageSize"]
257
- if actual_page_size < self._page_size and not actual_page_size == total_items:
258
- logger.warning(
259
- f"Page size {self._page_size} was silently truncated to {actual_page_size}."
260
- f"Using the server-side enforced page size {actual_page_size} for further requests."
261
- )
262
- logger.debug(f"Found {total_items} total items, retrieved {len(first_page)} in first page.")
263
- return first_page, total_items, actual_page_size
227
+ return _extract_first_page(res, result_key, self._page_size)
264
228
 
265
229
  async def _run_with_retry(
266
230
  self, func: Callable[..., Coroutine[Any, Any, Response]], *args, **kwargs
@@ -279,123 +243,28 @@ class _AsyncHttpService:
279
243
  return response
280
244
  except HTTPError as error:
281
245
  if i >= self._retry_count - 1:
282
- raise_error(error)
246
+ _raise_error(error)
283
247
  url = args[0] or kwargs.get("url")
284
248
  logger.info(f"Retrying for: {url}")
285
249
 
286
250
  raise AnaplanException("Exhausted all retries without a successful response or Error.")
287
251
 
288
252
 
289
- def models_url(only_in_workspace: bool | str, workspace_id: str | None) -> str:
290
- if isinstance(only_in_workspace, bool) and only_in_workspace:
291
- if not workspace_id:
292
- raise ValueError(
293
- "Cannot list models in the current workspace, since no workspace Id was "
294
- "provided when instantiating the client. Either provide a workspace Id when "
295
- "instantiating the client, or pass a specific workspace Id to this method."
296
- )
297
- return f"https://api.anaplan.com/2/0/workspaces/{workspace_id}/models"
298
- if isinstance(only_in_workspace, str):
299
- return f"https://api.anaplan.com/2/0/workspaces/{only_in_workspace}/models"
300
- return "https://api.anaplan.com/2/0/models"
301
-
302
-
303
- def sort_params(sort_by: str | None, descending: bool) -> dict[str, str | bool]:
304
- """
305
- Construct search parameters for sorting. This also converts snake_case to camelCase.
306
- :param sort_by: The field to sort by, optionally in snake_case.
307
- :param descending: Whether to sort in descending order.
308
- :return: A dictionary of search parameters in Anaplan's expected format.
309
- """
310
- if not sort_by:
311
- return {}
312
- return {"sort": f"{'-' if descending else '+'}{to_camel(sort_by)}"}
313
-
314
-
315
- def construct_payload(model: Type[T], body: T | dict[str, Any]) -> dict[str, Any]:
316
- """
317
- Construct a payload for the given model and body.
318
- :param model: The model class to use for validation.
319
- :param body: The body to validate and optionally convert to a dictionary.
320
- :return: A dictionary representation of the validated body.
321
- """
322
- if isinstance(body, dict):
323
- body = model.model_validate(body)
324
- return body.model_dump(exclude_none=True, by_alias=True)
325
-
326
-
327
- def connection_body_payload(body: ConnectionBody | dict[str, Any]) -> dict[str, Any]:
328
- """
329
- Construct a payload for the given integration body.
330
- :param body: The body to validate and optionally convert to a dictionary.
331
- :return: A dictionary representation of the validated body.
332
- """
333
- if isinstance(body, dict):
334
- if "sasToken" in body:
335
- body = AzureBlobConnectionInput.model_validate(body)
336
- elif "secretAccessKey" in body:
337
- body = AmazonS3ConnectionInput.model_validate(body)
338
- else:
339
- body = GoogleBigQueryConnectionInput.model_validate(body)
340
- return body.model_dump(exclude_none=True, by_alias=True)
341
-
342
-
343
- def integration_payload(
344
- body: IntegrationInput | IntegrationProcessInput | dict[str, Any],
345
- ) -> dict[str, Any]:
346
- """
347
- Construct a payload for the given integration body.
348
- :param body: The body to validate and optionally convert to a dictionary.
349
- :return: A dictionary representation of the validated body.
350
- """
351
- if isinstance(body, dict):
352
- body = (
353
- IntegrationInput.model_validate(body)
354
- if "jobs" in body
355
- else IntegrationProcessInput.model_validate(body)
253
+ def _extract_first_page(
254
+ res: dict[str, Any], result_key: str, page_size: int
255
+ ) -> tuple[list[dict[str, Any]], int, int]:
256
+ total_items, first_page = res["meta"]["paging"]["totalSize"], res.get(result_key, [])
257
+ actual_page_size = res["meta"]["paging"]["currentPageSize"]
258
+ if actual_page_size < page_size and not actual_page_size == total_items:
259
+ logger.warning(
260
+ f"Page size {page_size} was silently truncated to {actual_page_size}."
261
+ f"Using the server-side enforced page size {actual_page_size} for further requests."
356
262
  )
357
- return body.model_dump(exclude_none=True, by_alias=True)
358
-
359
-
360
- def schedule_payload(
361
- integration_id: str, schedule: ScheduleInput | dict[str, Any]
362
- ) -> dict[str, Any]:
363
- """
364
- Construct a payload for the given integration ID and schedule.
365
- :param integration_id: The ID of the integration.
366
- :param schedule: The schedule to validate and optionally convert to a dictionary.
367
- :return: A dictionary representation of the validated schedule.
368
- """
369
- if isinstance(schedule, dict):
370
- schedule = ScheduleInput.model_validate(schedule)
371
- return {
372
- "integrationId": integration_id,
373
- "schedule": schedule.model_dump(exclude_none=True, by_alias=True),
374
- }
375
-
376
-
377
- def action_url(action_id: int) -> Literal["imports", "exports", "actions", "processes"]:
378
- """
379
- Determine the type of action based on its identifier.
380
- :param action_id: The identifier of the action.
381
- :return: The type of action.
382
- """
383
- if 12000000000 <= action_id < 113000000000:
384
- return "imports"
385
- if 116000000000 <= action_id < 117000000000:
386
- return "exports"
387
- if 117000000000 <= action_id < 118000000000:
388
- return "actions"
389
- if 118000000000 <= action_id < 119000000000:
390
- return "processes"
391
- raise InvalidIdentifierException(f"Action '{action_id}' is not a valid identifier.")
392
-
393
-
394
- def raise_error(error: HTTPError) -> None:
395
- """
396
- Raise an appropriate exception based on the error.
397
- :param error: The error to raise an exception for.
398
- """
263
+ logger.debug(f"Found {total_items} total items, retrieved {len(first_page)} in first page.")
264
+ return first_page, total_items, actual_page_size
265
+
266
+
267
+ def _raise_error(error: HTTPError) -> None:
399
268
  if isinstance(error, httpx.TimeoutException):
400
269
  raise AnaplanTimeoutException from error
401
270
  if isinstance(error, httpx.HTTPStatusError):
@@ -406,59 +275,3 @@ def raise_error(error: HTTPError) -> None:
406
275
 
407
276
  logger.error(f"Error: {error}")
408
277
  raise AnaplanException from error
409
-
410
-
411
- def parse_calendar_response(data: dict) -> ModelCalendar:
412
- """
413
- Parse calendar response and return appropriate calendar model.
414
- :param data: The calendar data from the API response.
415
- :return: The calendar settings of the model based on calendar type.
416
- """
417
- calendar_data = data["modelCalendar"]
418
- cal_type = calendar_data["calendarType"]
419
- if cal_type == "Calendar Months/Quarters/Years":
420
- return MonthsQuartersYearsCalendar.model_validate(calendar_data)
421
- if cal_type == "Weeks: 4-4-5, 4-5-4 or 5-4-4":
422
- return WeeksGroupingCalendar.model_validate(calendar_data)
423
- if cal_type == "Weeks: General":
424
- return WeeksGeneralCalendar.model_validate(calendar_data)
425
- if cal_type == "Weeks: 13 4-week Periods":
426
- return WeeksPeriodsCalendar.model_validate(calendar_data)
427
- raise AnaplanException(
428
- "Unknown calendar type encountered. Please report this issue: "
429
- "https://github.com/VinzenzKlass/anaplan-sdk/issues/new"
430
- )
431
-
432
-
433
- def parse_insertion_response(data: list[dict]) -> InsertionResult:
434
- failures, added, ignored, total = [], 0, 0, 0
435
- for res in data:
436
- failures.append(res.get("failures", []))
437
- added += res.get("added", 0)
438
- total += res.get("total", 0)
439
- ignored += res.get("ignored", 0)
440
- return InsertionResult(
441
- added=added, ignored=ignored, total=total, failures=list(chain.from_iterable(failures))
442
- )
443
-
444
-
445
- def validate_dimension_id(dimension_id: int) -> int:
446
- if not (
447
- dimension_id == 101999999999
448
- or 101_000_000_000 <= dimension_id < 102_000_000_000
449
- or 109_000_000_000 <= dimension_id < 110_000_000_000
450
- or 114_000_000_000 <= dimension_id < 115_000_000_000
451
- ):
452
- raise InvalidIdentifierException(
453
- "Invalid dimension_id. Must be a List (101xxxxxxxxx), List Subset (109xxxxxxxxx), "
454
- "Line Item Subset (114xxxxxxxxx), or Users (101999999999)."
455
- )
456
- msg = (
457
- "Using `get_dimension_items` for {} is discouraged. "
458
- "Prefer `{}` for better performance and more details on the members."
459
- )
460
- if dimension_id == 101999999999:
461
- logger.warning(msg.format("Users", "get_users"))
462
- if 101000000000 <= dimension_id < 102000000000:
463
- logger.warning(msg.format("Lists", "get_list_items"))
464
- return dimension_id
anaplan_sdk/_utils.py ADDED
@@ -0,0 +1,188 @@
1
+ from itertools import chain
2
+ from typing import Any, Literal, Type, TypeVar
3
+
4
+ from pydantic.alias_generators import to_camel
5
+
6
+ from anaplan_sdk._services import logger
7
+ from anaplan_sdk.exceptions import AnaplanException, InvalidIdentifierException
8
+ from anaplan_sdk.models import (
9
+ AnaplanModel,
10
+ InsertionResult,
11
+ ModelCalendar,
12
+ MonthsQuartersYearsCalendar,
13
+ WeeksGeneralCalendar,
14
+ WeeksGroupingCalendar,
15
+ WeeksPeriodsCalendar,
16
+ )
17
+ from anaplan_sdk.models.cloud_works import (
18
+ AmazonS3ConnectionInput,
19
+ AzureBlobConnectionInput,
20
+ ConnectionBody,
21
+ GoogleBigQueryConnectionInput,
22
+ IntegrationInput,
23
+ IntegrationProcessInput,
24
+ ScheduleInput,
25
+ )
26
+
27
+ T = TypeVar("T", bound=AnaplanModel)
28
+
29
+
30
+ def models_url(only_in_workspace: bool | str, workspace_id: str | None) -> str:
31
+ if isinstance(only_in_workspace, bool) and only_in_workspace:
32
+ if not workspace_id:
33
+ raise ValueError(
34
+ "Cannot list models in the current workspace, since no workspace Id was "
35
+ "provided when instantiating the client. Either provide a workspace Id when "
36
+ "instantiating the client, or pass a specific workspace Id to this method."
37
+ )
38
+ return f"https://api.anaplan.com/2/0/workspaces/{workspace_id}/models"
39
+ if isinstance(only_in_workspace, str):
40
+ return f"https://api.anaplan.com/2/0/workspaces/{only_in_workspace}/models"
41
+ return "https://api.anaplan.com/2/0/models"
42
+
43
+
44
+ def sort_params(sort_by: str | None, descending: bool) -> dict[str, str | bool]:
45
+ """
46
+ Construct search parameters for sorting. This also converts snake_case to camelCase.
47
+ :param sort_by: The field to sort by, optionally in snake_case.
48
+ :param descending: Whether to sort in descending order.
49
+ :return: A dictionary of search parameters in Anaplan's expected format.
50
+ """
51
+ if not sort_by:
52
+ return {}
53
+ return {"sort": f"{'-' if descending else '+'}{to_camel(sort_by)}"}
54
+
55
+
56
+ def construct_payload(model: Type[T], body: T | dict[str, Any]) -> dict[str, Any]:
57
+ """
58
+ Construct a payload for the given model and body.
59
+ :param model: The model class to use for validation.
60
+ :param body: The body to validate and optionally convert to a dictionary.
61
+ :return: A dictionary representation of the validated body.
62
+ """
63
+ if isinstance(body, dict):
64
+ body = model.model_validate(body)
65
+ return body.model_dump(exclude_none=True, by_alias=True)
66
+
67
+
68
+ def connection_body_payload(body: ConnectionBody | dict[str, Any]) -> dict[str, Any]:
69
+ """
70
+ Construct a payload for the given integration body.
71
+ :param body: The body to validate and optionally convert to a dictionary.
72
+ :return: A dictionary representation of the validated body.
73
+ """
74
+ if isinstance(body, dict):
75
+ if "sasToken" in body:
76
+ body = AzureBlobConnectionInput.model_validate(body)
77
+ elif "secretAccessKey" in body:
78
+ body = AmazonS3ConnectionInput.model_validate(body)
79
+ else:
80
+ body = GoogleBigQueryConnectionInput.model_validate(body)
81
+ return body.model_dump(exclude_none=True, by_alias=True)
82
+
83
+
84
+ def integration_payload(
85
+ body: IntegrationInput | IntegrationProcessInput | dict[str, Any],
86
+ ) -> dict[str, Any]:
87
+ """
88
+ Construct a payload for the given integration body.
89
+ :param body: The body to validate and optionally convert to a dictionary.
90
+ :return: A dictionary representation of the validated body.
91
+ """
92
+ if isinstance(body, dict):
93
+ body = (
94
+ IntegrationInput.model_validate(body)
95
+ if "jobs" in body
96
+ else IntegrationProcessInput.model_validate(body)
97
+ )
98
+ return body.model_dump(exclude_none=True, by_alias=True)
99
+
100
+
101
+ def schedule_payload(
102
+ integration_id: str, schedule: ScheduleInput | dict[str, Any]
103
+ ) -> dict[str, Any]:
104
+ """
105
+ Construct a payload for the given integration ID and schedule.
106
+ :param integration_id: The ID of the integration.
107
+ :param schedule: The schedule to validate and optionally convert to a dictionary.
108
+ :return: A dictionary representation of the validated schedule.
109
+ """
110
+ if isinstance(schedule, dict):
111
+ schedule = ScheduleInput.model_validate(schedule)
112
+ return {
113
+ "integrationId": integration_id,
114
+ "schedule": schedule.model_dump(exclude_none=True, by_alias=True),
115
+ }
116
+
117
+
118
+ def action_url(action_id: int) -> Literal["imports", "exports", "actions", "processes"]:
119
+ """
120
+ Determine the type of action based on its identifier.
121
+ :param action_id: The identifier of the action.
122
+ :return: The type of action.
123
+ """
124
+ if 12000000000 <= action_id < 113000000000:
125
+ return "imports"
126
+ if 116000000000 <= action_id < 117000000000:
127
+ return "exports"
128
+ if 117000000000 <= action_id < 118000000000:
129
+ return "actions"
130
+ if 118000000000 <= action_id < 119000000000:
131
+ return "processes"
132
+ raise InvalidIdentifierException(f"Action '{action_id}' is not a valid identifier.")
133
+
134
+
135
+ def parse_calendar_response(data: dict) -> ModelCalendar:
136
+ """
137
+ Parse calendar response and return appropriate calendar model.
138
+ :param data: The calendar data from the API response.
139
+ :return: The calendar settings of the model based on calendar type.
140
+ """
141
+ calendar_data = data["modelCalendar"]
142
+ cal_type = calendar_data["calendarType"]
143
+ if cal_type == "Calendar Months/Quarters/Years":
144
+ return MonthsQuartersYearsCalendar.model_validate(calendar_data)
145
+ if cal_type == "Weeks: 4-4-5, 4-5-4 or 5-4-4":
146
+ return WeeksGroupingCalendar.model_validate(calendar_data)
147
+ if cal_type == "Weeks: General":
148
+ return WeeksGeneralCalendar.model_validate(calendar_data)
149
+ if cal_type == "Weeks: 13 4-week Periods":
150
+ return WeeksPeriodsCalendar.model_validate(calendar_data)
151
+ raise AnaplanException(
152
+ "Unknown calendar type encountered. Please report this issue: "
153
+ "https://github.com/VinzenzKlass/anaplan-sdk/issues/new"
154
+ )
155
+
156
+
157
+ def parse_insertion_response(data: list[dict]) -> InsertionResult:
158
+ failures, added, ignored, total = [], 0, 0, 0
159
+ for res in data:
160
+ failures.append(res.get("failures", []))
161
+ added += res.get("added", 0)
162
+ total += res.get("total", 0)
163
+ ignored += res.get("ignored", 0)
164
+ return InsertionResult(
165
+ added=added, ignored=ignored, total=total, failures=list(chain.from_iterable(failures))
166
+ )
167
+
168
+
169
+ def validate_dimension_id(dimension_id: int) -> int:
170
+ if not (
171
+ dimension_id == 101999999999
172
+ or 101_000_000_000 <= dimension_id < 102_000_000_000
173
+ or 109_000_000_000 <= dimension_id < 110_000_000_000
174
+ or 114_000_000_000 <= dimension_id < 115_000_000_000
175
+ ):
176
+ raise InvalidIdentifierException(
177
+ "Invalid dimension_id. Must be a List (101xxxxxxxxx), List Subset (109xxxxxxxxx), "
178
+ "Line Item Subset (114xxxxxxxxx), or Users (101999999999)."
179
+ )
180
+ msg = (
181
+ "Using `get_dimension_items` for {} is discouraged. "
182
+ "Prefer `{}` for better performance and more details on the members."
183
+ )
184
+ if dimension_id == 101999999999:
185
+ logger.warning(msg.format("Users", "get_users"))
186
+ if 101000000000 <= dimension_id < 102000000000:
187
+ logger.warning(msg.format("Lists", "get_list_items"))
188
+ return dimension_id
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anaplan-sdk
3
- Version: 0.5.0a5
3
+ Version: 0.5.0a6
4
4
  Summary: Streamlined Python Interface for the Anaplan API.
5
5
  Project-URL: Homepage, https://vinzenzklass.github.io/anaplan-sdk/
6
6
  Project-URL: Repository, https://github.com/VinzenzKlass/anaplan-sdk
@@ -0,0 +1,31 @@
1
+ anaplan_sdk/__init__.py,sha256=WScEKtXlnRLjCb-j3qW9W4kEACTyPsTLFs-L54et2TQ,351
2
+ anaplan_sdk/_auth.py,sha256=l5z2WCcfQ05OkuQ1dcmikp6dB87Rw1qy2zu8bbaAQTs,16620
3
+ anaplan_sdk/_oauth.py,sha256=AynlJDrGIinQT0jwxI2RSvtU4D7Wasyw3H1uicdlLVI,12672
4
+ anaplan_sdk/_services.py,sha256=n14DvTC2I5ub7yzjWBaAAX9YMF7LwI_BU7zuofAj0l4,13026
5
+ anaplan_sdk/_utils.py,sha256=jsrhdfLpriMoANukVvXpjpEJ5hWDNx7ZJKAguLvKgJA,7517
6
+ anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
7
+ anaplan_sdk/_async_clients/__init__.py,sha256=pZXgMMg4S9Aj_pxQCaSiPuNG-sePVGBtNJ0133VjqW4,364
8
+ anaplan_sdk/_async_clients/_alm.py,sha256=_PTD9Eght879HAadjcsfdvS0KCN93jgwpPOF8r3_E14,13178
9
+ anaplan_sdk/_async_clients/_audit.py,sha256=MkCTt7s6039GfDoyyINmDA1vAkZn2xPL4hFK8D4CYsE,2927
10
+ anaplan_sdk/_async_clients/_bulk.py,sha256=TsUUIGK_RPo1yKUyEbaGoFXFMBd0H4YHXI-ABYieZog,30100
11
+ anaplan_sdk/_async_clients/_cloud_works.py,sha256=aSgmJQvE7dSJawwK0A7GEBWs7wokWk7eCwRiQuiVg6I,17701
12
+ anaplan_sdk/_async_clients/_cw_flow.py,sha256=qJJFfnwLR7zIdZ_ay4fVI9zr3eP5B-qMcs4GlC9vqQY,3966
13
+ anaplan_sdk/_async_clients/_transactional.py,sha256=f-NlIjvRJ0NIKRcI6ZaO2YatLERwIaC0TY0fKvgUJ5Q,18050
14
+ anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
15
+ anaplan_sdk/_clients/_alm.py,sha256=oRHTjnCuwQYZDE2CBWA1u410jSImIroCsDOSeP9U9w8,12905
16
+ anaplan_sdk/_clients/_audit.py,sha256=Am6VviRw88_VC5i0N7TQTXEm6pJAHZb7xNaJTljD0fc,2850
17
+ anaplan_sdk/_clients/_bulk.py,sha256=9kN8LU3c0iDF-Ov47utBl-dj_hxX3twf7TOcolzogyI,30257
18
+ anaplan_sdk/_clients/_cloud_works.py,sha256=1bbMYM_g8MSorxTI9Au_dFzbJZgKGJVBE6DYlbWBR0U,17492
19
+ anaplan_sdk/_clients/_cw_flow.py,sha256=x64Ua2FwCpt8vab6gaLV8tDwW_ugJrDfU5dv-TnmM2M,3855
20
+ anaplan_sdk/_clients/_transactional.py,sha256=IgkvBaq1Ep5mB-uxu6QoE17cUCfsodvV8dppASQrIT4,17875
21
+ anaplan_sdk/models/__init__.py,sha256=zfwDQJQrXuLEXSpbJcm08a_YK1P7a7u-kMhwtJiJFmA,1783
22
+ anaplan_sdk/models/_alm.py,sha256=oeENd0YM7-LoIRBq2uATIQTxVgIP9rXx3UZE2UnQAp0,4670
23
+ anaplan_sdk/models/_base.py,sha256=6AZc9CfireUKgpZfMxYKu4MbwiyHQOsGLjKrxGXBLic,508
24
+ anaplan_sdk/models/_bulk.py,sha256=C0s6XdvHxuJHrPXU-pnZ1JXK1PJOl9FScHArpaox_mQ,8489
25
+ anaplan_sdk/models/_transactional.py,sha256=2bH10zvtMb5Lfh6DC7iQk72aEwq6tyLQ-XnH_0wYSqI,14172
26
+ anaplan_sdk/models/cloud_works.py,sha256=APUGDt_e-JshtXkba5cQh5rZkXOZBz0Aix0qVNdEWgw,19501
27
+ anaplan_sdk/models/flows.py,sha256=SuLgNj5-2SeE3U1i8iY8cq2IkjuUgd_3M1n2ENructk,3625
28
+ anaplan_sdk-0.5.0a6.dist-info/METADATA,sha256=VZfvzZQlLtBEOFFXMexsiEaetmGPe8fLilZBewT7iOg,3678
29
+ anaplan_sdk-0.5.0a6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
+ anaplan_sdk-0.5.0a6.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
31
+ anaplan_sdk-0.5.0a6.dist-info/RECORD,,
@@ -1,30 +0,0 @@
1
- anaplan_sdk/__init__.py,sha256=WScEKtXlnRLjCb-j3qW9W4kEACTyPsTLFs-L54et2TQ,351
2
- anaplan_sdk/_auth.py,sha256=l5z2WCcfQ05OkuQ1dcmikp6dB87Rw1qy2zu8bbaAQTs,16620
3
- anaplan_sdk/_oauth.py,sha256=AynlJDrGIinQT0jwxI2RSvtU4D7Wasyw3H1uicdlLVI,12672
4
- anaplan_sdk/_services.py,sha256=D54hGrzj1MSj7_6-WDZUZsoLcThI_neW4aIoDOqALjE,20869
5
- anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
6
- anaplan_sdk/_async_clients/__init__.py,sha256=pZXgMMg4S9Aj_pxQCaSiPuNG-sePVGBtNJ0133VjqW4,364
7
- anaplan_sdk/_async_clients/_alm.py,sha256=zvKEvXlxNkcQim_XvyZLCbDafFldljg8APHqhAAIfvw,13147
8
- anaplan_sdk/_async_clients/_audit.py,sha256=r3pJ4iHmXHyfDu19L80UTZzMReg6FKE7DlzyhXLhI8A,2896
9
- anaplan_sdk/_async_clients/_bulk.py,sha256=JkXZ6mtK04bATyVZA6yA8akjVerurRjgINCViXAwoWM,30069
10
- anaplan_sdk/_async_clients/_cloud_works.py,sha256=VB4l93426A0Xes5dZ6DsDu0go-BVNhs2RZn2zX5DSOc,17675
11
- anaplan_sdk/_async_clients/_cw_flow.py,sha256=_allKIOP-qb33wrOj6GV5VAOvrCXOVJ1QXvck-jsocQ,3935
12
- anaplan_sdk/_async_clients/_transactional.py,sha256=U6X5pW7By387JOgvHx-GmgVRi7MRJKALpx0lWI6xRMo,18024
13
- anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
14
- anaplan_sdk/_clients/_alm.py,sha256=3U7Cy5U5TsePF1YPogXvsOzNeQlQm_ezO5TlmD-Xbbs,12874
15
- anaplan_sdk/_clients/_audit.py,sha256=oF1-7rGfYWG6LfM-i0vJzgpx4NAsLczo4welJR14N-U,2819
16
- anaplan_sdk/_clients/_bulk.py,sha256=-I0e5yBHhQPIuS9pF_dRRi7OmY7H88P5mGPusGupKi4,30226
17
- anaplan_sdk/_clients/_cloud_works.py,sha256=FsCp2wPxIoArAN1vcIfOI6ANNkK2ZebQ4MWJZB-nFJU,17466
18
- anaplan_sdk/_clients/_cw_flow.py,sha256=O6t4utbDZdSVXGC0PXUcPpQ4oXrPohU9_8SUBCpxTXw,3824
19
- anaplan_sdk/_clients/_transactional.py,sha256=SaHAnaGLZrhXmM8d6JnWWkwf-sVCEDW0nL2a4_wvjfk,17849
20
- anaplan_sdk/models/__init__.py,sha256=zfwDQJQrXuLEXSpbJcm08a_YK1P7a7u-kMhwtJiJFmA,1783
21
- anaplan_sdk/models/_alm.py,sha256=oeENd0YM7-LoIRBq2uATIQTxVgIP9rXx3UZE2UnQAp0,4670
22
- anaplan_sdk/models/_base.py,sha256=6AZc9CfireUKgpZfMxYKu4MbwiyHQOsGLjKrxGXBLic,508
23
- anaplan_sdk/models/_bulk.py,sha256=C0s6XdvHxuJHrPXU-pnZ1JXK1PJOl9FScHArpaox_mQ,8489
24
- anaplan_sdk/models/_transactional.py,sha256=2bH10zvtMb5Lfh6DC7iQk72aEwq6tyLQ-XnH_0wYSqI,14172
25
- anaplan_sdk/models/cloud_works.py,sha256=APUGDt_e-JshtXkba5cQh5rZkXOZBz0Aix0qVNdEWgw,19501
26
- anaplan_sdk/models/flows.py,sha256=SuLgNj5-2SeE3U1i8iY8cq2IkjuUgd_3M1n2ENructk,3625
27
- anaplan_sdk-0.5.0a5.dist-info/METADATA,sha256=cPMNYurLnr08YAdhsgOUFlrcw-xehz2gd5MQF_TrFg8,3678
28
- anaplan_sdk-0.5.0a5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
29
- anaplan_sdk-0.5.0a5.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
30
- anaplan_sdk-0.5.0a5.dist-info/RECORD,,