anaplan-sdk 0.2.5__py3-none-any.whl → 0.2.7__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/__init__.py +2 -1
- anaplan_sdk/_async_clients/_audit.py +69 -0
- anaplan_sdk/_async_clients/_bulk.py +13 -1
- anaplan_sdk/_base.py +8 -4
- anaplan_sdk/_clients/__init__.py +2 -1
- anaplan_sdk/_clients/_audit.py +68 -0
- anaplan_sdk/_clients/_bulk.py +10 -0
- {anaplan_sdk-0.2.5.dist-info → anaplan_sdk-0.2.7.dist-info}/METADATA +1 -1
- anaplan_sdk-0.2.7.dist-info/RECORD +19 -0
- anaplan_sdk-0.2.5.dist-info/RECORD +0 -17
- {anaplan_sdk-0.2.5.dist-info → anaplan_sdk-0.2.7.dist-info}/WHEEL +0 -0
- {anaplan_sdk-0.2.5.dist-info → anaplan_sdk-0.2.7.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
from ._alm import _AsyncAlmClient
|
2
|
+
from ._audit import _AsyncAuditClient
|
2
3
|
from ._bulk import AsyncClient
|
3
4
|
from ._transactional import _AsyncTransactionalClient
|
4
5
|
|
5
|
-
__all__ = ["_AsyncAlmClient", "
|
6
|
+
__all__ = ["AsyncClient", "_AsyncAlmClient", "_AsyncAuditClient", "_AsyncTransactionalClient"]
|
@@ -0,0 +1,69 @@
|
|
1
|
+
from asyncio import gather
|
2
|
+
from itertools import chain
|
3
|
+
from math import ceil
|
4
|
+
from typing import Literal
|
5
|
+
|
6
|
+
import httpx
|
7
|
+
|
8
|
+
from anaplan_sdk._base import _AsyncBaseClient
|
9
|
+
|
10
|
+
Event = Literal["all", "byok", "user_activity"]
|
11
|
+
|
12
|
+
|
13
|
+
class _AsyncAuditClient(_AsyncBaseClient):
|
14
|
+
def __init__(self, client: httpx.AsyncClient, retry_count: int) -> None:
|
15
|
+
self._client = client
|
16
|
+
self._limit = 10_000
|
17
|
+
self._url = "https://audit.anaplan.com/audit/api/1/events"
|
18
|
+
super().__init__(retry_count, client)
|
19
|
+
|
20
|
+
async def _get_total(self, days_into_past: int = 60, event_type: Event = "all") -> int:
|
21
|
+
return ( # noqa
|
22
|
+
await self._get(
|
23
|
+
self._url,
|
24
|
+
params={
|
25
|
+
"limit": 0,
|
26
|
+
"type": event_type,
|
27
|
+
"intervalInHours": days_into_past * 24,
|
28
|
+
},
|
29
|
+
)
|
30
|
+
)["meta"]["paging"]["totalSize"] # noqa
|
31
|
+
|
32
|
+
async def _get_result_page(
|
33
|
+
self,
|
34
|
+
days_into_past: int = 60,
|
35
|
+
event_type: Event = "all",
|
36
|
+
limit: int = 10_000,
|
37
|
+
offset: int = 0,
|
38
|
+
) -> list:
|
39
|
+
return (
|
40
|
+
await self._get(
|
41
|
+
self._url,
|
42
|
+
params={
|
43
|
+
"intervalInHours": days_into_past * 24,
|
44
|
+
"limit": limit,
|
45
|
+
"offset": offset,
|
46
|
+
"type": event_type,
|
47
|
+
},
|
48
|
+
)
|
49
|
+
).get("response", [])
|
50
|
+
|
51
|
+
async def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
|
52
|
+
total = await self._get_total(days_into_past, event_type)
|
53
|
+
if total == 0:
|
54
|
+
return []
|
55
|
+
if total <= 10_000:
|
56
|
+
return await self._get_result_page(total)
|
57
|
+
|
58
|
+
return list(
|
59
|
+
chain.from_iterable(
|
60
|
+
await gather(
|
61
|
+
*(
|
62
|
+
self._get_result_page(
|
63
|
+
days_into_past, event_type, self._limit, n * self._limit
|
64
|
+
)
|
65
|
+
for n in range(ceil(total / self._limit))
|
66
|
+
)
|
67
|
+
)
|
68
|
+
)
|
69
|
+
)
|
@@ -17,6 +17,7 @@ from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierExceptio
|
|
17
17
|
from anaplan_sdk.models import Action, Export, File, Import, Model, Process, Workspace
|
18
18
|
|
19
19
|
from ._alm import _AsyncAlmClient
|
20
|
+
from ._audit import _AsyncAuditClient
|
20
21
|
from ._transactional import _AsyncTransactionalClient
|
21
22
|
|
22
23
|
logging.getLogger("httpx").setLevel(logging.CRITICAL)
|
@@ -109,6 +110,7 @@ class AsyncClient(_AsyncBaseClient):
|
|
109
110
|
self._alm_client = (
|
110
111
|
_AsyncAlmClient(self._client, model_id, self._retry_count) if model_id else None
|
111
112
|
)
|
113
|
+
self.audit = _AsyncAuditClient(self._client, self._retry_count)
|
112
114
|
self.status_poll_delay = status_poll_delay
|
113
115
|
self.upload_chunk_size = upload_chunk_size
|
114
116
|
self.allow_file_creation = allow_file_creation
|
@@ -379,7 +381,7 @@ class AsyncClient(_AsyncBaseClient):
|
|
379
381
|
|
380
382
|
async def export_and_download(self, action_id: int) -> bytes:
|
381
383
|
"""
|
382
|
-
Convenience wrapper around `run_action()`
|
384
|
+
Convenience wrapper around `run_action()` and `get_file()` to run an export action and
|
383
385
|
download the exported content in one call.
|
384
386
|
:param action_id: The identifier of the action to run.
|
385
387
|
:return: The content of the exported file.
|
@@ -387,6 +389,16 @@ class AsyncClient(_AsyncBaseClient):
|
|
387
389
|
await self.run_action(action_id)
|
388
390
|
return await self.get_file(action_id)
|
389
391
|
|
392
|
+
async def list_task_status(self, action_id: int) -> list:
|
393
|
+
"""
|
394
|
+
Retrieves the status of all tasks spawned by the specified action.
|
395
|
+
:param action_id: The identifier of the action that was invoked.
|
396
|
+
:return: The list of tasks spawned by the action.
|
397
|
+
"""
|
398
|
+
return (await self._get(f"{self._url}/{action_url(action_id)}/{action_id}/tasks")).get(
|
399
|
+
"tasks", []
|
400
|
+
)
|
401
|
+
|
390
402
|
async def get_task_status(
|
391
403
|
self, action_id: int, task_id: str
|
392
404
|
) -> dict[str, float | int | str | list | dict | bool]:
|
anaplan_sdk/_base.py
CHANGED
@@ -23,8 +23,8 @@ class _BaseClient:
|
|
23
23
|
self._retry_count = retry_count
|
24
24
|
self._client = client
|
25
25
|
|
26
|
-
def _get(self, url: str) -> dict[str, float | int | str | list | dict | bool]:
|
27
|
-
return self._run_with_retry(self._client.get, url).json()
|
26
|
+
def _get(self, url: str, **kwargs) -> dict[str, float | int | str | list | dict | bool]:
|
27
|
+
return self._run_with_retry(self._client.get, url, **kwargs).json()
|
28
28
|
|
29
29
|
def _get_binary(self, url: str) -> bytes:
|
30
30
|
return self._run_with_retry(self._client.get, url).content
|
@@ -59,14 +59,16 @@ class _BaseClient:
|
|
59
59
|
url = args[0] or kwargs.get("url")
|
60
60
|
logger.info(f"Retrying for: {url}")
|
61
61
|
|
62
|
+
raise AnaplanException("Exhausted all retries without a successful response or Error.")
|
63
|
+
|
62
64
|
|
63
65
|
class _AsyncBaseClient:
|
64
66
|
def __init__(self, retry_count: int, client: httpx.AsyncClient):
|
65
67
|
self._retry_count = retry_count
|
66
68
|
self._client = client
|
67
69
|
|
68
|
-
async def _get(self, url: str) -> dict[str, float | int | str | list | dict | bool]:
|
69
|
-
return (await self._run_with_retry(self._client.get, url)).json()
|
70
|
+
async def _get(self, url: str, **kwargs) -> dict[str, float | int | str | list | dict | bool]:
|
71
|
+
return (await self._run_with_retry(self._client.get, url, **kwargs)).json()
|
70
72
|
|
71
73
|
async def _get_binary(self, url: str) -> bytes:
|
72
74
|
return (await self._run_with_retry(self._client.get, url)).content
|
@@ -105,6 +107,8 @@ class _AsyncBaseClient:
|
|
105
107
|
url = args[0] or kwargs.get("url")
|
106
108
|
logger.info(f"Retrying for: {url}")
|
107
109
|
|
110
|
+
raise AnaplanException("Exhausted all retries without a successful response or Error.")
|
111
|
+
|
108
112
|
|
109
113
|
def action_url(action_id: int) -> Literal["imports", "exports", "actions", "processes"]:
|
110
114
|
"""
|
anaplan_sdk/_clients/__init__.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from ._alm import _AlmClient
|
2
|
+
from ._audit import _AuditClient
|
2
3
|
from ._bulk import Client
|
3
4
|
from ._transactional import _TransactionalClient
|
4
5
|
|
5
|
-
__all__ = ["_AlmClient", "
|
6
|
+
__all__ = ["Client", "_AlmClient", "_AuditClient", "_TransactionalClient"]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
from itertools import chain
|
2
|
+
from math import ceil
|
3
|
+
from typing import Literal
|
4
|
+
|
5
|
+
import httpx
|
6
|
+
|
7
|
+
from anaplan_sdk._base import _BaseClient
|
8
|
+
|
9
|
+
Event = Literal["all", "byok", "user_activity"]
|
10
|
+
|
11
|
+
|
12
|
+
class _AuditClient(_BaseClient):
|
13
|
+
def __init__(self, client: httpx.Client, retry_count: int, thread_count: int) -> None:
|
14
|
+
self._client = client
|
15
|
+
self._limit = 10_000
|
16
|
+
self._thread_count = thread_count
|
17
|
+
self._url = "https://audit.anaplan.com/audit/api/1/events"
|
18
|
+
super().__init__(retry_count, client)
|
19
|
+
|
20
|
+
def _get_total(self, days_into_past: int = 60, event_type: Event = "all") -> int:
|
21
|
+
return ( # noqa
|
22
|
+
self._get(
|
23
|
+
self._url,
|
24
|
+
params={
|
25
|
+
"limit": 0,
|
26
|
+
"type": event_type,
|
27
|
+
"intervalInHours": days_into_past * 24,
|
28
|
+
},
|
29
|
+
)
|
30
|
+
)["meta"]["paging"]["totalSize"] # noqa
|
31
|
+
|
32
|
+
def _get_result_page(
|
33
|
+
self,
|
34
|
+
days_into_past: int = 60,
|
35
|
+
event_type: Event = "all",
|
36
|
+
limit: int = 10_000,
|
37
|
+
offset: int = 0,
|
38
|
+
) -> list:
|
39
|
+
return (
|
40
|
+
self._get(
|
41
|
+
self._url,
|
42
|
+
params={
|
43
|
+
"intervalInHours": days_into_past * 24,
|
44
|
+
"limit": limit,
|
45
|
+
"offset": offset,
|
46
|
+
"type": event_type,
|
47
|
+
},
|
48
|
+
)
|
49
|
+
).get("response", [])
|
50
|
+
|
51
|
+
def get_events(self, days_into_past: int = 30, event_type: Event = "all") -> list:
|
52
|
+
total = self._get_total(days_into_past, event_type)
|
53
|
+
if total == 0:
|
54
|
+
return []
|
55
|
+
if total <= 10_000:
|
56
|
+
return self._get_result_page(total)
|
57
|
+
|
58
|
+
from concurrent.futures import ThreadPoolExecutor
|
59
|
+
|
60
|
+
with ThreadPoolExecutor(max_workers=self._thread_count) as executor:
|
61
|
+
futures = [
|
62
|
+
executor.submit(
|
63
|
+
self._get_result_page, days_into_past, event_type, self._limit, n * self._limit
|
64
|
+
)
|
65
|
+
for n in range(ceil(total / self._limit))
|
66
|
+
]
|
67
|
+
results = [future.result() for future in futures]
|
68
|
+
return list(chain.from_iterable(results))
|
anaplan_sdk/_clients/_bulk.py
CHANGED
@@ -18,6 +18,7 @@ from anaplan_sdk.exceptions import AnaplanActionError, InvalidIdentifierExceptio
|
|
18
18
|
from anaplan_sdk.models import Action, Export, File, Import, Model, Process, Workspace
|
19
19
|
|
20
20
|
from ._alm import _AlmClient
|
21
|
+
from ._audit import _AuditClient
|
21
22
|
from ._transactional import _TransactionalClient
|
22
23
|
|
23
24
|
logging.getLogger("httpx").setLevel(logging.CRITICAL)
|
@@ -115,6 +116,7 @@ class Client(_BaseClient):
|
|
115
116
|
_AlmClient(self._client, model_id, self._retry_count) if model_id else None
|
116
117
|
)
|
117
118
|
self._thread_count = multiprocessing.cpu_count()
|
119
|
+
self.audit = _AuditClient(self._client, self._retry_count, self._thread_count)
|
118
120
|
self.status_poll_delay = status_poll_delay
|
119
121
|
self.upload_parallel = upload_parallel
|
120
122
|
self.upload_chunk_size = upload_chunk_size
|
@@ -380,6 +382,14 @@ class Client(_BaseClient):
|
|
380
382
|
self.run_action(action_id)
|
381
383
|
return self.get_file(action_id)
|
382
384
|
|
385
|
+
def list_task_status(self, action_id: int) -> list:
|
386
|
+
"""
|
387
|
+
Retrieves the status of all tasks spawned by the specified action.
|
388
|
+
:param action_id: The identifier of the action that was invoked.
|
389
|
+
:return: The list of tasks spawned by the action.
|
390
|
+
"""
|
391
|
+
return self._get(f"{self._url}/{action_url(action_id)}/{action_id}/tasks").get("tasks", [])
|
392
|
+
|
383
393
|
def get_task_status(
|
384
394
|
self, action_id: int, task_id: str
|
385
395
|
) -> dict[str, float | int | str | list | dict | bool]:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: anaplan-sdk
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.7
|
4
4
|
Summary: Provides pythonic access to 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,19 @@
|
|
1
|
+
anaplan_sdk/__init__.py,sha256=5fr-SZSsH6f3vkRUTDoK6xdAN31cCpe9Mwz2VNu47Uw,134
|
2
|
+
anaplan_sdk/_auth.py,sha256=wRcMpdDiHuV-dtiGAKElDiwzfZAEeTOFWfSfaLwNPoU,6597
|
3
|
+
anaplan_sdk/_base.py,sha256=2Te7rg_o8_1KD64NfKsDiPLladaoDxMuzk0PaAUNSr8,5299
|
4
|
+
anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
|
5
|
+
anaplan_sdk/models.py,sha256=ceMaVctpjwQHk7a71Io_-1YcCQshx5i1YYnqxS51nYw,12491
|
6
|
+
anaplan_sdk/_async_clients/__init__.py,sha256=wT6qfi4f_4vLFWTJQTsBw8r3DrHtoTIVqi88p5_j-Cg,259
|
7
|
+
anaplan_sdk/_async_clients/_alm.py,sha256=-sFk91tRihc5GVPlW41-I5sQ0fxSRCSYTop5S4q2lHc,3673
|
8
|
+
anaplan_sdk/_async_clients/_audit.py,sha256=sPu5D_4ENh0iFdlPjuOA1ffFUdAhs2T_M5vHzpwFouA,2194
|
9
|
+
anaplan_sdk/_async_clients/_bulk.py,sha256=JmjvZKuekHQceBmItexhkk9YT6njg5_gsYLdtGCyWzE,22202
|
10
|
+
anaplan_sdk/_async_clients/_transactional.py,sha256=wX_1U5YS4uwrr8D8MfNkfeA-ylFERMb-6xvewG799xY,4961
|
11
|
+
anaplan_sdk/_clients/__init__.py,sha256=FsbwvZC1FHrxuRXwbPxUzbhz_lO1DpXIxEOjx6-3QuA,219
|
12
|
+
anaplan_sdk/_clients/_alm.py,sha256=Vcrn3TjHlH365O7F1TKc1WiZWURNqQf_gSc8UUVESM8,3564
|
13
|
+
anaplan_sdk/_clients/_audit.py,sha256=vYJxZPM-OS5f4wgzKd2Wcl9kbeypeYrlhT7JSh89KM0,2269
|
14
|
+
anaplan_sdk/_clients/_bulk.py,sha256=_rhp7MgcIRq0FYwxEzvU7jBkDm5PQaw9YCU_zqIwauY,22222
|
15
|
+
anaplan_sdk/_clients/_transactional.py,sha256=4NYhq2HdxNh4K-HqMyYoyAEqKih2eTtFiXDZi8xOaf8,4695
|
16
|
+
anaplan_sdk-0.2.7.dist-info/METADATA,sha256=Kx0L4iI7OVMVSEHTZpv-uCwe8fTGBkOORKNuH-RUSFw,3599
|
17
|
+
anaplan_sdk-0.2.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
18
|
+
anaplan_sdk-0.2.7.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
19
|
+
anaplan_sdk-0.2.7.dist-info/RECORD,,
|
@@ -1,17 +0,0 @@
|
|
1
|
-
anaplan_sdk/__init__.py,sha256=5fr-SZSsH6f3vkRUTDoK6xdAN31cCpe9Mwz2VNu47Uw,134
|
2
|
-
anaplan_sdk/_auth.py,sha256=wRcMpdDiHuV-dtiGAKElDiwzfZAEeTOFWfSfaLwNPoU,6597
|
3
|
-
anaplan_sdk/_base.py,sha256=qO5NVmvTYvH7SIEe6SlA-pdMiCEe61EgWXxifvOSG7o,5061
|
4
|
-
anaplan_sdk/exceptions.py,sha256=ALkA9fBF0NQ7dufFxV6AivjmHyuJk9DOQ9jtJV2n7f0,1809
|
5
|
-
anaplan_sdk/models.py,sha256=ceMaVctpjwQHk7a71Io_-1YcCQshx5i1YYnqxS51nYw,12491
|
6
|
-
anaplan_sdk/_async_clients/__init__.py,sha256=OOQrL69Xa-F1Mx3N_UaipdbtwNDMbZ6rJB-wHo54Ys4,199
|
7
|
-
anaplan_sdk/_async_clients/_alm.py,sha256=-sFk91tRihc5GVPlW41-I5sQ0fxSRCSYTop5S4q2lHc,3673
|
8
|
-
anaplan_sdk/_async_clients/_bulk.py,sha256=feCUZKh4PfOoC1VNIlBY5DBCL3MxuOsPrDsUPL56dVw,21662
|
9
|
-
anaplan_sdk/_async_clients/_transactional.py,sha256=wX_1U5YS4uwrr8D8MfNkfeA-ylFERMb-6xvewG799xY,4961
|
10
|
-
anaplan_sdk/_clients/__init__.py,sha256=vKMLbsWDjlEiJY1q296lnZzYkJk3eKtIwJLoWrO7yxk,169
|
11
|
-
anaplan_sdk/_clients/_alm.py,sha256=Vcrn3TjHlH365O7F1TKc1WiZWURNqQf_gSc8UUVESM8,3564
|
12
|
-
anaplan_sdk/_clients/_bulk.py,sha256=sdOQeJbxxJTPNKmUN3JMiKBVXTR8HJJcsFaKjduiHq4,21705
|
13
|
-
anaplan_sdk/_clients/_transactional.py,sha256=4NYhq2HdxNh4K-HqMyYoyAEqKih2eTtFiXDZi8xOaf8,4695
|
14
|
-
anaplan_sdk-0.2.5.dist-info/METADATA,sha256=BdoPh7HxuwpO1F3dFMqsH1mLu_JHSYkz3HqYeNfVfJo,3599
|
15
|
-
anaplan_sdk-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
-
anaplan_sdk-0.2.5.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
17
|
-
anaplan_sdk-0.2.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|