mcm-cli 1.4.0__tar.gz → 1.4.2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. {mcm_cli-1.4.0/mcm_cli.egg-info → mcm_cli-1.4.2}/PKG-INFO +3 -2
  2. {mcm_cli-1.4.0 → mcm_cli-1.4.2/mcm_cli.egg-info}/PKG-INFO +3 -2
  3. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcm_cli.egg-info/SOURCES.txt +6 -0
  4. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcm_cli.egg-info/requires.txt +1 -0
  5. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/__main__.py +5 -1
  6. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/command/account.py +10 -12
  7. mcm_cli-1.4.2/mcmcli/command/admin.py +485 -0
  8. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/command/auth.py +14 -0
  9. mcm_cli-1.4.2/mcmcli/command/campaign.py +317 -0
  10. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/command/config.py +2 -0
  11. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/command/decision.py +4 -0
  12. mcm_cli-1.4.2/mcmcli/command/report.py +150 -0
  13. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/command/wallet.py +37 -36
  14. mcm_cli-1.4.2/mcmcli/data/campaign.py +86 -0
  15. mcm_cli-1.4.2/mcmcli/data/platform_user.py +64 -0
  16. mcm_cli-1.4.2/mcmcli/data/report.py +110 -0
  17. mcm_cli-1.4.2/mcmcli/data/user_join_request.py +44 -0
  18. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/setup.py +3 -3
  19. mcm_cli-1.4.0/mcmcli/command/admin.py +0 -152
  20. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/LICENSE +0 -0
  21. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/NOTICE +0 -0
  22. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/README.md +0 -0
  23. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcm_cli.egg-info/dependency_links.txt +0 -0
  24. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcm_cli.egg-info/entry_points.txt +0 -0
  25. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcm_cli.egg-info/top_level.txt +0 -0
  26. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/__init__.py +0 -0
  27. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/account.py +0 -0
  28. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/account_user.py +0 -0
  29. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/decision.py +0 -0
  30. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/error.py +0 -0
  31. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/item.py +0 -0
  32. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/item_blocking_result.py +0 -0
  33. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/seller.py +0 -0
  34. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/token.py +0 -0
  35. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/data/wallet.py +0 -0
  36. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/logging.py +0 -0
  37. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/mcmcli/requests.py +0 -0
  38. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/setup.cfg +0 -0
  39. {mcm_cli-1.4.0 → mcm_cli-1.4.2}/tests/test_config.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: A command-line interface for Moloco Commerde Media
5
5
  Home-page: https://github.com/moloco-mcm/mcm-cli
6
6
  Author: Moloco MCM Team
7
7
  Author-email: mcm-help@moloco.com
8
8
  License: Apache-2.0 license
9
9
  Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.6
13
13
  Description-Content-Type: text/markdown
@@ -20,6 +20,7 @@ Requires-Dist: pygithub
20
20
  Requires-Dist: python-terraform
21
21
  Requires-Dist: requests
22
22
  Requires-Dist: rich
23
+ Requires-Dist: setuptools
23
24
  Requires-Dist: shortuuid
24
25
  Requires-Dist: toml
25
26
  Requires-Dist: typer
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: A command-line interface for Moloco Commerde Media
5
5
  Home-page: https://github.com/moloco-mcm/mcm-cli
6
6
  Author: Moloco MCM Team
7
7
  Author-email: mcm-help@moloco.com
8
8
  License: Apache-2.0 license
9
9
  Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
11
  Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.6
13
13
  Description-Content-Type: text/markdown
@@ -20,6 +20,7 @@ Requires-Dist: pygithub
20
20
  Requires-Dist: python-terraform
21
21
  Requires-Dist: requests
22
22
  Requires-Dist: rich
23
+ Requires-Dist: setuptools
23
24
  Requires-Dist: shortuuid
24
25
  Requires-Dist: toml
25
26
  Requires-Dist: typer
@@ -15,16 +15,22 @@ mcmcli/requests.py
15
15
  mcmcli/command/account.py
16
16
  mcmcli/command/admin.py
17
17
  mcmcli/command/auth.py
18
+ mcmcli/command/campaign.py
18
19
  mcmcli/command/config.py
19
20
  mcmcli/command/decision.py
21
+ mcmcli/command/report.py
20
22
  mcmcli/command/wallet.py
21
23
  mcmcli/data/account.py
22
24
  mcmcli/data/account_user.py
25
+ mcmcli/data/campaign.py
23
26
  mcmcli/data/decision.py
