mcm-cli 1.3.0__py3-none-any.whl → 1.4.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 1.3.0
3
+ Version: 1.4.0
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
@@ -1,9 +1,9 @@
1
1
  mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
- mcmcli/__main__.py,sha256=RgIMoKZR3IQ-lFOyXQqEEQYuJPgTxU0KpLdqIu0pUKA,1616
2
+ mcmcli/__main__.py,sha256=eoXcJ-YvL7VS-qI3tzWEm2Zz9Mc3aKMC2WRgr8nxDAY,1616
3
3
  mcmcli/logging.py,sha256=xjRS5ey1ONx_d34qB1Fetb_SwPysoh2hzNDuNAaYYWQ,1739
4
- mcmcli/requests.py,sha256=ZoQULPpKdAvGtki25Jr6K2Xq2v213XLinzPHUeBi9wo,2601
4
+ mcmcli/requests.py,sha256=IuySBQ8P_GoGF3f_TRysfgQNOhi2n9M84WK_eRXnoEU,2945
5
5
  mcmcli/command/account.py,sha256=kxJbBKYrw6OyCdaNZ0K0BEvKgZEAjj5azO-lNSeYLTM,25107
6
- mcmcli/command/admin.py,sha256=nJ7rm0nm0jHPobg0PjNHIWaWURTQu6QEUEUKIo__GO0,3010
6
+ mcmcli/command/admin.py,sha256=mDngighzQZPWlq0hOex2qzW8GNcZmvzVOTWre5RTJRE,5520
7
7
  mcmcli/command/auth.py,sha256=QLdr_XFW5BVw9r4a7Kjj5BTBXpSux3AWI9eI03S8aiA,2480
8
8
  mcmcli/command/config.py,sha256=sdzge-l_Yi2P_TlTgSLqShcGyPCzpW3QJzctpIvc-g4,4195
9
9
  mcmcli/command/decision.py,sha256=Zjbmt71OVU-oL8Itt9O-SvwT9Lbxw-PAgRZaIgiXi-E,8411
@@ -13,13 +13,14 @@ mcmcli/data/account_user.py,sha256=27nQp52nMma5a3QszSJGqgq5Z0ivIb-nMZcZuhEgbEg,1
13
13
  mcmcli/data/decision.py,sha256=bQ5j_PbPRSFa0sY5g9UVqdNzl_2epchcz1lHoDVuV90,2880
14
14
  mcmcli/data/error.py,sha256=d6xa_jTXumlA0EzXy2PJQ86ajBb0Pm90fss9R3LuHUc,1094
15
15
  mcmcli/data/item.py,sha256=Z2xTRhU8T4vyJADO0l6-XPyQXvb9DX_OAjExhSXpW2A,1091
16
+ mcmcli/data/item_blocking_result.py,sha256=daK4c8--aCe2vRsnTzLBjgKQo0C-zcDH09dC0GgrN7E,1429
16
17
  mcmcli/data/seller.py,sha256=40SA7QekM3a3svDrDYLo_QYJ68VUxDO0KeGejJMp4k4,1004
17
18
  mcmcli/data/token.py,sha256=11wtyLHCAZHu0LVbNDPa-yipcL6lenxoYIKEI58VzFs,1744
18
19
  mcmcli/data/wallet.py,sha256=eMUi8N0vJdg_E10TPhSPoZkZtmIG7gHyqgabQ8C5Lg8,3217
19
- mcm_cli-1.3.0.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
20
- mcm_cli-1.3.0.dist-info/METADATA,sha256=sTYKOH3XncIeSNsjAER9fFM2nKdrST922dpGg7khsGs,3018
21
- mcm_cli-1.3.0.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
22
- mcm_cli-1.3.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
23
- mcm_cli-1.3.0.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
24
- mcm_cli-1.3.0.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
25
- mcm_cli-1.3.0.dist-info/RECORD,,
20
+ mcm_cli-1.4.0.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
21
+ mcm_cli-1.4.0.dist-info/METADATA,sha256=qRht0jD47cuF5FQN1JwCZPlVWsIWNrsMl_sdY4VF7Rs,3018
22
+ mcm_cli-1.4.0.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
23
+ mcm_cli-1.4.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
24
+ mcm_cli-1.4.0.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
25
+ mcm_cli-1.4.0.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
26
+ mcm_cli-1.4.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
mcmcli/__main__.py CHANGED
@@ -35,7 +35,7 @@ def version():
35
35
  """
36
36
  Show the tool version
37
37
  """
38
- typer.echo(f"Version: mcm-cli v1.3.0")
38
+ typer.echo(f"Version: mcm-cli v1.4.0")
39
39
 
40
40
  app.add_typer(mcmcli.command.account.app, name="account", help="Ad account management")
41
41
  app.add_typer(mcmcli.command.admin.app, name="admin", help="Platform administration")
mcmcli/command/admin.py CHANGED
@@ -11,6 +11,11 @@
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.data.error import Error
16
+ from mcmcli.data.item_blocking_result import ItemBlockingResult
17
+ from mcmcli.requests import CurlString, api_request
18
+ from typing import Optional
14
19
 
15
20
  import mcmcli.command.account
