Skip to content

Commit 811391d

Browse files
committed
Fixes issue #'s 6 & 9
1 parent 2c60e70 commit 811391d

File tree

12 files changed

+166
-101
lines changed

12 files changed

+166
-101
lines changed

ChangeLog

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
1.0.5 - 05/07/2014
2+
* Fixed an issue where PQL fields weren't encoding properly / not handling None
3+
responses correctly. This resolves issue #9.
4+
* Fixed an issue where the AdWords ReportDownloader would fail when downloading
5+
gzipped reports. Now better documents the need to use binary files in this
6+
case. This resolves issue #6.
7+
* Fixed an artifact from issue #5, ReportDownloader now sets https_proxy before
8+
retrieving the WSDL.
9+
110
1.0.4 - 04/22/2014
211
* Fixed a bug where https_proxy wasn't set for retrieving the API WSDL. This
312
resolves issue #5.

examples/dfp/v201403/report_service/run_delivery_report.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,21 @@ def main(client, order_id):
6363
report_job_id = report_downloader.WaitForReport(report_job)
6464
except errors.DfpReportError, e:
6565
print 'Failed to generate report. Error was: %s' % e
66-
else:
67-
# Change to your preferred export format.
68-
export_format = 'CSV_DUMP'
6966

70-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
67+
# Change to your preferred export format.
68+
export_format = 'CSV_DUMP'
7169

72-
# Download report data.
73-
report_downloader.DownloadReportToFile(
74-
report_job_id, export_format, report_file)
70+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
7571

76-
report_file.close()
72+
# Download report data.
73+
report_downloader.DownloadReportToFile(
74+
report_job_id, export_format, report_file)
7775

78-
# Display results.
79-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
80-
report_job['id'], report_file.name)
76+
report_file.close()
77+
78+
# Display results.
79+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
80+
report_job_id, report_file.name)
8181

8282
if __name__ == '__main__':
8383
# Initialize client object.

examples/dfp/v201403/report_service/run_inventory_report.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,21 @@ def main(client):
6969
report_job_id = report_downloader.WaitForReport(report_job)
7070
except errors.DfpReportError, e:
7171
print 'Failed to generate report. Error was: %s' % e
72-
else:
73-
# Change to your preferred export format.
74-
export_format = 'CSV_DUMP'
7572

76-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
73+
# Change to your preferred export format.
74+
export_format = 'CSV_DUMP'
7775

78-
# Download report data.
79-
report_downloader.DownloadReportToFile(
80-
report_job_id, export_format, report_file)
76+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
8177

82-
report_file.close()
78+
# Download report data.
79+
report_downloader.DownloadReportToFile(
80+
report_job_id, export_format, report_file)
8381

84-
# Display results.
85-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
86-
report_job['id'], report_file.name)
82+
report_file.close()
83+
84+
# Display results.
85+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
86+
report_job_id, report_file.name)
8787

8888
if __name__ == '__main__':
8989
# Initialize client object.

examples/dfp/v201403/report_service/run_merged_delivery_report.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,21 @@ def main(client):
4848
report_job_id = report_downloader.WaitForReport(report_job)
4949
except errors.DfpReportError, e:
5050
print 'Failed to generate report. Error was: %s' % e
51-
else:
52-
# Change to your preferred export format.
53-
export_format = 'CSV_DUMP'
5451

55-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
52+
# Change to your preferred export format.
53+
export_format = 'CSV_DUMP'
5654

57-
# Download report data.
58-
report_downloader.DownloadReportToFile(
59-
report_job_id, export_format, report_file)
55+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
6056

61-
report_file.close()
57+
# Download report data.
58+
report_downloader.DownloadReportToFile(
59+
report_job_id, export_format, report_file)
6260

63-
# Display results.
64-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
65-
report_job['id'], report_file.name)
61+
report_file.close()
62+
63+
# Display results.
64+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
65+
report_job_id, report_file.name)
6666

6767
if __name__ == '__main__':
6868
# Initialize client object.

examples/dfp/v201403/report_service/run_reach_report.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ def main(client):
4343
report_job_id = report_downloader.WaitForReport(report_job)
4444
except errors.DfpReportError, e:
4545
print 'Failed to generate report. Error was: %s' % e
46-
else:
47-
# Change to your preferred export format.
48-
export_format = 'CSV_DUMP'
4946

50-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
47+
# Change to your preferred export format.
48+
export_format = 'CSV_DUMP'
5149

52-
# Download report data.
53-
report_downloader.DownloadReportToFile(
54-
report_job_id, export_format, report_file)
50+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
5551

56-
report_file.close()
52+
# Download report data.
53+
report_downloader.DownloadReportToFile(
54+
report_job_id, export_format, report_file)
5755

58-
# Display results.
59-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
60-
report_job_id, report_file.name)
56+
report_file.close()
57+
58+
# Display results.
59+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
60+
report_job_id, report_file.name)
6161

6262

6363
if __name__ == '__main__':

examples/dfp/v201403/report_service/run_report_with_custom_fields.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,21 @@ def main(client, order_id):
8585
report_job_id = report_downloader.WaitForReport(report_job)
8686
except errors.DfpReportError, e:
8787
print 'Failed to generate report. Error was: %s' % e
88-
else:
89-
# Change to your preferred export format.
90-
export_format = 'CSV_DUMP'
9188

92-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
89+
# Change to your preferred export format.
90+
export_format = 'CSV_DUMP'
9391

94-
# Download report data.
95-
report_downloader.DownloadReportToFile(
96-
report_job_id, export_format, report_file)
92+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
9793

98-
report_file.close()
94+
# Download report data.
95+
report_downloader.DownloadReportToFile(
96+
report_job_id, export_format, report_file)
9997

100-
# Display results.
101-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
102-
report_job['id'], report_file.name)
98+
report_file.close()
99+
100+
# Display results.
101+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
102+
report_job_id, report_file.name)
103103

104104
if __name__ == '__main__':
105105
# Initialize client object.

examples/dfp/v201403/report_service/run_sales_report.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@ def main(client):
4545
report_job_id = report_downloader.WaitForReport(report_job)
4646
except errors.DfpReportError, e:
4747
print 'Failed to generate report. Error was: %s' % e
48-
else:
49-
# Change to your preferred export format.
50-
export_format = 'CSV_DUMP'
5148

52-
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
49+
# Change to your preferred export format.
50+
export_format = 'CSV_DUMP'
5351

54-
# Download report data.
55-
report_downloader.DownloadReportToFile(
56-
report_job_id, export_format, report_file)
52+
report_file = tempfile.NamedTemporaryFile(suffix='.csv.gz', delete=False)
5753

58-
report_file.close()
54+
# Download report data.
55+
report_downloader.DownloadReportToFile(
56+
report_job_id, export_format, report_file)
5957

60-
# Display results.
61-
print 'Report job with id \'%s\' downloaded to:\n%s' % (
62-
report_job['id'], report_file.name)
58+
report_file.close()
59+
60+
# Display results.
61+
print 'Report job with id \'%s\' downloaded to:\n%s' % (
62+
report_job_id, report_file.name)
6363

6464
if __name__ == '__main__':
6565
# Initialize client object.