24
27
  mcmcli/data/error.py
25
28
  mcmcli/data/item.py
26
29
  mcmcli/data/item_blocking_result.py
30
+ mcmcli/data/platform_user.py
31
+ mcmcli/data/report.py
27
32
  mcmcli/data/seller.py
28
33
  mcmcli/data/token.py
34
+ mcmcli/data/user_join_request.py
29
35
  mcmcli/data/wallet.py
30
36
  tests/test_config.py
@@ -5,6 +5,7 @@ pygithub
5
5
  python-terraform
6
6
  requests
7
7
  rich
8
+ setuptools
8
9
  shortuuid
9
10
  toml
10
11
  typer
@@ -22,8 +22,10 @@ from typing import Optional
22
22
  import mcmcli.command.account
23
23
  import mcmcli.command.admin
24
24
  import mcmcli.command.auth
25
+ import mcmcli.command.campaign
25
26
  import mcmcli.command.config
26
27
  import mcmcli.command.decision
28
+ import mcmcli.command.report
27
29
  import mcmcli.command.wallet
28
30
  import mcmcli.logging
29
31
  import typer
@@ -35,13 +37,15 @@ def version():
35
37
  """
36
38
  Show the tool version
37
39
  """
38
- typer.echo(f"Version: mcm-cli v1.4.0")
40
+ typer.echo(f"Version: mcm-cli v1.4.2")
39
41
 
40
42
  app.add_typer(mcmcli.command.account.app, name="account", help="Ad account management")
41
43
  app.add_typer(mcmcli.command.admin.app, name="admin", help="Platform administration")
42
44
  app.add_typer(mcmcli.command.auth.app, name="auth", help="Authentication management")
45
+ app.add_typer(mcmcli.command.campaign.app, name="campaign", help="Campaign management")
43
46
  app.add_typer(mcmcli.command.config.app, name="config", help="Configurations")
44
47
  app.add_typer(mcmcli.command.decision.app, name="decision", help="Decision command")
48
+ app.add_typer(mcmcli.command.report.app, name="report", help="Report commands")
45
49
  app.add_typer(mcmcli.command.wallet.app, name="wallet", help="Wallet management")
46
50
 
47
51
  if __name__ == "__main__":
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from enum import Enum
16
+ from mcmcli.command.auth import AuthCommand, AuthHeaderName, AuthHeaderValue
16
17
  from mcmcli.data.account import Account, AccountListWrapper
17
18
  from mcmcli.data.account_user import User, UserWrapper, UserListWrapper
18
19
  from mcmcli.data.error import Error
@@ -348,8 +349,7 @@ class AccountCommand:
348
349
  def __init__(
349
350
  self,
350
351
  profile,
351
- auth_command: mcmcli.command.auth.AuthCommand,
352
- token
352
+ auth_command: AuthCommand,
353
353
  ):
354
354
  self.config = mcmcli.command.config.get_config(profile)
355
355
  if (self.config is None):
@@ -362,18 +362,20 @@ class AccountCommand:
362
362
  self.headers = {
363
363
  "accept": "application/json",
364
364
  "content-type": "application/json",
365
- "Authorization": f"Bearer {token}"
366
365
  }
367
366
 
367
+ self.refresh_token()
368
+
368
369
 
369
370
  def refresh_token(
370
371
  self,
371
372
  ) -> None:
372
- _, error, token = self.auth_command.get_token()
373
+ error, auth_header_name, auth_header_value = self.auth_command.get_auth_credential()
373
374
  if error:
374
375
  print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
375
- return
376
- self.headers["Authorization"] = f"Bearer {token.token}"
376
+ sys.exit()
377
+
378
+ self.headers[auth_header_name] = auth_header_value
377
379
 
378
380
 