16
21
  import mcmcli.command.auth
@@ -43,6 +48,35 @@ def list_wallet_balances(
43
48
  admin.list_wallet_balances()
44
49
 
45
50
 
51
+ @app.command()
52
+ def block_item(
53
+ item_id: str = typer.Option(help="Item ID"),
54
+ 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."),
55
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
56
+ profile: str = typer.Option("default", help="Profile name of the MCM CLI."),
57
+ ):
58
+ """
59
+ Item Kill Switch Command.
60
+ This API immediately blocks an item or an ad account item from appearing in ads by marking it as “blocked.”
61
+ """
62
+ timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
63
+
64
+ # print(f"invoked block_item(item_id={item_id}, account_id={account_id}, blocked='Requested at {timestamp}')");
65
+ admin = _create_admin_command(profile)
66
+ if admin is None:
67
+ return
68
+
69
+ curl, error, result = admin.block_item(item_id=item_id, account_id=account_id, to_curl=to_curl)
70
+ if curl:
71
+ print(curl)
72
+ return
73
+ if error:
74
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
75
+ return
76
+
77
+ print(result.model_dump_json())
78
+ return
79
+
46
80
  class AdminCommand:
47
81
  def __init__(
48
82
  self,
@@ -63,6 +97,36 @@ class AdminCommand:
63
97
  "Authorization": f"Bearer {token}"
64
98
  }
65
99
 
100
+ def block_item(
101
+ self,
102
+ item_id,
103
+ account_id,
104
+ to_curl,
105
+ ) -> tuple[
106
+ Optional[CurlString],
107
+ Optional[Error],
108
+ Optional[ItemBlockingResult],
109
+ ]:
110
+ _api_url = f"{self.api_base_url}/item-status-bulk"
111
+ _requested_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
112
+ _payload = {
113
+ "items": [{
114
+ "item_id": item_id,
115
+ "seller_id": account_id,
116
+ "updated_time": _requested_at,
117
+ "blocked": f'Requested at {_requested_at}',
118
+ }]
119
+ }
120
+ if account_id is None:
121
+ del _payload["items"][0]["seller_id"]
122
+
123
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
124
+ if curl:
125
+ return curl, None, None
126
+ if error:
127
+ return None, error, None
128
+ return None, None, ItemBlockingResult(**json_obj)
129
+
66
130
  def list_wallet_balances(
67
131
  self
68
132
  ):
@@ -0,0 +1,54 @@
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
+
15
+ from pydantic import BaseModel
16
+ from typing import Optional
17
+
18
+ #
19
+ # API response dataclasse of the item kill switch API
20
+ #
21
+ # The example response is as below:
22
+ # {
23
+ # "success_items": [
24
+ # {
25
+ # "item_id": "item_1",
26
+ # "seller_id": "seller_1",
27
+ # "region": ""
28
+ # }
29
+ # ],
30
+ # "failure_items": [
31
+ # {
32
+ # "item_id": "item_2",
33
+ # "seller_id": "seller_2",
34
+ # "region": "",
35
+ # "message": "rpc error: code = Internal desc = ad account not found"
36
+ # }
37
+ # ]
38
+ # }
39
+
40
+ class SucceededItemBlockingAttempt(BaseModel):
41
+ item_id: str
42
+ seller_id: str
43
+ region: str
44
+
45
+ class FailedItemBlockingAttempt(BaseModel):
46
+ item_id: str
47
+ seller_id: str
48
+ region: str
49
+ message: str
50
+
51
+
52
+ class ItemBlockingResult(BaseModel):
53
+ success_items: list[SucceededItemBlockingAttempt]
54
+ failure_items: list[FailedItemBlockingAttempt]
mcmcli/requests.py CHANGED
@@ -38,6 +38,8 @@ def get(url, headers):
38
38
  try:
39
39
  res = requests.get(url, headers=headers)
40
40
  return None, json.loads(res.text)
41
+ except json.JSONDecodeError:
42
+ return Error(code=0, message=res.text), None
41
43
  except Exception as e:
42
44
  return e, None
43
45
 
@@ -46,6 +48,8 @@ def delete(url, headers):
46
48
  try:
47
49
  res = requests.delete(url, headers=headers)
48
50
  return None, json.loads(res.text)
51
+ except json.JSONDecodeError:
52
+ return Error(code=0, message=res.text), None
49
53
  except Exception as e:
50
54
  return e, None
51
55
 
@@ -54,6 +58,8 @@ def post(url, headers, payload):
54
58
  try:
55
59
  res = requests.post(url, headers=headers, json=payload)
56
60
  return None, json.loads(res.text)
61
+ except json.JSONDecodeError:
62
+ return Error(code=0, message=res.text), None
57
63
  except Exception as e:
58
64
  return e, None
59
65
 
@@ -61,6 +67,8 @@ def put(url, headers, payload):
61
67
  try:
62
68
  res = requests.put(url, headers=headers, json=payload)
63
69
  return None, json.loads(res.text)
70
+ except json.JSONDecodeError:
71
+ return Error(code=0, message=res.text), None
64
72
  except Exception as e:
65
73
  return e, None
66
74