mcm-cli 0.471__tar.gz → 1.0.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. {mcm_cli-0.471/mcm_cli.egg-info → mcm_cli-1.0.1}/PKG-INFO +2 -1
  2. {mcm_cli-0.471 → mcm_cli-1.0.1}/README.md +1 -0
  3. {mcm_cli-0.471 → mcm_cli-1.0.1/mcm_cli.egg-info}/PKG-INFO +2 -1
  4. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcm_cli.egg-info/SOURCES.txt +2 -0
  5. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/__main__.py +3 -1
  6. mcm_cli-1.0.1/mcmcli/command/decision.py +265 -0
  7. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/command/wallet.py +2 -2
  8. mcm_cli-1.0.1/mcmcli/data/decision.py +104 -0
  9. {mcm_cli-0.471 → mcm_cli-1.0.1}/setup.py +1 -1
  10. {mcm_cli-0.471 → mcm_cli-1.0.1}/LICENSE +0 -0
  11. {mcm_cli-0.471 → mcm_cli-1.0.1}/NOTICE +0 -0
  12. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcm_cli.egg-info/dependency_links.txt +0 -0
  13. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcm_cli.egg-info/entry_points.txt +0 -0
  14. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcm_cli.egg-info/requires.txt +0 -0
  15. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcm_cli.egg-info/top_level.txt +0 -0
  16. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/__init__.py +0 -0
  17. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/command/account.py +0 -0
  18. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/command/auth.py +0 -0
  19. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/command/config.py +0 -0
  20. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/account.py +0 -0
  21. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/account_user.py +0 -0
  22. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/error.py +0 -0
  23. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/seller.py +0 -0
  24. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/token.py +0 -0
  25. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/data/wallet.py +0 -0
  26. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/logging.py +0 -0
  27. {mcm_cli-0.471 → mcm_cli-1.0.1}/mcmcli/requests.py +0 -0
  28. {mcm_cli-0.471 → mcm_cli-1.0.1}/setup.cfg +0 -0
  29. {mcm_cli-0.471 → mcm_cli-1.0.1}/tests/test_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 0.471
3
+ Version: 1.0.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
@@ -81,6 +81,7 @@ $ mcm --help
81
81
  │ account Ad account management │
82
82
  │ auth Authentication management │
83
83
  │ config Configurations │
84
+ │ decision Decision command │
84
85
  │ version Show the tool version │
85
86
  │ wallet Wallet management │
86
87
  ╰──────────────────────────────────────────────────────────────────╯
@@ -55,6 +55,7 @@ $ mcm --help
55
55
  │ account Ad account management │
56
56
  │ auth Authentication management │
57
57
  │ config Configurations │
58
+ │ decision Decision command │
58
59
  │ version Show the tool version │
59
60
  │ wallet Wallet management │
60
61
  ╰──────────────────────────────────────────────────────────────────╯
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mcm-cli
3
- Version: 0.471
3
+ Version: 1.0.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
@@ -81,6 +81,7 @@ $ mcm --help
81
81
  │ account Ad account management │
82
82
  │ auth Authentication management │
83
83
  │ config Configurations │
84
+ │ decision Decision command │
84
85
  │ version Show the tool version │
85
86
  │ wallet Wallet management │
86
87
  ╰──────────────────────────────────────────────────────────────────╯
@@ -15,9 +15,11 @@ mcmcli/requests.py
15
15
  mcmcli/command/account.py
16
16
  mcmcli/command/auth.py
17
17
  mcmcli/command/config.py
18
+ mcmcli/command/decision.py
18
19
  mcmcli/command/wallet.py
19
20
  mcmcli/data/account.py
20
21
  mcmcli/data/account_user.py
22
+ mcmcli/data/decision.py
21
23
  mcmcli/data/error.py
22
24
  mcmcli/data/seller.py
23
25
  mcmcli/data/token.py
@@ -20,6 +20,7 @@
20
20
  import mcmcli.command.account
21
21
  import mcmcli.command.auth
22
22
  import mcmcli.command.config
23
+ import mcmcli.command.decision
23
24
  import mcmcli.command.wallet
24
25
  import mcmcli.logging
25
26
  import typer
@@ -31,11 +32,12 @@ def version():
31
32
  """
32
33
  Show the tool version
33
34
  """
34
- typer.echo(f"Version: mcm-cli v0.471")
35
+ typer.echo(f"Version: mcm-cli v1.0.1")
35
36
 
36
37
  app.add_typer(mcmcli.command.account.app, name="account", help="Ad account management")
