mcm-cli 1.3.1__py3-none-any.whl → 1.4.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,32 @@
1
+ mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
+ mcmcli/__main__.py,sha256=wbiwdsy9Sq1Ebv93G6LlnH7wTb2SUT5gRxxFF9w5Hgc,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=05Ti5nMnuL_Ppo8hy2_bMGwfNZGdN6s528Gpi-EnpDg,16036
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=vUukEsZ9Lzvcqm4Pa8z7vzyt_qOpT_t6_YEXuLubVSQ,1731
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.1.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
27
+ mcm_cli-1.4.1.dist-info/METADATA,sha256=lwdpWx33PEgWxiCl4FBfbqM_CIflw5eLDOLtzCpjK1Y,3056
28
+ mcm_cli-1.4.1.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
29
+ mcm_cli-1.4.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
30
+ mcm_cli-1.4.1.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
31
+ mcm_cli-1.4.1.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
32
+ mcm_cli-1.4.1.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.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__":
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
@@ -11,24 +11,34 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
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
14
24
 
25
+ import csv
15
26
  import mcmcli.command.account
16
27
  import mcmcli.command.auth
28
+ import mcmcli.command.campaign
17
29
  import mcmcli.command.config
18
30
  import mcmcli.command.wallet
19
31
  import mcmcli.requests
20
32
  import sys
21
33
  import typer
22
34
 
35
+ from mcmcli.logging import echo, echo_newline, start_dot_printing, stop_dot_printing, print_error
36
+
23
37
  app = typer.Typer(add_completion=False)
24
38
 
25
39
  def _create_admin_command(profile):
26
- auth = mcmcli.command.auth.AuthCommand(profile)
27
- _, error, token = auth.get_token()
28
- if error:
29
- print(f"ERROR: {error.message}")
30
- return None
31
- return AdminCommand(profile, auth, token.token)
40
+ auth = AuthCommand(profile)
41
+ return AdminCommand(profile, auth)
32
42
 
33
43
  @app.command()
34
44
  def list_wallet_balances(
@@ -43,31 +53,319 @@ def list_wallet_balances(
43
53
  admin.list_wallet_balances()
44
54
 
45
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
+
46
261
  class AdminCommand:
47
262
  def __init__(
48
263
  self,
49
264
  profile,
50
- auth_command: mcmcli.command.auth.AuthCommand,
51
- token
265
+ auth_command: AuthCommand,
52
266
  ):
53
- self.profile = profile
54
- self.auth_command = auth_command
55
267
  self.config = mcmcli.command.config.get_config(profile)
56
- mcmcli.command.config.assert_config_exists(self.config)
268
+ if (self.config is None):
269
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
270
+ sys.exit()
57
271
 
58
- self.token = token
272
+ self.profile = profile
273
+ self.auth_command = auth_command
59
274
  self.api_base_url = f"{self.config['management_api_hostname']}/rmp/mgmt/v1/platforms/{self.config['platform_id']}"
60
275
  self.headers = {
61
276
  "accept": "application/json",
62
277
  "content-type": "application/json",
63
- "Authorization": f"Bearer {token}"
64
278
  }
65
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
+
66
364
  def list_wallet_balances(
67
365
  self
68
366
  ):
69
- ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command, self.token)
70
- wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command, self.token)
367
+ ac = mcmcli.command.account.AccountCommand(self.profile, self.auth_command)
368
+ wc = mcmcli.command.wallet.WalletCommand(self.profile, self.auth_command)
71
369
  _, error, accounts = ac.list_accounts()
72
370
  if error:
73
371
  print(error, file=sys.stderr, flush=True)
@@ -84,5 +382,105 @@ class AdminCommand:
84
382
  prepaid = w1 if w1.type == 'PRE_PAID' else w0
85
383
  credits = int(credits.balance.amount_micro) / 1000000
86
384
  prepaid = int(prepaid.balance.amount_micro) / 1000000
385
+
87
386
  print(f'"{accounts[id].title}", {id}, {credits}, {prepaid}')
88
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
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