Metadata-Version: 2.1
Name: adsctl
Version: 0.4.1
Summary: Google Ads Control CLI and Prompt
Project-URL: Documentation, https://github.com/danielfrg/adsctl#readme
Project-URL: Issues, https://github.com/danielfrg/adsctl/issues
Project-URL: Source, https://github.com/danielfrg/adsctl
Author-email: Daniel Rodriguez <daniel@danielfrg.com>
License-Expression: Apache-2.0
License-File: LICENSE.txt
Keywords: cli,google ads,google ads api,prompt
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
Requires-Dist: click>=8.1
Requires-Dist: google-ads>=20.0.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: pandas>=2.0.0
Requires-Dist: pillow>=9.5.0
Requires-Dist: prompt-toolkit>=3.0.38
Requires-Dist: pydantic<2.0.0,>=1.10.0
Requires-Dist: tabulate>=0.9.0
Requires-Dist: tomli-w>=1.0
Requires-Dist: tomlkit>=0.11.1
Provides-Extra: test
Requires-Dist: coverage[toml]; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Description-Content-Type: text/markdown

# Google Ads API CLI and Prompt

<p align="center">
    <a href="https://pypi.org/project/adsctl/">
        <img src="https://img.shields.io/pypi/v/adsctl.svg">
    </a>
    <a href="https://pypi.org/project/mkdocs-jupyter">
        <img src="https://img.shields.io/pypi/pyversions/adsctl.svg">
    </a>
    <a href="https://github.com/danielfrg/adsctl/actions/workflows/test.yml">
        <img src="https://github.com/danielfrg/adsctl/workflows/test/badge.svg">
    </a>
    </a>
    <a href="https://github.com/danielfrg/adsctl/blob/main/LICENSE.txt">
        <img src="https://img.shields.io/:license-Apache%202-blue.svg">
    </a>
</p>

Google Ads Interface for humans.

_This is a work in progress, please open an issue if you find any bugs or have any suggestions._

Features:

