Skip to content

Commit 28240a9

Browse files
authored
Merge pull request #25 from kaincenteno/activity_download
adds a download util to download the resulting file from OrgDeviceActivity object adds an initial wait time of 10 seconds for the result of assign/unassign devices to prevent rate limitting updates parameters from device_id to device_ids when refering to a list of devices updates unassignment/assignment command to include downloading the CSV with the result updates cli.md and readme.md with changes
2 parents 9e7e273 + 4890aa2 commit 28240a9

File tree

5 files changed

+62
-12
lines changed

5 files changed

+62
-12
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,17 @@ device_assigned_server = axm_client.list_devices_in_mdm_server(device_id='SERIAL
7979
print(device_assigned_server)
8080

8181
assignment_result = axm_client.assign_unassign_device_to_mdm_server(
82-
device_id='SERIAL_NUMBER',
82+
device_ids=['SERIAL_NUMBER', "ANOTHER_SERIAL_NUMBER"],
8383
server_id="MDM_SERVER_ID",
8484
action="ASSIGN_DEVICES"|"UNASSIGN_DEVICES"
8585
)
86+
print(assignment_result)
8687

8788
apple_care_coverage = axm_client.get_apple_care_coverage(device_id='SERIAL_NUMBER')
8889
print(apple_care_coverage)
8990
```
9091

9192
## Issues:
9293
* need to add tests
93-
* unassign, assign devices need to be able to pass more than 1 device
9494

9595
This is still a work in progress

docs/cli.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ Assign one or more devices to an MDM server.
130130
**Usage**:
131131

132132
```console
133-
$ pyaxm-cli assign-device [OPTIONS] DEVICE_ID... SERVER_ID
133+
$ pyaxm-cli assign-device [OPTIONS] DEVICE_IDS... SERVER_ID
134134
```
135135

136136
**Arguments**:
137137

138-
* `DEVICE_ID...`: [required]
138+
* `DEVICE_IDS...`: [required]
139139
* `SERVER_ID`: [required]
140140

141141
**Options**:
@@ -149,12 +149,12 @@ Unassign one or more devices from an MDM server.
149149
**Usage**:
150150

151151
```console
152-
$ pyaxm-cli unassign-device [OPTIONS] DEVICE_ID... SERVER_ID
152+
$ pyaxm-cli unassign-device [OPTIONS] DEVICE_IDS... SERVER_ID
153153
```
154154

155155
**Arguments**:
156156

157-
* `DEVICE_ID...`: [required]
157+
* `DEVICE_IDS...`: [required]
158158
* `SERVER_ID`: [required]
159159

160160
**Options**:

pyaxm/cli.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing_extensions import Annotated
55
from typing import List
66
from pyaxm.client import Client
7+
from pyaxm.utils import download_activity_csv
78

89
app = typer.Typer()
910

@@ -71,24 +72,32 @@ def mdm_server_assigned(device_id: Annotated[str, typer.Argument()]):
7172
df.to_csv(sys.stdout, index=False)
7273

7374
@app.command()
74-
def assign_device(device_id: Annotated[List[str], typer.Argument()], server_id: Annotated[str, typer.Argument()]):
75+
def assign_device(device_ids: Annotated[List[str], typer.Argument()], server_id: Annotated[str, typer.Argument()]):
7576
"""Assign one or more devices to an MDM server."""
7677
client = Client()
77-
activity = client.assign_unassign_device_to_mdm_server(device_id, server_id, 'ASSIGN_DEVICES')
78+
activity = client.assign_unassign_device_to_mdm_server(device_ids, server_id, 'ASSIGN_DEVICES')
7879
activity_data = {'id': activity.id}
7980
activity_data.update(activity.attributes.model_dump())
8081
df = pd.DataFrame([activity_data])
8182
df.to_csv(sys.stdout, index=False)
8283

84+
file_path = download_activity_csv(activity)
85+
if file_path:
86+
typer.echo(f"Report downloaded successfully to: {file_path}")
87+
8388
@app.command()
84-
def unassign_device(device_id: Annotated[List[str], typer.Argument()], server_id: Annotated[str, typer.Argument()]):
89+
def unassign_device(device_ids: Annotated[List[str], typer.Argument()], server_id: Annotated[str, typer.Argument()]):
8590
"""Unassign one or more devices from an MDM server."""
8691
client = Client()
87-
activity = client.assign_unassign_device_to_mdm_server(device_id, server_id, 'UNASSIGN_DEVICES')
92+
activity = client.assign_unassign_device_to_mdm_server(device_ids, server_id, 'UNASSIGN_DEVICES')
8893
activity_data = {'id': activity.id}
8994
activity_data.update(activity.attributes.model_dump())
9095
df = pd.DataFrame([activity_data])
9196
df.to_csv(sys.stdout, index=False)
97+
98+
file_path = download_activity_csv(activity)
99+
if file_path:
100+
typer.echo(f"Report downloaded successfully to: {file_path}")
92101

93102
if __name__ == "__main__":
94103
app()

pyaxm/client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,12 @@ def assign_unassign_device_to_mdm_server(
157157
)
158158

159159
# use the ID to check the status until it is complete
160+
# Waits before the first check to prevent rate limitting
161+
time.sleep(10)
160162
activity_response = self.abm.get_device_activity(unassign_response.data.id, self.access_token.value)
161163
retry = 0
162-
while 'COMPLETED' not in activity_response.data.attributes.status and retry < 5:
163-
time.sleep(2 ** retry)
164+
while activity_response.data.attributes.status == 'IN_PROGRESS' and retry < 5:
165+
time.sleep(10 * retry)
164166
retry += 1
165167
activity_response = self.abm.get_device_activity(unassign_response.data.id, self.access_token.value)
166168

pyaxm/utils.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
import re
3+
import requests
4+
from typing import Optional
5+
from urllib.parse import urlparse, parse_qs
6+
from pyaxm.models import OrgDeviceActivity
7+
8+
9+
def download_activity_csv(activity: OrgDeviceActivity, save_path: str = None) -> Optional[str]:
10+
"""
11+
Download CSV file from activity if download URL is available.
12+
13+
:param activity: OrgDeviceActivity object with attributes.downloadUrl property
14+
:param save_path: Optional path to save file (defaults to current directory)
15+
:return: Path to saved file or None if no download URL
16+
"""
17+
if not save_path:
18+
save_path = os.getcwd()
19+
20+
if not (activity.attributes and activity.attributes.downloadUrl):
21+
return None
22+
23+
url = activity.attributes.downloadUrl
24+
parsed_url = urlparse(url)
25+
query_params = parse_qs(parsed_url.query)
26+
27+
disposition = query_params['response-content-disposition'][0]
28+
match = re.search(r'filename="([^"]+)"', disposition)
29+
filename = match.group(1)
30+
31+
file_path = os.path.join(save_path, filename)
32+
33+
response = requests.get(url)
34+
response.raise_for_status()
35+
36+
with open(file_path, 'wb') as f:
37+
f.write(response.content)
38+
39+
return file_path

0 commit comments

Comments
 (0)