Skip to content

Commit cf252ed

Browse files
authored
Add annotations to examples/reporting (#951)
1 parent d3c9cdf commit cf252ed

File tree

1 file changed

+53
-24
lines changed

1 file changed

+53
-24
lines changed

examples/reporting/parallel_report_download.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,31 @@
2323
from itertools import product
2424
import multiprocessing
2525
import time
26+
from typing import Any, Dict, Iterable, List, Tuple
2627

2728
from google.ads.googleads.client import GoogleAdsClient
2829
from google.ads.googleads.errors import GoogleAdsException
30+
from google.ads.googleads.v20.errors.types import (
31+
ErrorLocation,
32+
GoogleAdsError,
33+
)
34+
from google.ads.googleads.v20.services.services.google_ads_service import (
35+
GoogleAdsServiceClient,
36+
)
37+
from google.ads.googleads.v20.services.types import (
38+
GoogleAdsRow,
39+
SearchGoogleAdsStreamResponse,
40+
)
2941

3042
# Maximum number of processes to spawn.
31-
MAX_PROCESSES = multiprocessing.cpu_count()
43+
MAX_PROCESSES: int = multiprocessing.cpu_count()
3244
# Timeout between retries in seconds.
33-
BACKOFF_FACTOR = 5
45+
BACKOFF_FACTOR: int = 5
3446
# Maximum number of retries for errors.
35-
MAX_RETRIES = 5
47+
MAX_RETRIES: int = 5
3648

3749

38-
def main(client, customer_ids):
50+
def main(client: GoogleAdsClient, customer_ids: List[str]) -> None:
3951
"""The main method that creates all necessary entities for the example.
4052
4153
Args:
@@ -44,26 +56,29 @@ def main(client, customer_ids):
4456
"""
4557

4658
# Define the GAQL query strings to run for each customer ID.
47-
campaign_query = """
59+
campaign_query: str = """
4860
SELECT campaign.id, metrics.impressions, metrics.clicks
4961
FROM campaign
5062
WHERE segments.date DURING LAST_30_DAYS"""
51-
ad_group_query = """
63+
ad_group_query: str = """
5264
SELECT campaign.id, ad_group.id, metrics.impressions, metrics.clicks
5365
FROM ad_group
5466
WHERE segments.date DURING LAST_30_DAYS"""
5567

56-
inputs = generate_inputs(
68+
inputs: Iterable[Tuple[GoogleAdsClient, str, str]] = generate_inputs(
5769
client, customer_ids, [campaign_query, ad_group_query]
5870
)
5971
with multiprocessing.Pool(MAX_PROCESSES) as pool:
6072
# Call issue_search_request on each input, parallelizing the work
6173
# across processes in the pool.
62-
results = pool.starmap(issue_search_request, inputs)
74+
results: List[Tuple[bool, Dict[str, Any]]] = pool.starmap(
75+
issue_search_request, inputs
76+
)
6377

6478
# Partition our results into successful and failed results.
65-
successes = []
66-
failures = []
79+
successes: List[Dict[str, Any]] = []
80+
failures: List[Dict[str, Any]] = []
81+
res: Tuple[bool, Dict[str, Any]]
6782
for res in results:
6883
if res[0]:
6984
successes.append(res[1])
@@ -77,31 +92,37 @@ def main(client, customer_ids):
7792
)
7893

7994
print("Successes:") if len(successes) else None
95+
success: Dict[str, Any]
8096
for success in successes:
8197
# success["results"] represents an array of result strings for one
8298
# customer ID / query combination.
83-
result_str = "\n".join(success["results"])
99+
result_str: str = "\n".join(success["results"])
84100
print(result_str)
85101

86102
print("Failures:") if len(failures) else None
103+
failure: Dict[str, Any]
87104
for failure in failures:
88-
ex = failure["exception"]
105+
ex: GoogleAdsException = failure["exception"]
89106
print(
90107
f'Request with ID "{ex.request_id}" failed with status '
91108
f'"{ex.error.code().name}" for customer_id '
92109
f'{failure["customer_id"]} and query "{failure["query"]}" and '
93110
"includes the following errors:"
94111
)
112+
error: GoogleAdsError
95113
for error in ex.failure.errors:
96114
print(f'\tError with message "{error.message}".')
97115
if error.location:
116+
field_path_element: ErrorLocation.FieldPathElement
98117
for (
99118
field_path_element
100119
) in error.location.field_path_elements:
101120
print(f"\t\tOn field: {field_path_element.field_name}")
102121

103122

104-
def issue_search_request(client, customer_id, query):
123+
def issue_search_request(
124+
client: GoogleAdsClient, customer_id: str, query: str
125+
) -> Tuple[bool, Dict[str, Any]]:
105126
"""Issues a search request using streaming.
106127
107128
Retries if a GoogleAdsException is caught, until MAX_RETRIES is reached.
@@ -111,27 +132,29 @@ def issue_search_request(client, customer_id, query):
111132
customer_id: a client customer ID str.
112133
query: a GAQL query str.
113134
"""
114-
ga_service = client.get_service("GoogleAdsService")
115-
retry_count = 0
135+
ga_service: GoogleAdsServiceClient = client.get_service("GoogleAdsService")
136+
retry_count: int = 0
116137
# Retry until we've reached MAX_RETRIES or have successfully received a
117138
# response.
118139
while True:
119140
try:
120-
stream = ga_service.search_stream(
121-
customer_id=customer_id, query=query
141+
stream: Iterable[SearchGoogleAdsStreamResponse] = (
142+
ga_service.search_stream(customer_id=customer_id, query=query)
122143
)
123144
# Returning a list of GoogleAdsRows will result in a
124145
# PicklingError, so instead we put the GoogleAdsRow data
125146
# into a list of str results and return that.
126-
result_strings = []
147+
result_strings: List[str] = []
148+
batch: SearchGoogleAdsStreamResponse
127149
for batch in stream:
150+
row: GoogleAdsRow
128151
for row in batch.results:
129-
ad_group_id = (
152+
ad_group_id: str = (
130153
f"Ad Group ID {row.ad_group.id} in "
131154
if "ad_group.id" in query
132155
else ""
133156
)
134-
result_string = (
157+
result_string: str = (
135158
f"{ad_group_id}"
136159
f"Campaign ID {row.campaign.id} "
137160
f"had {row.metrics.impressions} impressions "
@@ -157,7 +180,11 @@ def issue_search_request(client, customer_id, query):
157180
)
158181

159182

160-
def generate_inputs(client, customer_ids, queries):
183+
def generate_inputs(
184+
client: GoogleAdsClient,
185+
customer_ids: List[str],
186+
queries: List[str],
187+
) -> Iterable[Tuple[GoogleAdsClient, str, str]]:
161188
"""Generates all inputs to feed into search requests.
162189
163190
A GoogleAdsService instance cannot be serialized with pickle for parallel
@@ -173,7 +200,7 @@ def generate_inputs(client, customer_ids, queries):
173200

174201

175202
if __name__ == "__main__":
176-
parser = argparse.ArgumentParser(
203+
parser: argparse.ArgumentParser = argparse.ArgumentParser(
177204
description="Download a set of reports in parallel from a list of "
178205
"accounts."
179206
)
@@ -192,11 +219,13 @@ def generate_inputs(client, customer_ids, queries):
192219
type=str,
193220
help="The login customer ID (optional).",
194221
)
195-
args = parser.parse_args()
222+
args: argparse.Namespace = parser.parse_args()
196223

197224
# GoogleAdsClient will read the google-ads.yaml configuration file in the
198225
# home directory if none is specified.
199-
googleads_client = GoogleAdsClient.load_from_storage(version="v20")
226+
googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(
227+
version="v20"
228+
)
200229
# Override the login_customer_id on the GoogleAdsClient, if specified.
201230
if args.login_customer_id is not None:
202231
googleads_client.login_customer_id = args.login_customer_id

0 commit comments

Comments
 (0)