37
38
  app.add_typer(mcmcli.command.auth.app, name="auth", help="Authentication management")
38
39
  app.add_typer(mcmcli.command.config.app, name="config", help="Configurations")
40
+ app.add_typer(mcmcli.command.decision.app, name="decision", help="Decision command")
39
41
  app.add_typer(mcmcli.command.wallet.app, name="wallet", help="Wallet management")
40
42
 
41
43
  if __name__ == "__main__":
@@ -0,0 +1,265 @@
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 mcmcli.data.error import Error
16
+ from mcmcli.data.decision import DecidedCreative, DecidedCreativeBulkList, DecidedItemList
17
+ from mcmcli.requests import CurlString, api_request
18
+ from typing import Any, Callable, Dict, Optional, Tuple, TypeVar
19
+
20
+ import json
21
+ import mcmcli.command.auth
22
+ import mcmcli.command.config
23
+ import mcmcli.logging
24
+ import sys
25
+ import typer
26
+
27
+ MAX_NUM_ITEMS_PER_PAGE = 5000
28
+
29
+ app = typer.Typer(add_completion=False)
30
+
31
+ @app.command()
32
+ def decide_items(
33
+ inventory_id: str = typer.Option(help="Ad inventory ID"),
34
+ num_items: int = typer.Option(help="Number of items requested for the inventory."),
35
+ items: str = typer.Option(None, help="The main item ids of the page. For example, homepage inventories don't have any main items, and product-detail-page inventories have one main item."),
36
+ location_filter: str = typer.Option(None, help="Location filter value"),
37
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
38
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
39
+ ):
40
+ """
41
+ Request item decision by auction.
42
+ """
43
+ d = DecisionCommand(profile)
44
+
45
+ curl, error, ret = d.decide_items(inventory_id, num_items, items, location_filter, to_curl)
46
+ if to_curl:
47
+ print(curl)
48
+ return
49
+ if error:
50
+ print(f"ERROR: {error.message}")
51
+ return
52
+ if ret is None:
53
+ print(f"ERROR: Unknown error")
54
+ return
55
+
56
+ print(ret.model_dump_json())
57
+ return
58
+
59
+
60
+ @app.command()
61
+ def decide_creative(
62
+ inventory_id: str = typer.Option(help="Ad inventory ID"),
63
+ items: str = typer.Option(None, help="The main item ids of the page. For example, homepage inventories don't have any main items, and product-detail-page inventories have one main item."),
64
+ location_filter: str = typer.Option(None, help="Location filter value"),
65
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
66
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
67
+ ):
68
+ """
69
+ Request item decision by creative auction.
70
+ """
71
+ d = DecisionCommand(profile)
72
+
73
+ curl, error, ret = d.decide_creative(inventory_id, items, location_filter, to_curl)
74
+ if to_curl:
75
+ print(curl)
76
+ return
77
+ if error:
78
+ print(f"ERROR: {error.message}")
79
+ return
80
+ if ret is None:
81
+ print(f"ERROR: Unknown error")
82
+ return
83
+
84
+ print(ret.model_dump_json())
85
+ return
86
+
87
+
88
+ @app.command()
89
+ def decide_creative_bulk(
90
+ inventory_id_list: str = typer.Option(help="Ad inventory IDs separated by comma(,)"),
91
+ items: str = typer.Option(None, help="The main item ids of the page. For example, homepage inventories don't have any main items, and product-detail-page inventories have one main item."),
92
+ location_filter: str = typer.Option(None, help="Location filter value"),
93
+ to_curl: bool = typer.Option(False, help="Generate the curl command instead of executing it."),
94
+ profile: str = typer.Option("default", help="profile name of the MCM CLI."),
95
+ ):
96
+ """
97
+ Request item decision by creative auction for multiple inventories.
98
+ """
99
+ d = DecisionCommand(profile)
100
+
101
+ curl, error, ret = d.decide_creative_bulk(inventory_id_list, items, location_filter, to_curl)
102
+ if to_curl:
103
+ print(curl)
104
+ return
105
+ if error:
106
+ print(f"ERROR: {error.message}")
107
+ return
108
+ if ret is None:
109
+ print(f"ERROR: Unknown error")
110
+ return
111
+
112
+ print(ret.model_dump_json())
113
+ return
114
+
115
+
116
+
117
+ class DecisionCommand:
118
+ def __init__(
119
+ self,
120
+ profile,
121
+ ):
122
+ self.config = mcmcli.command.config.get_config(profile)
123
+ if (self.config is None):
124
+ print(f"ERROR: Failed to load the CLI profile", file=sys.stderr, flush=True)
125
+ sys.exit()
126
+
127
+ self.profile = profile
128
+ self.api_base_url = f"{self.config['decision_api_hostname']}/rmp/decision/v1/platforms/{self.config['platform_id']}"
129
+ self.headers = {
130
+ "accept": "application/json",
131
+ "content-type": "application/json",
132
+ "x-api-key": self.config['decision_api_key']
133
+ }
134
+
135
+ def decide_items(
136
+ self,
137
+ inventory_id,
138
+ num_items,
139
+ items,
140
+ location_filter,
141
+ to_curl,
142
+ ) -> tuple[
143
+ Optional[CurlString],
144
+ Optional[Error],
145
+ Optional[DecidedItemList],
146
+ ]:
147
+ _api_url = f"{self.api_base_url}/auction"
148
+ _payload = {
149
+ "request_id": "request-1",
150
+ "inventory": {
151
+ "inventory_id": inventory_id,
152
+ "num_items": num_items
153
+ },
154
+ "user": {
155
+ "user_id": "user-1"
156
+ },
157
+ "device": {
158
+ "persistent_id": "persistent-device-1"
159
+ },
160
+ }
161
+ if items:
162
+ _payload["inventory"]["items"] = items.split(',')
163
+
164
+ if location_filter:
165
+ _payload["filtering"] = {
166
+ "location": {
167
+ "locations": location_filter.split(',')
168
+ }
169
+ }
170
+
171
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
172
+ if curl:
173
+ return curl, None, None
174
+ if error:
175
+ return None, error, None
176
+
177
+ decided_items = DecidedItemList(**json_obj)
178
+ return None, None, decided_items
179
+
180
+
181
+ def decide_creative(
182
+ self,
183
+ inventory_id,
184
+ items,
185
+ location_filter,
186
+ to_curl
187
+ ) -> tuple[
188
+ Optional[CurlString],
189
+ Optional[Error],
190
+ Optional[DecidedCreative],
191
+ ]:
192
+ _api_url = f"{self.api_base_url}/creative-auction"
193
+ _payload = {
194
+ "request_id": "request-1",
195
+ "inventory": {
196
+ "inventory_id": inventory_id
197
+ },
198
+ "user": {
199
+ "user_id": "user-1"
200
+ },
201
+ "device": {
202
+ "persistent_id": "persistent-device-1"
203
+ },
204
+ }
205
+ if items:
206
+ _payload["inventory"]["items"] = items.split(',')
207
+
208
+ if location_filter:
209
+ _payload["filtering"] = {
210
+ "location": {
211
+ "locations": location_filter.split(',')
212
+ }
213
+ }
214
+
215
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
216
+ if curl:
217
+ return curl, None, None
218
+ if error:
219
+ return None, error, None
220
+
221
+ decided_creative = DecidedCreative(**json_obj)
222
+ return None, None, decided_creative
223
+
224
+
225
+ def decide_creative_bulk(
226
+ self,
227
+ inventory_id_list,
228
+ items,
229
+ location_filter,
230
+ to_curl
231
+ ) -> tuple[
232
+ Optional[CurlString],
233
+ Optional[Error],
234
+ Optional[DecidedCreativeBulkList],
235
+ ]:
236
+ _api_url = f"{self.api_base_url}/creative-auction-bulk"
237
+ _payload = {
238
+ "request_id": "request-1",
239
+ "inventories": list(map(lambda x: { "inventory_id": x }, inventory_id_list.split(','))),
240
+ "user": {
241
+ "user_id": "user-1"
242
+ },
243
+ "device": {
244
+ "persistent_id": "persistent-device-1"
245
+ },
246
+ }
247
+ if items:
248
+ for inventory in _payload["inventories"]:
249
+ inventory["items"] = items.split(',')
250
+
251
+ if location_filter:
252
+ _payload["filtering"] = {
253
+ "location": {
254
+ "locations": location_filter.split(',')
255
+ }
256
+ }
257
+
258
+ curl, error, json_obj = api_request('POST', to_curl, _api_url, self.headers, _payload)
259
+ if curl:
260
+ return curl, None, None
261
+ if error:
262
+ return None, error, None
263
+
264
+ return None, None, DecidedCreativeBulkList(**json_obj)
265
+
@@ -127,7 +127,7 @@ def deposit(
127
127
  credits_amount = float(credits_amount_micro) / float(1000000)
128
128
  pre_paid_amount = float(pre_paid_amount_micro) / float(1000000)
129
129
 
130
- print(f"Deposited to the wallet. Currence balance of the ad account id {account_id} is {pre_paid_amount} for PRE_PAID and {credits_amount} for CREDITS.")
130
+ print(f"Funds have been deposited into the wallet. The current balance for ad account ID {account_id} is {pre_paid_amount} in PRE_PAID and {credits_amount} in CREDITS.")
131
131
  return
132
132
 
133
133
  @app.command()
@@ -178,7 +178,7 @@ def withdraw(
178
178
  credits_amount = float(credits_amount_micro) / float(1000000)
179
179
  pre_paid_amount = float(pre_paid_amount_micro) / float(1000000)
180
180
 
181
- print(f"Withdrew the funds from to the wallet. Currence balance of the ad account id {account_id} is {pre_paid_amount} for PRE_PAID and {credits_amount} for CREDITS.")
181
+ print(f"Funds were withdrawn out of the wallet. The current balance of ad account ID {account_id} is {pre_paid_amount} for PRE_PAID and {credits_amount} for CREDITS.")
182
182
  return
183
183
 
184
184
  class WalletCommand:
@@ -0,0 +1,104 @@
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 dataclasses
20
+ #
21
+ # Item decision API's response example:
22
+ #
23
+ # {
24
+ # "request_id": "request-1",
25
+ # "decided_items": [
26
+ # {
27
+ # "item_id": "1111833",
28
+ # "auction_result": {
29
+ # "ad_account_id": "2444",
30
+ # "campaign_id": "zWHhpyNbzYcy5FAy",
31
+ # "win_price": {
32
+ # "currency": "USD",
33
+ # "amount_micro": "500000000"
34
+ # },
35
+ # "campaign_text_entry": ""
36
+ # },
37
+ # "imp_trackers": [
38
+ # "https://myplatform-evt.rmp-api.moloco.com/t/i/MYPLATFORM_TEST?source=2X0op"
39
+ # ],
40
+ # "click_trackers": [
41
+ # "https://myplatform-evt.rmp-api.moloco.com/t/c/MYPLATFORM_TEST?source=2X0opp"
42
+ # ],
43
+ # "track_id": "2X0op"
44
+ # }
45
+ # ]
46
+ # }
47
+
48
+ class MicroPrice(BaseModel):
49
+ currency: str
50
+ amount_micro: str
51
+
52
+ class AuctionResult(BaseModel):
53
+ ad_account_id: str
54
+ campaign_id: str
55
+ win_price: Optional[MicroPrice]
56
+ campaign_text_entry: Optional[str]
57
+
58
+ class DecidedItem(BaseModel):
59
+ item_id: str
60
+ auction_result: Optional[AuctionResult]
61
+ imp_trackers: list[str]
62
+ click_trackers: list[str]
63
+ track_id: Optional[str]
64
+
65
+ class DecidedItemList(BaseModel):
66
+ request_id: str
67
+ decided_items: list[DecidedItem]
68
+
69
+ class CreativeBanner(BaseModel):
70
+ creative_id: str
71
+ image_url: str
72
+ imp_trackers: list[str]
73
+ click_trackers: list[str]
74
+
75
+ class CreativeItem(BaseModel):
76
+ item_id: str
77
+ imp_trackers: list[str]
78
+ click_trackers: list[str]
79
+
80
+ class LandingUrl(BaseModel):
81
+ id: str
82
+ url: str
83
+
84
+ class DecidedCreative(BaseModel):
85
+ request_id: str
86
+ auction_result: Optional[AuctionResult]
87
+ banner: Optional[CreativeBanner]
88
+ items: list[CreativeItem]
89
+ landing_url:Optional[LandingUrl]
90
+
91
+ class CreativeBannerWrapper(BaseModel):
92
+ banner: CreativeBanner
93
+
94
+ class DecidedCreativeBulk(BaseModel):
95
+ inventory_id: str
96
+ auction_result: Optional[AuctionResult]
97
+ creatives: list[CreativeBannerWrapper]
98
+ items: list[DecidedItem]
99
+ landing_url: Optional[LandingUrl]
100
+
101
+ class DecidedCreativeBulkList(BaseModel):
102
+ request_id: str
103
+ results: list[DecidedCreativeBulk]
104
+
@@ -18,7 +18,7 @@ from setuptools import setup, find_packages
18
18
 
19
19
  setup(
20
20
  name='mcm-cli',
21
- version='0.471',
21
+ version='1.0.1',
22
22
  description='A command-line interface for Moloco Commerde Media',
23
23
  long_description=open('README.md').read(),
24
24
  long_description_content_type='text/markdown',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes