mcm-cli 1.4.0__tar.gz → 1.4.1__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {mcm_cli-1.4.0/mcm_cli.egg-info → mcm_cli-1.4.1}/PKG-INFO +3 -2
- {mcm_cli-1.4.0 → mcm_cli-1.4.1/mcm_cli.egg-info}/PKG-INFO +3 -2
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcm_cli.egg-info/SOURCES.txt +6 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcm_cli.egg-info/requires.txt +1 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/__main__.py +5 -1
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/command/account.py +10 -12
- mcm_cli-1.4.1/mcmcli/command/admin.py +486 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/command/auth.py +14 -0
- mcm_cli-1.4.1/mcmcli/command/campaign.py +317 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/command/config.py +2 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/command/decision.py +4 -0
- mcm_cli-1.4.1/mcmcli/command/report.py +150 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/command/wallet.py +37 -36
- mcm_cli-1.4.1/mcmcli/data/campaign.py +69 -0
- mcm_cli-1.4.1/mcmcli/data/platform_user.py +64 -0
- mcm_cli-1.4.1/mcmcli/data/report.py +110 -0
- mcm_cli-1.4.1/mcmcli/data/user_join_request.py +44 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/setup.py +3 -3
- mcm_cli-1.4.0/mcmcli/command/admin.py +0 -152
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/LICENSE +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/NOTICE +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/README.md +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcm_cli.egg-info/dependency_links.txt +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcm_cli.egg-info/entry_points.txt +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcm_cli.egg-info/top_level.txt +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/__init__.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/account.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/account_user.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/decision.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/error.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/item.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/item_blocking_result.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/seller.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/token.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/data/wallet.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/logging.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/mcmcli/requests.py +0 -0
- {mcm_cli-1.4.0 → mcm_cli-1.4.1}/setup.cfg +0 -0
- {mcm_cli-1.4.0 → 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.4.
|
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 ::
|
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.
|
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 ::
|
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
|
@@ -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.
|
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:
|
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
|
-
|
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
|
-
|
376
|
-
|
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 =
|
764
|
-
|
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
|
|