mcm-cli 1.4.0__py3-none-any.whl → 1.4.2__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,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
@@ -0,0 +1,32 @@
1
+ mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
+ mcmcli/__main__.py,sha256=8rr--sz02pkM_Z6u90JYm8f1s6sbHmgXF82xfIF6rqg,1844
3
+ mcmcli/logging.py,sha256=xjRS5ey1ONx_d34qB1Fetb_SwPysoh2hzNDuNAaYYWQ,1739
4
+ mcmcli/requests.py,sha256=IuySBQ8P_GoGF3f_TRysfgQNOhi2n9M84WK_eRXnoEU,2945
5
+ mcmcli/command/account.py,sha256=FWXmzOLj4rVLVLEv-w0eDVlQVrkONvR1UewZbcTDgE4,24994
6
+ mcmcli/command/admin.py,sha256=2GC0B3oqQsT5N5mHUyezgZvzPkB4-oGtddguOWfdLJs,15980
7
+ mcmcli/command/auth.py,sha256=Ak7ZNEskWPpMoeTJcbYlEpDBgzxn8N33Q2dNf67SsCs,2926
8
+ mcmcli/command/campaign.py,sha256=SU6TrDy6YhqM0JWPzbYWLpXZrz7Ymw31pd168dK2LZk,10638
9
+ mcmcli/command/config.py,sha256=08C5ftAvdvpQ26Z329LqhP8AxTI629LS7Ou6glzrRgw,4396
10
+ mcmcli/command/decision.py,sha256=iLvEDEa2k0LAgoXrOvcdn-HcFRo90E8Dxht_Ls9EGCY,8690
11
+ mcmcli/command/report.py,sha256=N8IMyDZ5QpY11-KkZG-n5_ZzEeh-ME5s2s3wrAKEGjY,5387
12
+ mcmcli/command/wallet.py,sha256=vG2rg7tPwGsV9YB7cpvkiocbqtB1reqXn7dmolFmzD4,11536
13
+ mcmcli/data/account.py,sha256=pe7tPapP6vlUD5D5L5Nh5k2bkWdYOK01Mpt5fBYFnJs,1782
14
+ mcmcli/data/account_user.py,sha256=27nQp52nMma5a3QszSJGqgq5Z0ivIb-nMZcZuhEgbEg,1328
15
+ mcmcli/data/campaign.py,sha256=-HR-SbEfe44Ys_UXnYr9HRG0TPcGkO2k61iEsz9uET0,2284
16
+ mcmcli/data/decision.py,sha256=bQ5j_PbPRSFa0sY5g9UVqdNzl_2epchcz1lHoDVuV90,2880
17
+ mcmcli/data/error.py,sha256=d6xa_jTXumlA0EzXy2PJQ86ajBb0Pm90fss9R3LuHUc,1094
18
+ mcmcli/data/item.py,sha256=Z2xTRhU8T4vyJADO0l6-XPyQXvb9DX_OAjExhSXpW2A,1091
19
+ mcmcli/data/item_blocking_result.py,sha256=daK4c8--aCe2vRsnTzLBjgKQo0C-zcDH09dC0GgrN7E,1429
20
+ mcmcli/data/platform_user.py,sha256=AC4ps5DdC8XdDpfBMKtVCne2UtnOKtQbVY8D-bLbcIY,1638
21
+ mcmcli/data/report.py,sha256=lJAKQUsaU6g_Kqbx3KpYtBPZ5RSWAJfAmVXxw3KHaUE,2933
22
+ mcmcli/data/seller.py,sha256=40SA7QekM3a3svDrDYLo_QYJ68VUxDO0KeGejJMp4k4,1004
23
+ mcmcli/data/token.py,sha256=11wtyLHCAZHu0LVbNDPa-yipcL6lenxoYIKEI58VzFs,1744
24
+ mcmcli/data/user_join_request.py,sha256=lXMO2hE_VpRg0JofVrYAVM82S-RLFkPrZk8-drvhoDI,1251
25
+ mcmcli/data/wallet.py,sha256=eMUi8N0vJdg_E10TPhSPoZkZtmIG7gHyqgabQ8C5Lg8,3217
26
+ mcm_cli-1.4.2.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
27
+ mcm_cli-1.4.2.dist-info/METADATA,sha256=6Y7vHnYzLC2-4-7AHMDyL2mprCj0BKVMCUAP8sOGob8,3056
28
+ mcm_cli-1.4.2.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
29
+ mcm_cli-1.4.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
30
+ mcm_cli-1.4.2.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
31
+ mcm_cli-1.4.2.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
32
+ mcm_cli-1.4.2.dist-info/RECORD,,
mcmcli/__main__.py CHANGED
@@ -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__":
mcmcli/command/account.py CHANGED
@@ -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(
mcmcli/command/admin.py CHANGED
@@ -12,28 +12,33 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  from datetime import datetime, timedelta, timezone
15
- from mcmcli.data.error import Error
15
+ from mcmcli.command.auth import AuthCommand, AuthHeaderName, AuthHeaderValue
16
+ from mcmcli.data.account import Account
16
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
17
22
  from mcmcli.requests import CurlString, api_request
18
23
  from typing import Optional
19
24
 
25
+ import csv
20
26
  import mcmcli.command.account
21
27
  import mcmcli.command.auth
28
+ import mcmcli.command.campaign
22
29
  import mcmcli.command.config
23
30
  import mcmcli.command.wallet
24
31
  import mcmcli.requests
25
32
  import sys
26
33
  import typer
27
34
 
35
+ from mcmcli.logging import echo, echo_newline, start_dot_printing, stop_dot_printing, print_error
36
+
28
37
  app = typer.Typer(add_completion=False)
29
38
 
30
39
  def _create_admin_command(profile):
31
- auth = mcmcli.command.auth.AuthCommand(profile)
32
- _, error, token = auth.get_token()
33
- if error:
34
- print(f"ERROR: {error.message}")
35
- return None
36
- return AdminCommand(profile, auth, token.token)
40
+ auth = AuthCommand(profile)
41
+ return AdminCommand(profile, auth)
37
42
 
38
43
  @app.command()
39
44
  def list_wallet_balances(
@@ -48,6 +53,182 @@ def list_wallet_balances(
48
53
  admin.list_wallet_balances()
49
54
 
50
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
+
51
232
  @app.command()
52
233
  def block_item(
53
234
  item_id: str = typer.Option(help="Item ID"),
@@ -77,26 +258,40 @@ def block_item(
77
258
  print(result.model_dump_json())
78
259
  return
79
260
 
261
+
80
262
  class AdminCommand:
81
263
  def __init__(
82
264
  self,
83
265
  profile,
84
- auth_command: mcmcli.command.auth.AuthCommand,
85
- token
266
+ auth_command: AuthCommand,
86
267
  ):
87
- self.profile = profile
88
- self.auth_command = auth_command
89
268
  self.config = mcmcli.command.config.get_config(profile)
90
- mcmcli.command.config.assert_config_exists(self.config)
269
+ if (self.config is None):
270
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
271
+ sys.exit()
91
272
 
92
- self.token = token
273
+ self.profile = profile
274
+ self.auth_command = auth_command
93
275
  self.api_base_url = f"{self.config['management_api_hostname']}/rmp/mgmt/v1/platforms/{self.config['platform_id']}"
94
276
  self.headers = {
95
277
  "accept": "application/json",
96
278
  "content-type": "application/json",
97
- "Authorization": f"Bearer {token}"
98
279
  }
99
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
+
100
295
  def block_item(
101
296
  self,
102
297
  item_id,
@@ -112,13 +307,12 @@ class AdminCommand:
112
307
  _payload = {
113
308
  "items": [{
114
309
  "item_id": item_id,
115
- "seller_id": account_id,
116
310
  "updated_time": _requested_at,
117
311
  "blocked": f'Requested at {_requested_at}',
118
312
  }]
119
313
  }
120
- if account_id is None:
121
- del _payload["items"][0]["seller_id"]
314
+ if account_id is not None:
315
+ _payload["items"][0]["seller_id"] = account_id
122
316
 
123
317
  curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
124
318
  if curl:
@@ -127,11 +321,52 @@ class AdminCommand:
127
321
  return None, error, None
128
322
  return None, None, ItemBlockingResult(**json_obj)
129
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
+
130
365
  def list_wallet_balances(
131
366
  self
132
367
  ):
133
- ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command, self.token)
134
- wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command, self.token)
368
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
369
+ wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command)
135
370
  _, error, accounts = ac.list_accounts()
136
371
  if error:
137
372
  print(error, file=sys.stderr, flush=True)
@@ -148,5 +383,103 @@ class AdminCommand:
148
383
  prepaid = w1 if w1.type == 'PRE_PAID' else w0
149
384
  credits = int(credits.balance.amount_micro) / 1000000
150
385
  prepaid = int(prepaid.balance.amount_micro) / 1000000
386
+
151
387
  print(f'"{accounts[id].title}", {id}, {credits}, {prepaid}')
152
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
mcmcli/command/auth.py CHANGED
@@ -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