mcm-cli 1.2.0__py3-none-any.whl → 1.3.1__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.2.0
3
+ Version: 1.3.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
@@ -1,13 +1,13 @@
1
1
  mcmcli/__init__.py,sha256=-U6lMZ9_99IXAKwnqnYXYr6NcO6TSmG-kxewgvJjU4k,575
2
- mcmcli/__main__.py,sha256=G9B6mZCD2JJpk8nHDdexuKfNigm8WZeFJXhutUEmIUE,1616
2
+ mcmcli/__main__.py,sha256=Rnx3sLJc0OObJic70Z0I6h0-pjTaKK-qYqj42nzcskw,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
6
  mcmcli/command/admin.py,sha256=nJ7rm0nm0jHPobg0PjNHIWaWURTQu6QEUEUKIo__GO0,3010
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
10
- mcmcli/command/wallet.py,sha256=0MprkN5hJqJCTbMpMnFLRcLKnnc5PNYpx5Sjhys6XTY,9242
10
+ mcmcli/command/wallet.py,sha256=giPDktndwh-NSIZbCvBzCQ6_MttZsqOkCW1wJ-fFJ8Y,11708
11
11
  mcmcli/data/account.py,sha256=pe7tPapP6vlUD5D5L5Nh5k2bkWdYOK01Mpt5fBYFnJs,1782
12
12
  mcmcli/data/account_user.py,sha256=27nQp52nMma5a3QszSJGqgq5Z0ivIb-nMZcZuhEgbEg,1328
13
13
  mcmcli/data/decision.py,sha256=bQ5j_PbPRSFa0sY5g9UVqdNzl_2epchcz1lHoDVuV90,2880
@@ -15,11 +15,11 @@ mcmcli/data/error.py,sha256=d6xa_jTXumlA0EzXy2PJQ86ajBb0Pm90fss9R3LuHUc,1094
15
15
  mcmcli/data/item.py,sha256=Z2xTRhU8T4vyJADO0l6-XPyQXvb9DX_OAjExhSXpW2A,1091
16
16
  mcmcli/data/seller.py,sha256=40SA7QekM3a3svDrDYLo_QYJ68VUxDO0KeGejJMp4k4,1004
17
17
  mcmcli/data/token.py,sha256=11wtyLHCAZHu0LVbNDPa-yipcL6lenxoYIKEI58VzFs,1744
18
- mcmcli/data/wallet.py,sha256=W-CksF9SPqiv3jZg07Wy8ehVUP5Ot1Gbq2LEGNQCOC8,1906
19
- mcm_cli-1.2.0.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
20
- mcm_cli-1.2.0.dist-info/METADATA,sha256=7ewr0UPyJ6IqNvXOSL7Gci2BryiET3dEqfnuM1BYOxc,3018
21
- mcm_cli-1.2.0.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
22
- mcm_cli-1.2.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
23
- mcm_cli-1.2.0.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
24
- mcm_cli-1.2.0.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
25
- mcm_cli-1.2.0.dist-info/RECORD,,
18
+ mcmcli/data/wallet.py,sha256=eMUi8N0vJdg_E10TPhSPoZkZtmIG7gHyqgabQ8C5Lg8,3217
19
+ mcm_cli-1.3.1.dist-info/LICENSE,sha256=RFhQPdSOiMTguUX7JSoIuTxA7HVzCbj_p8WU36HjUQQ,10947
20
+ mcm_cli-1.3.1.dist-info/METADATA,sha256=GMtyMAiGd5DxGm7v7Id5AcSgtW5tcq5paDD1XrXlaag,3018
21
+ mcm_cli-1.3.1.dist-info/NOTICE,sha256=Ldnl2MjRaXPxcldUdbI2NTybq60XAa2LowRhFrRTuiI,76
22
+ mcm_cli-1.3.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
23
+ mcm_cli-1.3.1.dist-info/entry_points.txt,sha256=qTHAWZ-ejSiU4t11RYwtAU8ScqhQPDeMVTG9y4wMVLg,60
24
+ mcm_cli-1.3.1.dist-info/top_level.txt,sha256=sh7oqIaqLQlMtKHlxHHgpV2xGMrBMPFWpSp0C6nvJ_Y,7
25
+ mcm_cli-1.3.1.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.2.0")
38
+ typer.echo(f"Version: mcm-cli v1.3.1")
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/wallet.py CHANGED
@@ -12,9 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from datetime import datetime
15
16
  from enum import Enum