379
381
  def retry_with_token_refresh(
@@ -760,12 +762,8 @@ class AccountCommand:
760
762
  def _create_account_command(
761
763
  profile: str
762
764
  ) -> Optional[AccountCommand]:
763
- auth = mcmcli.command.auth.AuthCommand(profile)
764
- _, error, token = auth.get_token()
765
- if error:
766
- print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
767
- return None
768
- return AccountCommand(profile, auth, token.token)
765
+ auth = AuthCommand(profile)
766
+ return AccountCommand(profile, auth)
769
767
 
770
768
 
771
769
  def _read_csv_file(
@@ -0,0 +1,485 @@
1
+ # Copyright 2023 Moloco, Inc
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from datetime import datetime, timedelta, timezone
15
+ from mcmcli.command.auth import AuthCommand, AuthHeaderName, AuthHeaderValue
16
+ from mcmcli.data.account import Account
17
+ from mcmcli.data.item_blocking_result import ItemBlockingResult
18
+ from mcmcli.data.campaign import Campaign
19
+ from mcmcli.data.error import Error
20
+ from mcmcli.data.item import Item
21
+ from mcmcli.data.platform_user import PlatformUser, PlatformUserListWrapper, PlatformUserWrapper
22
+ from mcmcli.requests import CurlString, api_request
23
+ from typing import Optional
24
+
25
+ import csv
26
+ import mcmcli.command.account
27
+ import mcmcli.command.auth
28
+ import mcmcli.command.campaign
29
+ import mcmcli.command.config
30
+ import mcmcli.command.wallet
31
+ import mcmcli.requests
32
+ import sys
33
+ import typer
34
+
35
+ from mcmcli.logging import echo, echo_newline, start_dot_printing, stop_dot_printing, print_error
36
+
37
+ app = typer.Typer(add_completion=False)
38
+
39
+ def _create_admin_command(profile):
40
+ auth = AuthCommand(profile)
41
+ return AdminCommand(profile, auth)
42
+
43
+ @app.command()
44
+ def list_wallet_balances(
45
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
46
+ ):
47
+ """
48
+ List the wallet balances of all of the ad accounts
49
+ """
50
+ admin = _create_admin_command(profile)
51
+ if admin is None:
52
+ return
53
+ admin.list_wallet_balances()
54
+
55
+
56
+ @app.command()
57
+ def list_all_campaigns(
58
+ profile: str = "default",
59
+ ):
60
+ """
61
+ List the campaigigns of all of the active ad accounts
62
+ """
63
+ admin = _create_admin_command(profile)
64
+ if admin is None:
65
+ return
66
+ error, account_campaigns = admin.list_all_campaigns()
67
+ if error:
68
+ print(error, file=sys.stderr, flush=True)
69
+ return
70
+
71
+ print("Account ID,Account Title,Campaign ID,Campaign Title,Ad Type,Start,End,Budget Period,Budget Amount,Enabling State,State,Created At,Updated At")
72
+ for account_campaign in account_campaigns:
73
+ a, c = account_campaign
74
+
75
+ print(f'"{a.id}","{a.title}",', end="", flush=True)
76
+ print(f'"{c.id}","{c.title}","{c.ad_type}",', end="", flush=True)
77
+ print(f'"{c.schedule.start}","{c.schedule.end}",', end="", flush=True)
78
+ print(f'"{c.budget.period}","{int(c.budget.amount.amount_micro) / 1000000}",', end="", flush=True)
79
+ print(f'"{c.enabling_state}","{c.state}",', end="", flush=True)
80
+ print(f'"{c.created_at}","{c.updated_at}",', end="", flush=True)
81
+ # print(f'"{";".join(c.catalog_item_ids)}"')
82
+ print("", flush=True)
83
+
84
+ @app.command()
85
+ def list_platform_users(
86
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
87
+ to_json: bool = typer.Option(False, help="Print raw output in json"),
88
+ profile: str = "default",
89
+ ):
90
+ """
91
+ List the users of the platform
92
+ """
93
+ admin = _create_admin_command(profile)
94
+ if admin is None:
95
+ return
96
+
97
+ curl, error, users = admin.list_platform_users(to_curl)
98
+ if to_curl:
99
+ print(curl)
100
+ return
101
+ if error:
102
+ print(error, file=sys.stderr, flush=True)
103
+ return
104
+ if to_json:
105
+ json_dumps = [x.model_dump_json() for x in users]
106
+ print(f"[{','.join(json_dumps)}]")
107
+ return
108
+
109
+ print('User ID,Created At,Updated At,Status,Email,Name,Roles')
110
+ for u in users:
111
+ roles = [f'{x.name} of {x.resource_type} {x.resource_id}' for x in u.roles]
112
+ print(f'{u.id},{u.created_at},{u.updated_at},{u.status},{u.email},{u.name},{';'.join(roles)}')
113
+
114
+
115
+ @app.command()
116
+ def get_platform_user(
117
+ user_email: str = typer.Option(help="User's email address"),
118
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
119
+ to_json: bool = typer.Option(False, help="Print raw output in json"),
120
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
121
+ ):
122
+ """
123
+ Get the email user's profile.
124
+ """
125
+ a = _create_admin_command(profile)
126
+ if a is None:
127
+ return
128
+
129
+ curl, error, user = a.get_platform_user(user_email, to_curl)
130
+ if to_curl:
131
+ print(curl)
132
+ return
133
+ if error:
134
+ print(f"ERROR: {error.message}")
135
+ return
136
+
137
+ if user is not None:
138
+ print(f"{user.model_dump_json()}")
139
+ return
140
+
141
+
142
+ @app.command()
143
+ def list_items(
144
+ account_id: str = typer.Option(help="Ad account ID"),
145
+ profile: str = "default",
146
+ ):
147
+ """
148
+ List the itmes, item status, and attached campaigns of a given ad account
149
+ """
150
+ admin = _create_admin_command(profile)
151
+ if admin is None:
152
+ return
153
+
154
+ error, campaigns = admin.list_campaigns(account_id)
155
+ if error:
156
+ print(f"ERROR: {error.message}")
157
+ return
158
+
159
+ campaign_item_obj = {} # build item list into item object with the item_id as an index
160
+ # print("Campaign ID, Campaign Title, Item ID, Item Title, Item Status")
161
+
162
+ for c in campaigns:
163
+ error, campaign_items = admin.list_campaign_items(account_id, c.id)
164
+ if error:
165
+ print(f"ERROR: {error.message}")
166
+ return
167
+ for ci in campaign_items:
168
+ if ci.id in campaign_item_obj:
169
+ campaign_item_obj[ci.id].append({
170
+ 'campaign_id': c.id,
171
+ 'campaign_title': c.title
172
+ })
173
+ else:
174
+ campaign_item_obj[ci.id] = [{
175
+ 'campaign_id': c.id,
176
+ 'campaign_title': c.title
177
+ }]
178
+ # print(f"{c['id']}, {c['title']}, {ci['id']}, {ci['title']}, {ci['is_active']}")
179
+
180
+ error, items = admin.list_items(account_id)
181
+ if error:
182
+ print(f"ERROR: {error.message}")
183
+
184
+ print("Ad Account ID,Item ID,Is Item Active,Item Title,Campaign ID,Campaign Title")
185
+ for i in items:
186
+ if i.id in campaign_item_obj:
187
+ ci = campaign_item_obj[i.id]
188
+ campaign_id_list = ""
189
+ campaign_title_list = ""
190
+ for c in ci:
191
+ campaign_id_list += f"{c['campaign_id']};"
192
+ campaign_title_list += f"{c['campaign_title']};"
193
+ campaign_id_list = campaign_id_list[:-1]
194
+ campaign_title_list = campaign_title_list[:-1]
195
+
196
+ print(f"{account_id},{i.id},{i.is_active},\"{i.title}\",{campaign_id_list},\"{campaign_title_list}\"")
197
+ else:
198
+ print(f"{account_id},{i.id},{i.is_active},\"{i.title}\",,")
199
+
200
+ @app.command()
201
+ def list_off_campaign_items(
202
+ account_id: str = typer.Option(help="Ad account ID"),
203
+ profile: str = "default",
204
+ ):
205
+ """
206
+ Lists the items that are not in any of campaigns
207
+ """
208
+ admin = _create_admin_command(profile)
209
+ if admin is None:
210
+ return
211
+
212
+ error, items = admin.list_items(account_id)
213
+ if error:
214
+ print(f"ERROR: {error.message}")
215
+ return
216
+ error, campaigns = admin.list_campaigns(account_id)
217
+ if error:
218
+ print(f"ERROR: {error.message}")
219
+ return
220
+
221
+ campaign_item_obj = {} # build item list into item object with the item_id as an index
222
+ for c in campaigns:
223
+ error, campaign_items = admin.list_campaign_items(account_id, c.id)
224
+ for ci in campaign_items:
225
+ campaign_item_obj[ci.id] = ci.title
226
+
227
+ print("Item ID, Item Title")
228
+ for i in items:
229
+ if i.id not in campaign_item_obj and i.is_active:
230
+ print(f"{i.id}, {i.title}")
231
+
232
+ @app.command()
233
+ def block_item(
234
+ item_id: str = typer.Option(help="Item ID"),
235
+ account_id: str = typer.Option(None, help="The Ad Account ID is applicable only for MSPI catalogs. If this value is provided, only the item associated with the specified seller ID will be removed from ad serving. If it is not provided, the specified item will be removed for all sellers in the MSPI catalog."),
236
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
237
+ profile: str = typer.Option("default", help="Profile name of the MCM CLI."),
238
+ ):
239
+ """
240
+ Item Kill Switch Command.
241
+ This API immediately blocks an item or an ad account item from appearing in ads by marking it as “blocked.”
242
+ """
243
+ timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
244
+
245
+ # print(f"invoked block_item(item_id={item_id}, account_id={account_id}, blocked='Requested at {timestamp}')");
246
+ admin = _create_admin_command(profile)
247
+ if admin is None:
248
+ return
249
+
250
+ curl, error, result = admin.block_item(item_id=item_id, account_id=account_id, to_curl=to_curl)
251
+ if curl:
252
+ print(curl)
253
+ return
254
+ if error:
255
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
256
+ return
257
+
258
+ print(result.model_dump_json())
259
+ return
260
+
261
+
262
+ class AdminCommand:
263
+ def __init__(
264
+ self,
265
+ profile,
266
+ auth_command: AuthCommand,
267
+ ):
268
+ self.config = mcmcli.command.config.get_config(profile)
269
+ if (self.config is None):
270
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
271
+ sys.exit()
272
+
273
+ self.profile = profile
274
+ self.auth_command = auth_command
275
+ self.api_base_url = f"{self.config['management_api_hostname']}/rmp/mgmt/v1/platforms/{self.config['platform_id']}"
276
+ self.headers = {
277
+ "accept": "application/json",
278
+ "content-type": "application/json",
279
+ }
280
+
281
+ self.refresh_token()
282
+
283
+
284
+ def refresh_token(
285
+ self,
286
+ ) -> None:
287
+ error, auth_header_name, auth_header_value = self.auth_command.get_auth_credential()
288
+ if error:
289
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
290
+ sys.exit()
291
+
292
+ self.headers[auth_header_name] = auth_header_value
293
+
294
+
295
+ def block_item(
296
+ self,
297
+ item_id,
298
+ account_id,
299
+ to_curl,
300
+ ) -> tuple[
301
+ Optional[CurlString],
302
+ Optional[Error],
303
+ Optional[ItemBlockingResult],
304
+ ]:
305
+ _api_url = f"{self.api_base_url}/item-status-bulk"
306
+ _requested_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
307
+ _payload = {
308
+ "items": [{
309
+ "item_id": item_id,
310
+ "updated_time": _requested_at,
311
+ "blocked": f'Requested at {_requested_at}',
312
+ }]
313
+ }
314
+ if account_id is not None:
315
+ _payload["items"][0]["seller_id"] = account_id
316
+
317
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
318
+ if curl:
319
+ return curl, None, None
320
+ if error:
321
+ return None, error, None
322
+ return None, None, ItemBlockingResult(**json_obj)
323
+
324
+
325
+ def get_platform_user(
326
+ self,
327
+ user_email,
328
+ to_curl = False,
329
+ ) -> tuple[
330
+ Optional[CurlString],
331
+ Optional[Error],
332
+ Optional[PlatformUser],
333
+ ]:
334
+ _api_url = f"{self.api_base_url}/users/{user_email}"
335
+ curl, error, json_obj = api_request('GET', to_curl, _api_url, self.headers)
336
+ if curl:
337
+ return curl, None, None
338
+ if error:
339
+ return None, error, None
340
+
341
+ ret = PlatformUserWrapper(**json_obj).user
342
+ return None, None, ret
343
+
344
+
345
+ def list_platform_users(
346
+ self,
347
+ to_curl=False,
348
+ ) -> tuple [
349
+ Optional[CurlString],
350
+ Optional[Error],
351
+ list[PlatformUser],
352
+ ]:
353
+ _api_url = f"{self.api_base_url}/users"
354
+
355
+ curl, error, json_obj = api_request('GET', to_curl, _api_url, self.headers)
356
+ if curl:
357
+ return curl, None, []
358
+ if error:
359
+ return None, error, []
360
+
361
+ user_list_wrapper = PlatformUserListWrapper(**json_obj)
362
+ users = user_list_wrapper.users
363
+ return None, None, users
364
+
365
+ def list_wallet_balances(
366
+ self
367
+ ):
368
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
369
+ wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command)
370
+ _, error, accounts = ac.list_accounts()
371
+ if error:
372
+ print(error, file=sys.stderr, flush=True)
373
+ return
374
+
375
+ print("ad_account_title, ad_account_id, credit_balance, prepaid_balance")
376
+ for id in accounts:
377
+ _, error, wallet = wc.get_balance(id, to_curl=False)
378
+ if error:
379
+ continue
380
+ w0 = wallet.accounts[0]
381
+ w1 = wallet.accounts[1]
382
+ credits = w0 if w0.type == 'CREDITS' else w1
383
+ prepaid = w1 if w1.type == 'PRE_PAID' else w0
384
+ credits = int(credits.balance.amount_micro) / 1000000
385
+ prepaid = int(prepaid.balance.amount_micro) / 1000000
386
+
387
+ print(f'"{accounts[id].title}", {id}, {credits}, {prepaid}')
388
+
389
+
390
+
391
+ def list_all_campaigns(
392
+ self
393
+ ) -> tuple [
394
+ Optional[Error],
395
+ list[tuple[Account, Campaign]]
396
+ ]:
397
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
398
+ cc = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
399
+ _, error, accounts = ac.list_accounts()
400
+ if error:
401
+ return error, []
402
+
403
+ echo('Collecting campaigns...')
404
+ return_value = []
405
+ for id in accounts:
406
+ account = accounts[id]
407
+ echo('.')
408
+ # print(f'{account.id}, \"{account.title}\"')
409
+ _, error, campaigns = cc.list_campaigns(account.id)
410
+ if error:
411
+ echo_newline(error)
412
+ continue
413
+ for c in campaigns:
414
+ return_value.append((account, c))
415
+
416
+ echo_newline(' done')
417
+ return None, return_value
418
+
419
+
420
+ def list_items(
421
+ self,
422
+ account_id
423
+ ) -> tuple [
424
+ Optional[Error],
425
+ list[Item],
426
+ ]:
427
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
428
+ echo("Gathering the account's items ")
429
+ thread, stopper = start_dot_printing()
430
+ _, error, items = ac.list_account_items(account_id)
431
+ stop_dot_printing(thread, stopper)
432
+ echo_newline(" done")
433
+
434
+ if error:
435
+ return error, []
436
+ if items == []:
437
+ return Error(code=0, message=str("Cannot find items")), []
438
+
439
+ return None, items
440
+
441
+ def list_campaigns(
442
+ self,
443
+ ad_account_id
444
+ ) -> tuple [
445
+ Optional[Error],
446
+ list[Campaign],
447
+ ]:
448
+ cam = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
449
+
450
+ echo("Gathering the account's campaigns ")
451
+ thread, stopper = start_dot_printing()
452
+ _, error, campaigns = cam.list_campaigns(ad_account_id)
453
+ stop_dot_printing(thread, stopper)
454
+ echo_newline(" done")
455
+
456
+ if error:
457
+ return error, []
458
+ if campaigns == []:
459
+ return Error(code=0, message=str("Cannot find campaigns")), []
460
+
461
+ return None, campaigns
462
+
463
+
464
+ def list_campaign_items(
465
+ self,
466
+ ad_account_id,
467
+ campaign_id
468
+ ) -> tuple [
469
+ Optional[Error],
470
+ list[Item]
471
+ ]:
472
+ cam = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
473
+
474
+ echo(f"Gathering the items of the campaign {campaign_id} ")
475
+ thread, stopper = start_dot_printing()
476
+ _, error, campaign_items = cam.list_campaign_items(ad_account_id, campaign_id)
477
+ stop_dot_printing(thread, stopper)
478
+ echo_newline(" done")
479
+
480
+ if error:
481
+ return error, []
482
+ if campaign_items == []:
483
+ return Error(code=0, message=str("Cannot find campaign items")), []
484
+
485
+ return None, campaign_items
@@ -21,6 +21,9 @@ import mcmcli.logging
21
21
  import sys
22
22
  import typer
23
23
 
24
+ AuthHeaderName = str
25
+ AuthHeaderValue = str
26
+
24
27
  app = typer.Typer(add_completion=False)
25
28
 
26
29
  #
@@ -79,4 +82,15 @@ class AuthCommand:
79
82
  return None, error, None
80
83
 
81
84
  return None, None, Token(**json_obj)
85
+
86
+ def get_auth_credential(self) -> tuple[Error, AuthHeaderName, AuthHeaderValue]:
87
+ if 'management_api_key' in self.config:
88
+ return None, "x-api-key", self.config['management_api_key']
89
+
90
+ _, error, token = self.get_token(to_curl=False)
91
+ if error:
92
+ return error, None, None
93
+
94
+ else:
95
+ return None, "Authorization", f"Bearer {token.token}"
82
96