- A command line tool for executing GAQL queries against the Google Ads API.
  Like [psql](https://www.postgresql.org/docs/current/app-psql.html) for the Google Ads API.
- A command line tool for managing Google Ads resources.
  Like [kubectl](https://kubernetes.io/docs/reference/kubectl/) for the Google Ads API.
- Centralized configuration with multiple account management
- Automatically update refresh token
- Python API with Pandas integration

## Installation

```shell
pip install adsctl
```

## Getting started

Requirements:

- All the requirements to use the Google Ads API including a Developer Token and OAuth2 credentials
- See [Google Ads API Quickstart](https://developers.google.com/google-ads/api/docs/first-call/overview) for more details.

### Configuration

This project manages it's own configuration files.
To create the configuration file run:

```shell
adsctl config

# Open the location of the config files
adsctl config explore
```

Open the new default config file and fill it with your credentials:
Dev Token, Client ID, Client Secret and Customer ID.

To login and get a refresh token:

```shell
adsctl auth <path-to-secret.json>
```

The token is saved automatically in the config file.
You can see it by running:

```shell
# View config
adsctl config view
```

### Multiple Accounts

You can manage multiple accounts in the config file by adding TOML sections.

```toml
current_account = "default"

[... default account ...]

[accounts.another]
developer_token = ""
customer_id = ""
login_customer_id = ""

[accounts.another.oauth]
client_id = ""
client_secret = ""
```

Set the current account:

```shell
$ adsctl config set-account another
$ adsctl config get-account
another
```

## GAQL Prompt

An interactive shell for executing GAQL queries against the Google Ads API.

```shell
$ gaql

>>> SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id
+----+-----------------------------+---------+-------------+
|    | resourceName                | name    |          id |
|----+-----------------------------+---------+-------------|
|  0 | customers/XXX/campaigns/YYY | name1   | 10000000000 |
|  1 | customers/XXX/campaigns/YYY | name2   | 10000000000 |
|  2 | customers/XXX/campaigns/YYY | name3   | 10000000000 |
+----+-----------------------------+---------+-------------+
```

By default it uses the it in `table` format but you can control the output
format with the `-o` option:

```shell
# Print the plain protobuf response
$ gaql -o plain

# Print the contents of a CSV file
$ gaql -o csv
```

You can also run a single inline command and redirect the output to a file:

```shell
gaql -c 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id' -o csv > my-query.csv
```

This assumes only table is returned but in more complex queries that include other
resources or when using metrics or segments multiple tables are created.
On those cases use the -o csv-files flag to save each table to a different file
based on the table name.

```shell
$ gaql -c 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id' -o csv-files

$ ls
campaign.csv
```

### Query variables

You can specify one or more variables using the jinja syntax, those
variables will be replaced with the values specified in the `-v` options.

```shell
gaql -c 'SELECT campaign.id, campaign.name FROM campaign WHERE campaign.id = {{ id }} ORDER BY campaign.id' -v id=123456789
```

You can also pass `-v` without a command and use this the variables in the prompt
queries:

```shell
$ gaql -v id=123456789 -v field=name

>>> SELECT campaign.id, campaign.{{ field }} FROM campaign WHERE campaign.id = {{ id }} ORDER BY campaign.id
```

### Other options

You can overwrite the account and customer ID using the `-a` and `-i` options.
See `gaql --help` for more details.

## CLI

### Campaign Management

#### Get campaigns

````shell

```shell
adsctl get campaign
````

```plain
Name                                          Status             Id
--------------------------------------------  --------  -----------
Interplanetary Cruise Campaign #168961427368  Paused    20370195066
Interplanetary Cruise Campaign #168961215970  Paused    20379497161
```

#### Status Management

```shell
adsctl edit campaign -i <campaign-id> status enabled

adsctl edit campaign -i 20370195066 status enabled
```

```shell
adsctl get campaign
```

```plain
Name                                          Status             Id
--------------------------------------------  --------  -----------
Interplanetary Cruise Campaign #168961427368  Enabled   20370195066
Interplanetary Cruise Campaign #168961215970  Paused    20379497161
```

#### Update budget

```shell
adsctl edit campaign -i <campaign-id> budget <amount>
```

## Python API

You can also use the Python API to easily execute GAQL queries
and get the results as a Python dict or pandas DataFrame.

```python
import adsctl as ads

# Read config file and creates the Google Ads client
google_ads = ads.GoogleAds()

# Execute GAQL query
get_campaigns_query = """
SELECT campaign.name,
  campaign_budget.amount_micros,
  campaign.status,
  campaign.optimization_score,
  campaign.advertising_channel_type,
  metrics.clicks,
  metrics.impressions,
  metrics.ctr,
  metrics.average_cpc,
  metrics.cost_micros,
  campaign.bidding_strategy_type
FROM campaign
WHERE segments.date DURING LAST_7_DAYS
  AND campaign.status != '{{ status }}'
"""

tables = google_ads.query(get_campaigns_query, params={"status": "REMOVED"}})

# Print Pandas DataFrames
for table_name, table in tables.items():
    print(table_name)
    print(table, "\n")
```

```plain
campaign
                                 resourceName   status  ...                      name optimizationScore
0  customers/XXXXXXXXXX/campaigns/YYYYYYYYYYY  ENABLED  ...               my-campaign          0.839904
[1 rows x 6 columns]

metrics
  clicks costMicros       ctr    averageCpc impressions
0    210    6730050  0.011457  32047.857143       18330

campaignBudget
                                       resourceName amountMicros
0  customers/XXXXXXXXXX/campaignBudgets/ZZZZZZZZZZZ      1000000
```

## Disclaimer

_This is not an official Google product_.

This repository is maintained by a Googler but is not a supported Google product.
Code and issues here are answered by maintainers and other community members on GitHub on a best-effort basis.