16
17
  from mcmcli.data.error import Error
17
- from mcmcli.data.wallet import Wallet, WalletsWrapper
18
+ from mcmcli.data.wallet import Wallet, WalletsWrapper, PlatformWalletsWrapper
18
19
  from mcmcli.requests import CurlString, api_request
19
20
 
20
21
  import json
@@ -36,20 +37,68 @@ class OperationType(Enum):
36
37
  DEPOSIT = "deposit"
37
38
  WITHDRAW = "withdraw"
38
39
 
40
+ def _get_wallet_balance(wallet: Wallet):
41
+ wa0 = wallet.accounts[0]
42
+ wa1 = wallet.accounts[1]
43
+
44
+ credits_amount_micro = wa0.balance.amount_micro if wa0.type == 'CREDITS' else wa1.balance.amount_micro
45
+ pre_paid_amount_micro = wa0.balance.amount_micro if wa0.type == 'PRE_PAID' else wa1.balance.amount_micro
46
+ credit_balance = float(credits_amount_micro) / float(1000000)
47
+ prepaid_balance = float(pre_paid_amount_micro) / float(1000000)
48
+
49
+ return (credit_balance, prepaid_balance)
50
+
51
+
52
+ @app.command()
53
+ def platform_balance(
54
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
55
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
56
+ ):
57
+ """
58
+ Get the current balances of all ad accounts in CSV format.
59
+ """
60
+ auth = mcmcli.command.auth.AuthCommand(profile)
61
+ curl, error, token = auth.get_token()
62
+ if error:
63
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
64
+ return
65
+
66
+ # Get current UTC timestamp and format it as an ISO 8601 string with microseconds
67
+ current_timestamp = datetime.utcnow().isoformat(timespec='microseconds') + "Z"
68
+
69
+ wc = WalletCommand(profile, auth, token.token)
70
+ curl, error, platform_wallets = wc.get_platform_balance(to_curl)
71
+ if to_curl:
72
+ print(curl)
73
+ return
74
+ if error:
75
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
76
+ return
77
+ if platform_wallets is None:
78
+ print(f"ERROR: Cannot find the wallets", file=sys.stderr, flush=True)
79
+ return
80
+
81
+ print("ad_account_id,credit_balance,prepaid_balance,got_balance_info_at_utc")
82
+ for account_id, account_data in platform_wallets.items():
83
+ wallet = account_data.wallets[0]
84
+ credit_balance, prepaid_balance = _get_wallet_balance(wallet)
85
+ print(f'{account_id},{credit_balance},{prepaid_balance},{current_timestamp}')
86
+
87
+
39
88
  @app.command()
40
89
  def balance(
41
90
  account_id: str = typer.Option(help="Ad account ID"),
42
91
  to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
43
92
  to_json: bool = typer.Option(False, help="Print raw output in json"),
44
- profile: str = "default",
45
- ):
93
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
94
+ ):
46
95
  """
47
96
  Retrive the current balance of the given ad account's wallet.
48
97
  """
49
98
  auth = mcmcli.command.auth.AuthCommand(profile)
50
99
  curl, error, token = auth.get_token()
51
100
  if error:
52
- print(f"ERROR: {error.message}")
101
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
53
102
  return
54
103
 
55
104
  wc = WalletCommand(profile, auth, token.token)