googleads/adwords.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,16 @@ def __init__(self, adwords_client, version=sorted(_SERVICE_MAP.keys())[-1],
319319
self._end_point = self._END_POINT_FORMAT % (server, version)
320320
self._header_handler = _AdWordsHeaderHandler(adwords_client, version)
321321

322+
proxy_option = None
323+
if self._adwords_client.https_proxy:
324+
proxy_option = {'https': self._adwords_client.https_proxy}
325+
322326
schema_url = self._SCHEMA_FORMAT % (server, version)
323327
schema = suds.client.Client(
324328
schema_url,
325329
doctor=suds.xsd.doctor.ImportDoctor(suds.xsd.doctor.Import(
326-
self._namespace, schema_url))).wsdl.schema
330+
self._namespace, schema_url)),
331+
proxy=proxy_option).wsdl.schema
327332
self._report_definition_type = schema.elements[
328333
(self._REPORT_DEFINITION_NAME, self._namespace)]
329334
self._marshaller = suds.mx.literal.Literal(schema)
@@ -340,16 +345,25 @@ def DownloadReport(self, report_definition, output=sys.stdout,
340345
that will be downloaded.
341346
[optional]
342347
output: A writable object where the contents of the report will be written
343-
to.
348+
to. If the report is gzip compressed, you need to specify an output
349+
that can write binary data.
344350
return_money_in_micros: A boolean indicating whether money should be
345351
represented as micros in reports. If None is supplied the AdWords
346352
server will use its default value, which is currently True.
347353
348354
Raises:
349-
An AdWordsReportBadRequestError if the report download fails due to
350-
improper input. If the request fails for any other reason (for example,
351-
a network error), an AdWordsReportError will be raised instead.
355+
AdWordsReportBadRequestError: if the report download fails due to
356+
improper input.
357+
GoogleAdsValueError: if the user-specified report format is incompatible
358+
with the output.
359+
AdWordsReportError: if the request fails for any other reason; e.g. a
360+
network error.
352361
"""
362+
if (report_definition['downloadFormat'].startswith('GZIPPED_')
363+
and getattr(output, 'mode', 'w') != 'wb'):
364+
raise googleads.errors.GoogleAdsValueError('Need to specify a binary'
365+
' output for GZIPPED formats.')
366+
353367
self._DownloadReport(self._SerializeReportDefinition(report_definition),
354368
output, return_money_in_micros)
355369

@@ -367,16 +381,25 @@ def DownloadReportWithAwql(self, query, file_format, output=sys.stdout,
367381
https://developers.google.com/adwords/api/docs/guides/reporting
368382
[optional]
369383
output: A writable object where the contents of the report will be written
370-
to.
384+
to. If the report is gzip compressed, you need to specify an output
385+
that can write binary data.
371386
return_money_in_micros: A boolean indicating whether money should be
372387
represented as micros in reports. If None is supplied the AdWords
373388
server will use its default value, which is currently True.
374389
375390
Raises:
376-
An AdWordsReportBadRequestError if the report download fails due to
377-
improper input. If the request fails for any other reason (for example,
378-
a network error), an AdWordsReportError will be raised instead.
391+
AdWordsReportBadRequestError: if the report download fails due to
392+
improper input.
393+
GoogleAdsValueError: if the user-specified report format is incompatible
394+
with the output.
395+
AdWordsReportError: if the request fails for any other reason; e.g. a
396+
network error.
379397
"""
398+
if (file_format.startswith('GZIPPED_')
399+
and getattr(output, 'mode', 'w') != 'wb'):
400+
raise googleads.errors.GoogleAdsValueError('Need to specify a binary'
401+
' output for GZIPPED formats.')
402+
380403
self._DownloadReport(self._SerializeAwql(query, file_format), output,
381404
return_money_in_micros)
382405

@@ -393,11 +416,12 @@ def _DownloadReport(self, post_body, output, return_money_in_micros):
393416
server will use its default value, which is currently True.
394417
395418
Raises:
396-
An AdWordsReportBadRequestError if the report download fails due to
397-
improper input. If the HTTP request fails with any other status code, an
398-
AdWordsReportError will be raised instead. In the event of certain other
399-
failures, a urllib2.URLError (Python 2) or urllib.error.URLError
400-
(Python 3) will be raised.
419+
AdWordsReportBadRequestError: if the report download fails due to
420+
improper input. In the event of certain other failures, a
421+
urllib2.URLError (Python 2) or urllib.error.URLError (Python 3) will be
422+
raised.
423+
AdWordsReportError: if the request fails for any other reason; e.g. a
424+
network error.
401425
"""
402426
if sys.version_info[0] == 3:
403427
post_body = bytes(post_body, 'utf8')
@@ -414,7 +438,8 @@ def _DownloadReport(self, post_body, output, return_money_in_micros):
414438
while True:
415439
chunk = response.read(_CHUNK_SIZE)
416440
if not chunk: break
417-
output.write(chunk.decode() if sys.version_info[0] == 3 else chunk)
441+
output.write(chunk.decode() if sys.version_info[0] == 3
442+
and getattr(output, 'mode', 'w') == 'w' else chunk)
418443

419444
def _SerializeAwql(self, query, file_format):
420445
"""Serializes an AWQL query and file format for transport.

googleads/common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import googleads.errors
2727
import googleads.oauth2
2828

29-
VERSION = '1.0.4'
29+
VERSION = '1.0.5'
3030
_COMMON_LIB_SIG = 'googleads/%s' % VERSION
3131
_PYTHON_VERSION = 'Python/%d.%d' % (sys.version_info[0], sys.version_info[1])
3232

googleads/dfp.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -375,18 +375,21 @@ def _ConvertValueForCsv(self, pql_value):
375375
"""
376376
field = pql_value['value']
377377

378-
if pql_value['Value.Type'] == 'TextValue':
379-
return field
380-
elif pql_value['Value.Type'] == 'NumberValue':
381-
return float(field) if '.' in field else int(field)
382-
elif pql_value['Value.Type'] == 'DateTimeValue':
383-
return self._ConvertDateTimeToOffset(field)
384-
elif pql_value['Value.Type'] == 'DateValue':
385-
return datetime.date(int(field['date']['year']),
386-
int(field['date']['month']),
387-
int(field['date']['day'])).isoformat()
378+
if field:
379+
if pql_value['Value.Type'] == 'TextValue':
380+
return field.encode('UTF8')
381+
elif pql_value['Value.Type'] == 'NumberValue':
382+
return float(field) if '.' in field else int(field)
383+
elif pql_value['Value.Type'] == 'DateTimeValue':
384+
return self._ConvertDateTimeToOffset(field)
385+
elif pql_value['Value.Type'] == 'DateValue':
386+
return datetime.date(int(field['date']['year']),
387+
int(field['date']['month']),
388+
int(field['date']['day'])).isoformat()
389+
else:
390+
return field
388391
else:
389-
return field
392+
return '-'
390393

391394
def _PageThroughPqlSet(self, pql_query, output_function, values):
392395
"""Pages through a pql_query and performs an action (output_function).

0 commit comments

Comments
 (0)