mcm-cli 1.3.1__tar.gz → 1.4.1__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.3.1/mcm_cli.egg-info → mcm_cli-1.4.1}/PKG-INFO +3 -2
  2. {mcm_cli-1.3.1 → mcm_cli-1.4.1/mcm_cli.egg-info}/PKG-INFO +3 -2
  3. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcm_cli.egg-info/SOURCES.txt +7 -0
  4. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcm_cli.egg-info/requires.txt +1 -0
  5. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/__main__.py +5 -1
  6. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/command/account.py +10 -12
  7. mcm_cli-1.4.1/mcmcli/command/admin.py +486 -0
  8. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/command/auth.py +14 -0
  9. mcm_cli-1.4.1/mcmcli/command/campaign.py +317 -0
  10. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/command/config.py +2 -0
  11. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/command/decision.py +4 -0
  12. mcm_cli-1.4.1/mcmcli/command/report.py +150 -0
  13. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/command/wallet.py +37 -36
  14. mcm_cli-1.4.1/mcmcli/data/campaign.py +69 -0
  15. mcm_cli-1.4.1/mcmcli/data/item_blocking_result.py +54 -0
  16. mcm_cli-1.4.1/mcmcli/data/platform_user.py +64 -0
  17. mcm_cli-1.4.1/mcmcli/data/report.py +110 -0
  18. mcm_cli-1.4.1/mcmcli/data/user_join_request.py +44 -0
  19. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/setup.py +3 -3
  20. mcm_cli-1.3.1/mcmcli/command/admin.py +0 -88
  21. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/LICENSE +0 -0
  22. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/NOTICE +0 -0
  23. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/README.md +0 -0
  24. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcm_cli.egg-info/dependency_links.txt +0 -0
  25. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcm_cli.egg-info/entry_points.txt +0 -0
  26. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcm_cli.egg-info/top_level.txt +0 -0
  27. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/__init__.py +0 -0
  28. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/account.py +0 -0
  29. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/account_user.py +0 -0
  30. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/decision.py +0 -0
  31. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/error.py +0 -0
  32. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/item.py +0 -0
  33. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/seller.py +0 -0
  34. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/token.py +0 -0
  35. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/data/wallet.py +0 -0
  36. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/logging.py +0 -0
  37. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/mcmcli/requests.py +0 -0
  38. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/setup.cfg +0 -0
  39. {mcm_cli-1.3.1 → mcm_cli-1.4.1}/tests/test_config.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 1.3.1
3
+ Version: 1.4.1
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.3.1
3
+ Version: 1.4.1
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,15 +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
29
+ mcmcli/data/item_blocking_result.py
30
+ mcmcli/data/platform_user.py
31
+ mcmcli/data/report.py
26
32
  mcmcli/data/seller.py
27
33
  mcmcli/data/token.py
34
+ mcmcli/data/user_join_request.py
28
35
  mcmcli/data/wallet.py