@@ -58,24 +107,20 @@ def balance(
58
107
  print(curl)
59
108
  return
60
109
  if error:
61
- print(f"ERROR: {error.message}")
110
+ print(f"ERROR: {error.message}", file=sys.stderr, flush=True)
111
+ return
112
+ if wallet is None:
113
+ print(f"ERROR: Wallet does not exist.", file=sys.stderr, flush=True)
62
114
  return
63
115
  if to_json:
64
116
  print(wallet.model_dump_json())
65
117
  return
66
118
 
67
- wa0 = wallet.accounts[0]
68
- wa1 = wallet.accounts[1]
69
-
70
- credits_amount_micro = wa0.balance.amount_micro if wa0.type == 'CREDITS' else wa1.balance.amount_micro
71
- pre_paid_amount_micro = wa0.balance.amount_micro if wa0.type == 'PRE_PAID' else wa1.balance.amount_micro
72
- credit_amount = float(credits_amount_micro) / float(1000000)
73
- money_amount = float(pre_paid_amount_micro) / float(1000000)
119
+ credit_balance, prepaid_balance = _get_wallet_balance(wallet)
74
120
 
75
121
  print(f"Ad account ID = {account_id}")
76
- print(f"Wallet ID = {wallet.id}")
77
- print(f"PRE_PAID balance = {money_amount}")
78
- print(f"CREDITS balance = {credit_amount}")
122
+ print(f"CREDITS balance = {credit_balance}")
123
+ print(f"PRE_PAID balance = {prepaid_balance}")
79
124
  return
80
125
 
81
126
 
@@ -86,8 +131,8 @@ def deposit(
86
131
  fund_amount: float = typer.Option(help="The fund amount to deposit"),
87
132
  to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
88
133
  to_json: bool = typer.Option(False, help="Print raw output in json"),
89
- profile: str = "default",
90
- ):
134
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
135
+ ):
91
136
  """
92
137
  Add or top up the money amount to the current balance of the given ad account's wallet.
93
138
  """
@@ -137,8 +182,8 @@ def withdraw(
137
182
  fund_amount: float = typer.Option(help="The amount of credit to add"),
138
183
  to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
139
184
  to_json: bool = typer.Option(False, help="Print raw output in json"),
140
- profile: str = "default",
141
- ):
185
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
186
+ ):
142
187
  """
143
188
  Withdraws the money amount from the current balance of the given ad account's wallet.
144
189
  """
@@ -202,6 +247,25 @@ class WalletCommand:
202
247
  "Authorization": f"Bearer {token}"
203
248
  }
204
249
 
250
+ def get_platform_balance(
251
+ self,
252
+ to_curl: bool,
253
+ ) -> tuple[
254
+ None | CurlString,
255
+ None | Error,
256
+ None | dict[str, WalletsWrapper]
257
+ ]:
258
+ _api_url = f"{self.api_base_url}/wallets-bulk/read"
259
+
260
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers)
261
+ if curl:
262
+ return curl, None, None
263
+ if error:
264
+ return None, error, None
265
+
266
+ platform_wallets_wrapper = PlatformWalletsWrapper(**json_obj)
267
+ return None, None, platform_wallets_wrapper.result
268
+
205
269
 
206
270
  def get_balance(
207
271
  self,
mcmcli/data/wallet.py CHANGED
@@ -67,3 +67,46 @@ class Wallet(BaseModel):
67
67
 
68
68
  class WalletsWrapper(BaseModel):
69
69
  wallets: list[Wallet]
70
+
71
+
72
+ #
73
+ # API response dataclasses for the platform balance API responses
74
+ #
75
+ #
76
+ # {
77
+ # "result": {
78
+ # "10054": {
79
+ # "wallets": [
80
+ # {
81
+ # "id": "wRydQ9MhQx4ZyJED",
82
+ # "title": "",
83
+ # "ad_account_id": "10054",
84
+ # "type": "PRE_PAYMENT",
85
+ # "accounts": [
86
+ # {
87
+ # "type": "CREDITS",
88
+ # "balance": {
89
+ # "currency": "USD",
90
+ # "amount_micro": "1890020000"
91
+ # }
92
+ # },
93
+ # {
94
+ # "type": "PRE_PAID",
95
+ # "balance": {
96
+ # "currency": "USD",
97
+ # "amount_micro": "0"
98
+ # }
99
+ # }
100
+ # ],
101
+ # "created_at": "2024-09-24T17:26:09.437580Z",
102
+ # "updated_at": "2024-10-20T22:00:21.412482Z"
103
+ # }
104
+ # ]
105
+ # }
106
+ # }
107
+ # }
108
+
109
+ class PlatformWalletsWrapper(BaseModel):
110
+ result: dict[str, WalletsWrapper] # Dictionary with ad_account_id as keys
111
+
112
+
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