direct-cli 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- direct_cli/__init__.py +14 -0
- direct_cli/api.py +94 -0
- direct_cli/auth.py +58 -0
- direct_cli/cli.py +85 -0
- direct_cli/commands/__init__.py +61 -0
- direct_cli/commands/adextensions.py +96 -0
- direct_cli/commands/adgroups.py +189 -0
- direct_cli/commands/adimages.py +63 -0
- direct_cli/commands/ads.py +306 -0
- direct_cli/commands/agencyclients.py +64 -0
- direct_cli/commands/audiencetargets.py +187 -0
- direct_cli/commands/bidmodifiers.py +110 -0
- direct_cli/commands/bids.py +108 -0
- direct_cli/commands/businesses.py +61 -0
- direct_cli/commands/campaigns.py +311 -0
- direct_cli/commands/changes.py +97 -0
- direct_cli/commands/clients.py +98 -0
- direct_cli/commands/creatives.py +68 -0
- direct_cli/commands/dictionaries.py +64 -0
- direct_cli/commands/dynamicads.py +104 -0
- direct_cli/commands/feeds.py +99 -0
- direct_cli/commands/keywordbids.py +111 -0
- direct_cli/commands/keywords.py +309 -0
- direct_cli/commands/keywordsresearch.py +71 -0
- direct_cli/commands/leads.py +65 -0
- direct_cli/commands/negativekeywordsharedsets.py +97 -0
- direct_cli/commands/reports.py +128 -0
- direct_cli/commands/retargeting.py +104 -0
- direct_cli/commands/sitelinks.py +92 -0
- direct_cli/commands/smartadtargets.py +104 -0
- direct_cli/commands/turbopages.py +97 -0
- direct_cli/commands/vcards.py +93 -0
- direct_cli/output.py +143 -0
- direct_cli/utils.py +120 -0
- direct_cli-0.0.0.dist-info/METADATA +393 -0
- direct_cli-0.0.0.dist-info/RECORD +39 -0
- direct_cli-0.0.0.dist-info/WHEEL +5 -0
- direct_cli-0.0.0.dist-info/entry_points.txt +2 -0
- direct_cli-0.0.0.dist-info/top_level.txt +1 -0
direct_cli/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Direct CLI - Command-line interface for Yandex Direct API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__author__ = "Pavel Maksimov"
|
|
6
|
+
__email__ = "vur21@ya.ru"
|
|
7
|
+
|
|
8
|
+
# Version will be set by setuptools_scm
|
|
9
|
+
try:
|
|
10
|
+
from ._version import version as __version__
|
|
11
|
+
except ImportError:
|
|
12
|
+
__version__ = "unknown"
|
|
13
|
+
|
|
14
|
+
__all__ = ["__version__", "__author__", "__email__"]
|
direct_cli/api.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
API client wrapper for Direct CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Dict, Any, List
|
|
6
|
+
|
|
7
|
+
from tapi_yandex_direct import YandexDirect
|
|
8
|
+
from .auth import get_credentials
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_client(
|
|
12
|
+
token: Optional[str] = None,
|
|
13
|
+
login: Optional[str] = None,
|
|
14
|
+
sandbox: bool = False,
|
|
15
|
+
) -> YandexDirect:
|
|
16
|
+
"""
|
|
17
|
+
Create YandexDirect client
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
token: API access token
|
|
21
|
+
login: Client login (for agency accounts)
|
|
22
|
+
sandbox: Use sandbox API
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
YandexDirect client instance
|
|
26
|
+
"""
|
|
27
|
+
final_token, final_login = get_credentials(token, login)
|
|
28
|
+
|
|
29
|
+
return YandexDirect(
|
|
30
|
+
access_token=final_token,
|
|
31
|
+
login=final_login,
|
|
32
|
+
is_sandbox=sandbox,
|
|
33
|
+
retry_if_exceeded_limit=True,
|
|
34
|
+
retries_if_server_error=5,
|
|
35
|
+
# Report settings
|
|
36
|
+
processing_mode="auto",
|
|
37
|
+
wait_report=True,
|
|
38
|
+
return_money_in_micros=False,
|
|
39
|
+
skip_report_header=True,
|
|
40
|
+
skip_column_header=False,
|
|
41
|
+
skip_report_summary=True,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def fetch_all_pages(
|
|
46
|
+
client: YandexDirect,
|
|
47
|
+
resource_name: str,
|
|
48
|
+
method: str = "get",
|
|
49
|
+
params: Dict[str, Any] = None,
|
|
50
|
+
field_names: Optional[List[str]] = None,
|
|
51
|
+
progress: bool = False,
|
|
52
|
+
) -> List[Dict[str, Any]]:
|
|
53
|
+
"""
|
|
54
|
+
Fetch all pages of results
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
client: YandexDirect client
|
|
58
|
+
resource_name: Resource name (e.g., 'campaigns', 'adgroups')
|
|
59
|
+
method: API method ('get')
|
|
60
|
+
params: Request params
|
|
61
|
+
field_names: Field names to include
|
|
62
|
+
progress: Show progress bar
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of all items from all pages
|
|
66
|
+
"""
|
|
67
|
+
if params is None:
|
|
68
|
+
params = {}
|
|
69
|
+
|
|
70
|
+
body = {
|
|
71
|
+
"method": method,
|
|
72
|
+
"params": params,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if field_names:
|
|
76
|
+
body["params"]["FieldNames"] = field_names
|
|
77
|
+
|
|
78
|
+
resource = getattr(client, resource_name)()
|
|
79
|
+
result = resource.post(data=body)
|
|
80
|
+
|
|
81
|
+
# Get items from first page
|
|
82
|
+
items = result().extract()
|
|
83
|
+
|
|
84
|
+
# Check if there are more pages
|
|
85
|
+
if hasattr(result, "iter_items") and callable(result().iter_items):
|
|
86
|
+
all_items = []
|
|
87
|
+
|
|
88
|
+
# Use iter_items for pagination
|
|
89
|
+
for item in result().iter_items():
|
|
90
|
+
all_items.append(item)
|
|
91
|
+
|
|
92
|
+
return all_items
|
|
93
|
+
|
|
94
|
+
return items if isinstance(items, list) else [items]
|
direct_cli/auth.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication module for Direct CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Optional, Tuple
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
except ImportError:
|
|
11
|
+
load_dotenv = lambda: None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_env_file(env_path: Optional[str] = None) -> None:
|
|
15
|
+
"""Load environment variables from .env file"""
|
|
16
|
+
if load_dotenv:
|
|
17
|
+
if env_path:
|
|
18
|
+
load_dotenv(env_path)
|
|
19
|
+
else:
|
|
20
|
+
load_dotenv()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_credentials(
|
|
24
|
+
token: Optional[str] = None,
|
|
25
|
+
login: Optional[str] = None,
|
|
26
|
+
env_path: Optional[str] = None,
|
|
27
|
+
) -> Tuple[str, Optional[str]]:
|
|
28
|
+
"""
|
|
29
|
+
Get credentials with priority:
|
|
30
|
+
1. Direct arguments
|
|
31
|
+
2. Environment variables
|
|
32
|
+
3. .env file
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
token: API access token
|
|
36
|
+
login: Client login (for agency accounts)
|
|
37
|
+
env_path: Path to .env file
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Tuple of (token, login)
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If token is not provided
|
|
44
|
+
"""
|
|
45
|
+
# Load .env file first
|
|
46
|
+
load_env_file(env_path)
|
|
47
|
+
|
|
48
|
+
# Priority: arguments > env vars
|
|
49
|
+
final_token = token or os.getenv("YANDEX_DIRECT_TOKEN")
|
|
50
|
+
final_login = login or os.getenv("YANDEX_DIRECT_LOGIN")
|
|
51
|
+
|
|
52
|
+
if not final_token:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"API token required. Set YANDEX_DIRECT_TOKEN environment variable "
|
|
55
|
+
"or create .env file, or use --token option."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return final_token, final_login
|
direct_cli/cli.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Direct CLI - Command-line interface for Yandex Direct API
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
|
|
9
|
+
from .commands.campaigns import campaigns
|
|
10
|
+
from .commands.adgroups import adgroups
|
|
11
|
+
from .commands.ads import ads
|
|
12
|
+
from .commands.keywords import keywords
|
|
13
|
+
from .commands.keywordbids import keywordbids
|
|
14
|
+
from .commands.bids import bids
|
|
15
|
+
from .commands.bidmodifiers import bidmodifiers
|
|
16
|
+
from .commands.audiencetargets import audiencetargets
|
|
17
|
+
from .commands.retargeting import retargeting
|
|
18
|
+
from .commands.creatives import creatives
|
|
19
|
+
from .commands.adimages import adimages
|
|
20
|
+
from .commands.adextensions import adextensions
|
|
21
|
+
from .commands.sitelinks import sitelinks
|
|
22
|
+
from .commands.vcards import vcards
|
|
23
|
+
from .commands.leads import leads
|
|
24
|
+
from .commands.clients import clients
|
|
25
|
+
from .commands.agencyclients import agencyclients
|
|
26
|
+
from .commands.dictionaries import dictionaries
|
|
27
|
+
from .commands.changes import changes
|
|
28
|
+
from .commands.reports import reports
|
|
29
|
+
from .commands.turbopages import turbopages
|
|
30
|
+
from .commands.negativekeywordsharedsets import negativekeywordsharedsets
|
|
31
|
+
from .commands.feeds import feeds
|
|
32
|
+
from .commands.smartadtargets import smartadtargets
|
|
33
|
+
from .commands.businesses import businesses
|
|
34
|
+
from .commands.keywordsresearch import keywordsresearch
|
|
35
|
+
from .commands.dynamicads import dynamicads
|
|
36
|
+
|
|
37
|
+
# Load .env file
|
|
38
|
+
load_dotenv()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.group()
|
|
42
|
+
@click.option("--token", envvar="YANDEX_DIRECT_TOKEN", help="API access token")
|
|
43
|
+
@click.option("--login", envvar="YANDEX_DIRECT_LOGIN", help="Client login")
|
|
44
|
+
@click.option("--sandbox", is_flag=True, help="Use sandbox API")
|
|
45
|
+
@click.pass_context
|
|
46
|
+
def cli(ctx, token, login, sandbox):
|
|
47
|
+
"""Command-line interface for Yandex Direct API"""
|
|
48
|
+
ctx.ensure_object(dict)
|
|
49
|
+
ctx.obj["token"] = token
|
|
50
|
+
ctx.obj["login"] = login
|
|
51
|
+
ctx.obj["sandbox"] = sandbox
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Register all commands
|
|
55
|
+
cli.add_command(campaigns)
|
|
56
|
+
cli.add_command(adgroups)
|
|
57
|
+
cli.add_command(ads)
|
|
58
|
+
cli.add_command(keywords)
|
|
59
|
+
cli.add_command(keywordbids)
|
|
60
|
+
cli.add_command(bids)
|
|
61
|
+
cli.add_command(bidmodifiers)
|
|
62
|
+
cli.add_command(audiencetargets)
|
|
63
|
+
cli.add_command(retargeting)
|
|
64
|
+
cli.add_command(creatives)
|
|
65
|
+
cli.add_command(adimages)
|
|
66
|
+
cli.add_command(adextensions)
|
|
67
|
+
cli.add_command(sitelinks)
|
|
68
|
+
cli.add_command(vcards)
|
|
69
|
+
cli.add_command(leads)
|
|
70
|
+
cli.add_command(clients)
|
|
71
|
+
cli.add_command(agencyclients)
|
|
72
|
+
cli.add_command(dictionaries)
|
|
73
|
+
cli.add_command(changes)
|
|
74
|
+
cli.add_command(reports)
|
|
75
|
+
cli.add_command(turbopages)
|
|
76
|
+
cli.add_command(negativekeywordsharedsets)
|
|
77
|
+
cli.add_command(feeds)
|
|
78
|
+
cli.add_command(smartadtargets)
|
|
79
|
+
cli.add_command(businesses)
|
|
80
|
+
cli.add_command(keywordsresearch)
|
|
81
|
+
cli.add_command(dynamicads)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
cli()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command handlers for Direct CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .campaigns import campaigns
|
|
6
|
+
from .adgroups import adgroups
|
|
7
|
+
from .ads import ads
|
|
8
|
+
from .keywords import keywords
|
|
9
|
+
from .keywordbids import keywordbids
|
|
10
|
+
from .bids import bids
|
|
11
|
+
from .bidmodifiers import bidmodifiers
|
|
12
|
+
from .audiencetargets import audiencetargets
|
|
13
|
+
from .retargeting import retargeting
|
|
14
|
+
from .creatives import creatives
|
|
15
|
+
from .adimages import adimages
|
|
16
|
+
from .adextensions import adextensions
|
|
17
|
+
from .sitelinks import sitelinks
|
|
18
|
+
from .vcards import vcards
|
|
19
|
+
from .leads import leads
|
|
20
|
+
from .clients import clients
|
|
21
|
+
from .agencyclients import agencyclients
|
|
22
|
+
from .dictionaries import dictionaries
|
|
23
|
+
from .changes import changes
|
|
24
|
+
from .reports import reports
|
|
25
|
+
from .turbopages import turbopages
|
|
26
|
+
from .negativekeywordsharedsets import negativekeywordsharedsets
|
|
27
|
+
from .feeds import feeds
|
|
28
|
+
from .smartadtargets import smartadtargets
|
|
29
|
+
from .businesses import businesses
|
|
30
|
+
from .keywordsresearch import keywordsresearch
|
|
31
|
+
from .dynamicads import dynamicads
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"campaigns",
|
|
35
|
+
"adgroups",
|
|
36
|
+
"ads",
|
|
37
|
+
"keywords",
|
|
38
|
+
"keywordbids",
|
|
39
|
+
"bids",
|
|
40
|
+
"bidmodifiers",
|
|
41
|
+
"audiencetargets",
|
|
42
|
+
"retargeting",
|
|
43
|
+
"creatives",
|
|
44
|
+
"adimages",
|
|
45
|
+
"adextensions",
|
|
46
|
+
"sitelinks",
|
|
47
|
+
"vcards",
|
|
48
|
+
"leads",
|
|
49
|
+
"clients",
|
|
50
|
+
"agencyclients",
|
|
51
|
+
"dictionaries",
|
|
52
|
+
"changes",
|
|
53
|
+
"reports",
|
|
54
|
+
"turbopages",
|
|
55
|
+
"negativekeywordsharedsets",
|
|
56
|
+
"feeds",
|
|
57
|
+
"smartadtargets",
|
|
58
|
+
"businesses",
|
|
59
|
+
"keywordsresearch",
|
|
60
|
+
"dynamicads",
|
|
61
|
+
]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AdExtensions commands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from ..api import create_client
|
|
9
|
+
from ..output import format_output, print_error
|
|
10
|
+
from ..utils import parse_ids
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
def adextensions():
|
|
15
|
+
"""Manage ad extensions"""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@adextensions.command()
|
|
20
|
+
@click.option("--ids", help="Comma-separated extension IDs")
|
|
21
|
+
@click.option("--types", help="Filter by types")
|
|
22
|
+
@click.option("--limit", type=int, help="Limit number of results")
|
|
23
|
+
@click.option("--fetch-all", is_flag=True, help="Fetch all pages")
|
|
24
|
+
@click.option("--format", "output_format", default="json", help="Output format")
|
|
25
|
+
@click.option("--output", help="Output file")
|
|
26
|
+
@click.option("--fields", help="Comma-separated field names")
|
|
27
|
+
@click.pass_context
|
|
28
|
+
def get(ctx, ids, types, limit, fetch_all, output_format, output, fields):
|
|
29
|
+
"""Get ad extensions"""
|
|
30
|
+
try:
|
|
31
|
+
client = create_client(
|
|
32
|
+
token=ctx.obj.get("token"),
|
|
33
|
+
login=ctx.obj.get("login"),
|
|
34
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
field_names = fields.split(",") if fields else ["Id", "Type", "Status"]
|
|
38
|
+
|
|
39
|
+
criteria = {}
|
|
40
|
+
if ids:
|
|
41
|
+
criteria["Ids"] = parse_ids(ids)
|
|
42
|
+
if types:
|
|
43
|
+
criteria["Types"] = types.split(",")
|
|
44
|
+
|
|
45
|
+
params = {"SelectionCriteria": criteria, "FieldNames": field_names}
|
|
46
|
+
|
|
47
|
+
if limit:
|
|
48
|
+
params["Page"] = {"Limit": limit}
|
|
49
|
+
|
|
50
|
+
body = {"method": "get", "params": params}
|
|
51
|
+
|
|
52
|
+
result = client.adextensions().post(data=body)
|
|
53
|
+
|
|
54
|
+
if fetch_all:
|
|
55
|
+
items = []
|
|
56
|
+
for item in result().iter_items():
|
|
57
|
+
items.append(item)
|
|
58
|
+
format_output(items, output_format, output)
|
|
59
|
+
else:
|
|
60
|
+
data = result().extract()
|
|
61
|
+
format_output(data, output_format, output)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print_error(str(e))
|
|
65
|
+
raise click.Abort()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@adextensions.command()
|
|
69
|
+
@click.option("--type", "ext_type", required=True, help="Extension type")
|
|
70
|
+
@click.option("--json", "extra_json", required=True, help="Extension data in JSON")
|
|
71
|
+
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
72
|
+
@click.pass_context
|
|
73
|
+
def add(ctx, ext_type, extra_json, dry_run):
|
|
74
|
+
"""Add ad extension"""
|
|
75
|
+
try:
|
|
76
|
+
ext_data = {"Type": ext_type}
|
|
77
|
+
ext_data.update(json.loads(extra_json))
|
|
78
|
+
|
|
79
|
+
body = {"method": "add", "params": {"AdExtensions": [ext_data]}}
|
|
80
|
+
|
|
81
|
+
if dry_run:
|
|
82
|
+
format_output(body, "json", None)
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
client = create_client(
|
|
86
|
+
token=ctx.obj.get("token"),
|
|
87
|
+
login=ctx.obj.get("login"),
|
|
88
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
result = client.adextensions().post(data=body)
|
|
92
|
+
format_output(result().extract(), "json", None)
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print_error(str(e))
|
|
96
|
+
raise click.Abort()
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ad Groups commands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from ..api import create_client
|
|
9
|
+
from ..output import format_output, print_error
|
|
10
|
+
from ..utils import parse_ids, get_default_fields
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
def adgroups():
|
|
15
|
+
"""Manage ad groups"""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@adgroups.command()
|
|
20
|
+
@click.option("--ids", help="Comma-separated ad group IDs")
|
|
21
|
+
@click.option("--campaign-ids", help="Comma-separated campaign IDs")
|
|
22
|
+
@click.option("--status", help="Filter by status")
|
|
23
|
+
@click.option("--types", help="Filter by types")
|
|
24
|
+
@click.option("--limit", type=int, help="Limit number of results")
|
|
25
|
+
@click.option("--fetch-all", is_flag=True, help="Fetch all pages")
|
|
26
|
+
@click.option("--format", "output_format", default="json", help="Output format")
|
|
27
|
+
@click.option("--output", help="Output file")
|
|
28
|
+
@click.option("--fields", help="Comma-separated field names")
|
|
29
|
+
@click.pass_context
|
|
30
|
+
def get(
|
|
31
|
+
ctx,
|
|
32
|
+
ids,
|
|
33
|
+
campaign_ids,
|
|
34
|
+
status,
|
|
35
|
+
types,
|
|
36
|
+
limit,
|
|
37
|
+
fetch_all,
|
|
38
|
+
output_format,
|
|
39
|
+
output,
|
|
40
|
+
fields,
|
|
41
|
+
):
|
|
42
|
+
"""Get ad groups"""
|
|
43
|
+
try:
|
|
44
|
+
client = create_client(
|
|
45
|
+
token=ctx.obj.get("token"),
|
|
46
|
+
login=ctx.obj.get("login"),
|
|
47
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
field_names = fields.split(",") if fields else get_default_fields("adgroups")
|
|
51
|
+
|
|
52
|
+
criteria = {}
|
|
53
|
+
if ids:
|
|
54
|
+
criteria["Ids"] = parse_ids(ids)
|
|
55
|
+
if campaign_ids:
|
|
56
|
+
criteria["CampaignIds"] = parse_ids(campaign_ids)
|
|
57
|
+
if status:
|
|
58
|
+
criteria["Statuses"] = [status]
|
|
59
|
+
if types:
|
|
60
|
+
criteria["Types"] = types.split(",")
|
|
61
|
+
|
|
62
|
+
params = {"SelectionCriteria": criteria, "FieldNames": field_names}
|
|
63
|
+
|
|
64
|
+
if limit:
|
|
65
|
+
params["Page"] = {"Limit": limit}
|
|
66
|
+
|
|
67
|
+
body = {"method": "get", "params": params}
|
|
68
|
+
|
|
69
|
+
result = client.adgroups().post(data=body)
|
|
70
|
+
|
|
71
|
+
if fetch_all:
|
|
72
|
+
items = []
|
|
73
|
+
for item in result().iter_items():
|
|
74
|
+
items.append(item)
|
|
75
|
+
format_output(items, output_format, output)
|
|
76
|
+
else:
|
|
77
|
+
data = result().extract()
|
|
78
|
+
format_output(data, output_format, output)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print_error(str(e))
|
|
82
|
+
raise click.Abort()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@adgroups.command()
|
|
86
|
+
@click.option("--name", required=True, help="Ad group name")
|
|
87
|
+
@click.option("--campaign-id", required=True, type=int, help="Campaign ID")
|
|
88
|
+
@click.option("--type", "group_type", default="TEXT_AD_GROUP", help="Ad group type")
|
|
89
|
+
@click.option("--region-ids", help="Comma-separated region IDs")
|
|
90
|
+
@click.option("--json", "extra_json", help="Additional JSON parameters")
|
|
91
|
+
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
92
|
+
@click.pass_context
|
|
93
|
+
def add(ctx, name, campaign_id, group_type, region_ids, extra_json, dry_run):
|
|
94
|
+
"""Add new ad group"""
|
|
95
|
+
try:
|
|
96
|
+
adgroup_data = {"Name": name, "CampaignId": campaign_id, "Type": group_type}
|
|
97
|
+
|
|
98
|
+
if region_ids:
|
|
99
|
+
adgroup_data["RegionIds"] = parse_ids(region_ids)
|
|
100
|
+
|
|
101
|
+
if extra_json:
|
|
102
|
+
extra = json.loads(extra_json)
|
|
103
|
+
adgroup_data.update(extra)
|
|
104
|
+
|
|
105
|
+
body = {"method": "add", "params": {"AdGroups": [adgroup_data]}}
|
|
106
|
+
|
|
107
|
+
if dry_run:
|
|
108
|
+
format_output(body, "json", None)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
client = create_client(
|
|
112
|
+
token=ctx.obj.get("token"),
|
|
113
|
+
login=ctx.obj.get("login"),
|
|
114
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
result = client.adgroups().post(data=body)
|
|
118
|
+
format_output(result().extract(), "json", None)
|
|
119
|
+
|
|
120
|
+
except Exception as e:
|
|
121
|
+
print_error(str(e))
|
|
122
|
+
raise click.Abort()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@adgroups.command()
|
|
126
|
+
@click.option("--id", "adgroup_id", required=True, type=int, help="Ad group ID")
|
|
127
|
+
@click.option("--name", help="New ad group name")
|
|
128
|
+
@click.option("--status", help="New status")
|
|
129
|
+
@click.option("--json", "extra_json", help="Additional JSON parameters")
|
|
130
|
+
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
131
|
+
@click.pass_context
|
|
132
|
+
def update(ctx, adgroup_id, name, status, extra_json, dry_run):
|
|
133
|
+
"""Update ad group"""
|
|
134
|
+
try:
|
|
135
|
+
adgroup_data = {"Id": adgroup_id}
|
|
136
|
+
|
|
137
|
+
if name:
|
|
138
|
+
adgroup_data["Name"] = name
|
|
139
|
+
|
|
140
|
+
if status:
|
|
141
|
+
adgroup_data["Status"] = status
|
|
142
|
+
|
|
143
|
+
if extra_json:
|
|
144
|
+
extra = json.loads(extra_json)
|
|
145
|
+
adgroup_data.update(extra)
|
|
146
|
+
|
|
147
|
+
body = {"method": "update", "params": {"AdGroups": [adgroup_data]}}
|
|
148
|
+
|
|
149
|
+
if dry_run:
|
|
150
|
+
format_output(body, "json", None)
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
client = create_client(
|
|
154
|
+
token=ctx.obj.get("token"),
|
|
155
|
+
login=ctx.obj.get("login"),
|
|
156
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
result = client.adgroups().post(data=body)
|
|
160
|
+
format_output(result().extract(), "json", None)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
print_error(str(e))
|
|
164
|
+
raise click.Abort()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@adgroups.command()
|
|
168
|
+
@click.option("--id", "adgroup_id", required=True, type=int, help="Ad group ID")
|
|
169
|
+
@click.pass_context
|
|
170
|
+
def delete(ctx, adgroup_id):
|
|
171
|
+
"""Delete ad group"""
|
|
172
|
+
try:
|
|
173
|
+
client = create_client(
|
|
174
|
+
token=ctx.obj.get("token"),
|
|
175
|
+
login=ctx.obj.get("login"),
|
|
176
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
body = {
|
|
180
|
+
"method": "delete",
|
|
181
|
+
"params": {"SelectionCriteria": {"Ids": [adgroup_id]}},
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
result = client.adgroups().post(data=body)
|
|
185
|
+
format_output(result().extract(), "json", None)
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
print_error(str(e))
|
|
189
|
+
raise click.Abort()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AdImages commands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ..api import create_client
|
|
8
|
+
from ..output import format_output, print_error
|
|
9
|
+
from ..utils import parse_ids
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def adimages():
|
|
14
|
+
"""Manage ad images"""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@adimages.command()
|
|
19
|
+
@click.option("--ids", help="Comma-separated image IDs")
|
|
20
|
+
@click.option("--limit", type=int, help="Limit number of results")
|
|
21
|
+
@click.option("--fetch-all", is_flag=True, help="Fetch all pages")
|
|
22
|
+
@click.option("--format", "output_format", default="json", help="Output format")
|
|
23
|
+
@click.option("--output", help="Output file")
|
|
24
|
+
@click.option("--fields", help="Comma-separated field names")
|
|
25
|
+
@click.pass_context
|
|
26
|
+
def get(ctx, ids, limit, fetch_all, output_format, output, fields):
|
|
27
|
+
"""Get ad images"""
|
|
28
|
+
try:
|
|
29
|
+
client = create_client(
|
|
30
|
+
token=ctx.obj.get("token"),
|
|
31
|
+
login=ctx.obj.get("login"),
|
|
32
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
field_names = (
|
|
36
|
+
fields.split(",") if fields else ["Id", "Name", "Status", "AdImageHash"]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
criteria = {}
|
|
40
|
+
if ids:
|
|
41
|
+
criteria["Ids"] = parse_ids(ids)
|
|
42
|
+
|
|
43
|
+
params = {"SelectionCriteria": criteria, "FieldNames": field_names}
|
|
44
|
+
|
|
45
|
+
if limit:
|
|
46
|
+
params["Page"] = {"Limit": limit}
|
|
47
|
+
|
|
48
|
+
body = {"method": "get", "params": params}
|
|
49
|
+
|
|
50
|
+
result = client.adimages().post(data=body)
|
|
51
|
+
|
|
52
|
+
if fetch_all:
|
|
53
|
+
items = []
|
|
54
|
+
for item in result().iter_items():
|
|
55
|
+
items.append(item)
|
|
56
|
+
format_output(items, output_format, output)
|
|
57
|
+
else:
|
|
58
|
+
data = result().extract()
|
|
59
|
+
format_output(data, output_format, output)
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
print_error(str(e))
|
|
63
|
+
raise click.Abort()
|