29
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.3.1")
40
+ typer.echo(f"Version: mcm-cli v1.4.1")
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,486 @@
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 IDAccount Title,Campaign ID,Campaign Title,Ad Type,Start,End,Budget Period,Budget Amount,Enabling State,State,Created At,Updated At,Item IDs")
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
+
83
+ @app.command()
84
+ def list_platform_users(
85
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
86
+ to_json: bool = typer.Option(False, help="Print raw output in json"),
87
+ profile: str = "default",
88
+ ):
89
+ """
90
+ List the users of the platform
91
+ """
92
+ admin = _create_admin_command(profile)
93
+ if admin is None:
94
+ return
95
+
96
+ curl, error, users = admin.list_platform_users(to_curl)
97
+ if to_curl:
98
+ print(curl)
99
+ return
100
+ if error:
101
+ print(error, file=sys.stderr, flush=True)
102
+ return
103
+ if to_json:
104
+ json_dumps = [x.model_dump_json() for x in users]
105
+ print(f"[{','.join(json_dumps)}]")
106
+ return
107
+
108
+ print('User ID,Created At,Updated At,Status,Email,Name,Roles')
109
+ for u in users:
110
+ roles = [f'{x.name} of {x.resource_type} {x.resource_id}' for x in u.roles]
111
+ print(f'{u.id},{u.created_at},{u.updated_at},{u.status},{u.email},{u.name},{';'.join(roles)}')
112
+
113
+
114
+ @app.command()
115
+ def get_platform_user(
116
+ user_email: str = typer.Option(help="User's email address"),
117
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
118
+ to_json: bool = typer.Option(False, help="Print raw output in json"),
119
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
120
+ ):
121
+ """
122
+ Get the email user's profile.
123
+ """
124
+ a = _create_admin_command(profile)
125
+ if a is None:
126
+ return
127
+
128
+ curl, error, user = a.get_platform_user(user_email, to_curl)
129
+ if to_curl:
130
+ print(curl)
131
+ return
132
+ if error:
133
+ print(f"ERROR: {error.message}")
134
+ return
135
+
136
+ if user is not None:
137
+ print(f"{user.model_dump_json()}")
138
+ return
139
+
140
+
141
+ @app.command()
142
+ def list_items(
143
+ account_id: str = typer.Option(help="Ad account ID"),
144
+ profile: str = "default",
145
+ ):
146
+ """
147
+ List the itmes, item status, and attached campaigns of a given ad account
148
+ """
149
+ admin = _create_admin_command(profile)
150
+ if admin is None:
151
+ return
152
+
153
+ error, campaigns = admin.list_campaigns(account_id)
154
+ if error:
155
+ print(f"ERROR: {error.message}")
156
+ return
157
+
158
+ campaign_item_obj = {} # build item list into item object with the item_id as an index
159
+ # print("Campaign ID, Campaign Title, Item ID, Item Title, Item Status")
160
+
161
+ for c in campaigns:
162
+ error, campaign_items = admin.list_campaign_items(account_id, c.id)
163
+ if error:
164
+ print(f"ERROR: {error.message}")
165
+ return
166
+ for ci in campaign_items:
167
+ if ci.id in campaign_item_obj:
168
+ campaign_item_obj[ci.id].append({
169
+ 'campaign_id': c.id,
170
+ 'campaign_title': c.title
171
+ })
172
+ else:
173
+ campaign_item_obj[ci.id] = [{
174
+ 'campaign_id': c.id,
175
+ 'campaign_title': c.title
176
+ }]
177
+ # print(f"{c['id']}, {c['title']}, {ci['id']}, {ci['title']}, {ci['is_active']}")
178
+
179
+ error, items = admin.list_items(account_id)
180
+ if error:
181
+ print(f"ERROR: {error.message}")
182
+
183
+ print("Ad Account ID,Item ID,Is Item Active,Item Title,Campaign ID,Campaign Title")
184
+ for i in items:
185
+ if i.id in campaign_item_obj:
186
+ ci = campaign_item_obj[i.id]
187
+ campaign_id_list = ""
188
+ campaign_title_list = ""
189
+ for c in ci:
190
+ campaign_id_list += f"{c['campaign_id']};"
191
+ campaign_title_list += f"{c['campaign_title']};"
192
+ campaign_id_list = campaign_id_list[:-1]
193
+ campaign_title_list = campaign_title_list[:-1]
194
+
195
+ print(f"{account_id},{i.id},{i.is_active},\"{i.title}\",{campaign_id_list},\"{campaign_title_list}\"")
196
+ else:
197
+ print(f"{account_id},{i.id},{i.is_active},\"{i.title}\",,")
198
+
199
+ @app.command()
200
+ def list_off_campaign_items(
201
+ account_id: str = typer.Option(help="Ad account ID"),
202
+ profile: str = "default",
203
+ ):
204
+ """
205
+ Lists the items that are not in any of campaigns
206
+ """
207
+ admin = _create_admin_command(profile)
208
+ if admin is None:
209
+ return
210
+
211
+ error, items = admin.list_items(account_id)
212
+ if error:
213
+ print(f"ERROR: {error.message}")
214
+ return
215
+ error, campaigns = admin.list_campaigns(account_id)
216
+ if error:
217
+ print(f"ERROR: {error.message}")
218
+ return
219
+
220
+ campaign_item_obj = {} # build item list into item object with the item_id as an index
221
+ for c in campaigns:
222
+ error, campaign_items = admin.list_campaign_items(account_id, c.id)
223
+ for ci in campaign_items:
224
+ campaign_item_obj[ci.id] = ci.title
225
+
226
+ print("Item ID, Item Title")
227
+ for i in items:
228
+ if i.id not in campaign_item_obj and i.is_active:
229
+ print(f"{i.id}, {i.title}")
230
+
231
+ @app.command()
232
+ def block_item(
233
+ item_id: str = typer.Option(help="Item ID"),
234
+ 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."),
235
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
236
+ profile: str = typer.Option("default", help="Profile name of the MCM CLI."),
237
+ ):
238
+ """
239
+ Item Kill Switch Command.
240
+ This API immediately blocks an item or an ad account item from appearing in ads by marking it as “blocked.”
241
+ """
242
+ timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
243
+
244
+ # print(f"invoked block_item(item_id={item_id}, account_id={account_id}, blocked='Requested at {timestamp}')");
245
+ admin = _create_admin_command(profile)
246
+ if admin is None:
247
+ return
248
+
249
+ curl, error, result = admin.block_item(item_id=item_id, account_id=account_id, to_curl=to_curl)
250
+ if curl:
251
+ print(curl)
252
+ return
253
+ if error:
254
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
255
+ return
256
+
257
+ print(result.model_dump_json())
258
+ return
259
+
260
+
261
+ class AdminCommand:
262
+ def __init__(
263
+ self,
264
+ profile,
265
+ auth_command: AuthCommand,
266
+ ):
267
+ self.config = mcmcli.command.config.get_config(profile)
268
+ if (self.config is None):
269
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
270
+ sys.exit()
271
+
272
+ self.profile = profile
273
+ self.auth_command = auth_command
274
+ self.api_base_url = f"{self.config['management_api_hostname']}/rmp/mgmt/v1/platforms/{self.config['platform_id']}"
275
+ self.headers = {
276
+ "accept": "application/json",
277
+ "content-type": "application/json",
278
+ }
279
+
280
+ self.refresh_token()
281
+
282
+
283
+ def refresh_token(
284
+ self,
285
+ ) -> None:
286
+ error, auth_header_name, auth_header_value = self.auth_command.get_auth_credential()
287
+ if error:
288
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
289
+ sys.exit()
290
+
291
+ self.headers[auth_header_name] = auth_header_value
292
+
293
+
294
+ def block_item(
295
+ self,
296
+ item_id,
297
+ account_id,
298
+ to_curl,
299
+ ) -> tuple[
300
+ Optional[CurlString],
301
+ Optional[Error],
302
+ Optional[ItemBlockingResult],
303
+ ]:
304
+ _api_url = f"{self.api_base_url}/item-status-bulk"
305
+ _requested_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
306
+ _payload = {
307
+ "items": [{
308
+ "item_id": item_id,
309
+ "updated_time": _requested_at,
310
+ "blocked": f'Requested at {_requested_at}',
311
+ }]
312
+ }
313
+ if account_id is not None:
314
+ _payload["items"][0]["seller_id"] = account_id
315
+
316
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
317
+ if curl:
318
+ return curl, None, None
319
+ if error:
320
+ return None, error, None
321
+ return None, None, ItemBlockingResult(**json_obj)
322
+
323
+
324
+ def get_platform_user(
325
+ self,
326
+ user_email,
327
+ to_curl = False,
328
+ ) -> tuple[
329
+ Optional[CurlString],
330
+ Optional[Error],
331
+ Optional[PlatformUser],
332
+ ]:
333
+ _api_url = f"{self.api_base_url}/users/{user_email}"
334
+ curl, error, json_obj = api_request('GET', to_curl, _api_url, self.headers)
335
+ if curl:
336
+ return curl, None, None
337
+ if error:
338
+ return None, error, None
339
+
340
+ ret = PlatformUserWrapper(**json_obj).user
341
+ return None, None, ret
342
+
343
+
344
+ def list_platform_users(
345
+ self,
346
+ to_curl=False,
347
+ ) -> tuple [
348
+ Optional[CurlString],
349
+ Optional[Error],
350
+ list[PlatformUser],
351
+ ]:
352
+ _api_url = f"{self.api_base_url}/users"
353
+
354
+ curl, error, json_obj = api_request('GET', to_curl, _api_url, self.headers)
355
+ if curl:
356
+ return curl, None, []
357
+ if error:
358
+ return None, error, []
359
+
360
+ user_list_wrapper = PlatformUserListWrapper(**json_obj)
361
+ users = user_list_wrapper.users
362
+ return None, None, users
363
+
364
+ def list_wallet_balances(
365
+ self
366
+ ):
367
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
368
+ wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command)
369
+ _, error, accounts = ac.list_accounts()
370
+ if error:
371
+ print(error, file=sys.stderr, flush=True)
372
+ return
373
+
374
+ print("ad_account_title, ad_account_id, credit_balance, prepaid_balance")
375
+ for id in accounts:
376
+ _, error, wallet = wc.get_balance(id, to_curl=False)
377
+ if error:
378
+ continue
379
+ w0 = wallet.accounts[0]
380
+ w1 = wallet.accounts[1]
381
+ credits = w0 if w0.type == 'CREDITS' else w1
382
+ prepaid = w1 if w1.type == 'PRE_PAID' else w0
383
+ credits = int(credits.balance.amount_micro) / 1000000
384
+ prepaid = int(prepaid.balance.amount_micro) / 1000000
385
+
386
+ print(f'"{accounts[id].title}", {id}, {credits}, {prepaid}')
387
+
388
+
389
+
390
+ def list_all_campaigns(
391
+ self
392
+ ) -> tuple [
393
+ Optional[Error],
394
+ list[tuple[Account, Campaign]]
395
+ ]:
396
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
397
+ cc = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
398
+ _, error, accounts = ac.list_accounts()
399
+ if error:
400
+ return error, []
401
+
402
+ echo('Collecting campaigns...')
403
+ return_value = []
404
+ for id in accounts:
405
+ account = accounts[id]
406
+ echo('.')
407
+ if account.state_info.state == "INACTIVE":
408
+ continue
409
+ # print(f'{account.id}, \"{account.title}\"')
410
+ _, error, campaigns = cc.list_campaigns(account.id)
411
+ if error:
412
+ echo_newline(error)
413
+ continue
414
+ for c in campaigns:
415
+ return_value.append((account, c))
416
+
417
+ echo_newline(' done')
418
+ return None, return_value
419
+
420
+
421
+ def list_items(
422
+ self,
423
+ account_id
424
+ ) -> tuple [
425
+ Optional[Error],
426
+ list[Item],
427
+ ]:
428
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
429
+ echo("Gathering the account's items ")
430
+ thread, stopper = start_dot_printing()
431
+ _, error, items = ac.list_account_items(account_id)
432
+ stop_dot_printing(thread, stopper)
433
+ echo_newline(" done")
434
+
435
+ if error:
436
+ return error, []
437
+ if items == []:
438
+ return Error(code=0, message=str("Cannot find items")), []
439
+
440
+ return None, items
441
+
442
+ def list_campaigns(
443
+ self,
444
+ ad_account_id
445
+ ) -> tuple [
446
+ Optional[Error],
447
+ list[Campaign],
448
+ ]:
449
+ cam = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
450
+
451
+ echo("Gathering the account's campaigns ")
452
+ thread, stopper = start_dot_printing()
453
+ _, error, campaigns = cam.list_campaigns(ad_account_id)
454
+ stop_dot_printing(thread, stopper)
455
+ echo_newline(" done")
456
+
457
+ if error:
458
+ return error, []
459
+ if campaigns == []:
460
+ return Error(code=0, message=str("Cannot find campaigns")), []
461
+
462
+ return None, campaigns
463
+
464
+
465
+ def list_campaign_items(
466
+ self,
467
+ ad_account_id,
468
+ campaign_id
469
+ ) -> tuple [
470
+ Optional[Error],
471
+ list[Item]
472
+ ]:
473
+ cam = mcmcli.command.campaign.CampaignCommand(self.profile, self.auth_command)
474
+
475
+ echo(f"Gathering the items of the campaign {campaign_id} ")
476
+ thread, stopper = start_dot_printing()
477
+ _, error, campaign_items = cam.list_campaign_items(ad_account_id, campaign_id)
478
+ stop_dot_printing(thread, stopper)
479
+ echo_newline(" done")
480
+
481
+ if error:
482
+ return error, []
483
+ if campaign_items == []:
484
+ return Error(code=0, message=str("Cannot find campaign items")), []
485
+
486
+ 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