Skip to content

Commit ad4442f

Browse files
committed
upgrade to version 11.12.2
1 parent 03f0a8b commit ad4442f

File tree

64 files changed

+2892
-67
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2892
-67
lines changed

HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
.. :changelog:
22
33
Release History
4+
-----------
5+
11.12.2(2018-05-15)
6+
+++++++++++++++++++
7+
*To extend support for [Microsoft Audience Ads](https://docs.microsoft.com/en-us/bingads/guides/audience-ads), new bulk objects are added to the SDK for reading and writing Bulk file records e.g., BulkResponsiveAd and BulkResponsiveAdLabel.
8+
*Added retries for the 117 throttling error if encountered while polling for the status of a bulk or reporting operation.
49
510
---------------
611
11.12.1(2018-04-12)

bingads/manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
VERSION = '11.12.1'
2+
VERSION = '11.12.2'
33
BULK_FORMAT_VERSION_5 = '5.0'
44
BULK_FORMAT_VERSION_6 = '6.0'
55
WORKING_NAME = 'BingAdsSDKPython'

bingads/util.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import time
22
from bingads.exceptions import TimeoutException
3+
from suds.client import WebFault
34

45
class _TimeHelper(object):
56
"""
@@ -44,4 +45,19 @@ def wait(self):
4445
if self._status_update_count >= _PollingBlocker.NUMBER_OF_INITIAL_STATUS_CHECKS:
4546
time.sleep(self._poll_interval_in_milliseconds / 1000.0)
4647
else:
47-
time.sleep(_PollingBlocker.INITIAL_STATUS_CHECK_INTERVAL_IN_MS / 1000.0)
48+
time.sleep(_PollingBlocker.INITIAL_STATUS_CHECK_INTERVAL_IN_MS / 1000.0)
49+
50+
51+
ratelimit_retry_duration=[15, 20, 25, 30]
52+
def errorcode_of_exception(ex):
53+
if isinstance(ex, WebFault):
54+
if hasattr(ex.fault, 'detail') \
55+
and hasattr(ex.fault.detail, 'AdApiFaultDetail') \
56+
and hasattr(ex.fault.detail.AdApiFaultDetail, 'Errors') \
57+
and hasattr(ex.fault.detail.AdApiFaultDetail.Errors, 'AdApiError'):
58+
ad_api_errors = ex.fault.detail.AdApiFaultDetail.Errors.AdApiError
59+
if type(ad_api_errors) == list:
60+
return ad_api_erros[0].Code
61+
else:
62+
return ad_api_errors.Code
63+
return -1

bingads/v11/bulk/bulk_operation.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .bulk_operation_status import *
1313
from .bulk_operation_progress_info import *
1414
from .exceptions import *
15-
from bingads.util import _PollingBlocker
15+
from bingads.util import _PollingBlocker, errorcode_of_exception, ratelimit_retry_duration
1616
from bingads.exceptions import *
1717

1818
from bingads.service_client import ServiceClient
@@ -239,7 +239,7 @@ def get_status(self):
239239

240240
if self.final_status is not None:
241241
return self.final_status
242-
response = self._get_status_with_retry(3)
242+
response = self._get_status_with_retry(4)
243243
headers=self.service_client.hp.get_headers(self.service_client.soap_client.service.GetBulkDownloadStatus)
244244
self.tracking_id = headers['TrackingId'] if 'TrackingId' in headers else None
245245
status = BulkOperationStatus(
@@ -265,9 +265,12 @@ def _get_status_with_retry(self, retry_times):
265265
while retry_times > 1:
266266
try:
267267
return self.service_client.GetBulkDownloadStatus(RequestId=self.request_id)
268-
except Exception:
268+
except Exception as ex:
269269
retry_times -= 1
270-
time.sleep(1000)
270+
if '117' == errorcode_of_exception(ex):
271+
time.sleep(ratelimit_retry_duration[3 - retry_times])
272+
else:
273+
time.sleep(1)
271274
return self.service_client.GetBulkDownloadStatus(RequestId=self.request_id)
272275

273276

@@ -335,7 +338,7 @@ def get_status(self):
335338

336339
if self.final_status is not None:
337340
return self.final_status
338-
response = self._get_status_with_retry(3)
341+
response = self._get_status_with_retry(4)
339342
headers=self.service_client.hp.get_headers(self.service_client.soap_client.service.GetBulkUploadStatus)
340343
self.tracking_id = headers['TrackingId'] if 'TrackingId' in headers else None
341344
status = BulkOperationStatus(
@@ -359,11 +362,15 @@ def get_status(self):
359362
self._final_status = status
360363
return status
361364

365+
362366
def _get_status_with_retry(self, retry_times):
363367
while retry_times > 1:
364368
try:
365369
return self.service_client.GetBulkUploadStatus(RequestId=self.request_id)
366-
except Exception:
370+
except Exception as ex:
367371
retry_times -= 1
368-
time.sleep(1000)
372+
if '117' == errorcode_of_exception(ex):
373+
time.sleep(ratelimit_retry_duration[3 - retry_times])
374+
else:
375+
time.sleep(1)
369376
return self.service_client.GetBulkUploadStatus(RequestId=self.request_id)

bingads/v11/bulk/entities/ad_extensions/bulk_site_links_ad_extensions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def order(self, value):
127127
def site_link(self):
128128
""" The sitelink.
129129
130-
See SiteLink at: https://msdn.microsoft.com/en-US/library/jj134381.aspx
130+
See SiteLink at: https://docs.microsoft.com/en-us/bingads/campaign-management-service/sitelink?view=bingads-11
131131
"""
132132

133133
return self._site_link

bingads/v11/bulk/entities/audiences/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
from .bulk_in_market_audience import *
1111
from .bulk_ad_group_in_market_audience_association import *
1212
from .bulk_ad_group_negative_in_market_audience_association import *
13+
from .bulk_ad_group_product_audience_association import *
14+
from .bulk_ad_group_negative_product_audience_association import *
15+
from .bulk_product_audience import *
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from bingads.v11.bulk.entities import *
2+
from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V11
3+
from bingads.v11.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity
4+
from bingads.v11.internal.bulk.mappings import _SimpleBulkMapping
5+
from bingads.v11.internal.bulk.string_table import _StringTable
6+
from bingads.v11.internal.extensions import *
7+
8+
9+
class BulkAdGroupNegativeProductAudienceAssociation(_SingleRecordBulkEntity):
10+
""" Represents an Ad Group Negative Product Audience Association that can be read or written in a bulk file.
11+
12+
This class exposes the :attr:`negative_ad_group_criterion` property that can be read and written as fields of the
13+
Ad Group Negative Product Audience Association record in a bulk file.
14+
15+
For more information, see Ad Group Negative Product Audience Association at https://go.microsoft.com/fwlink/?linkid=846127.
16+
17+
*See also:*
18+
19+
* :class:`.BulkServiceManager`
20+
* :class:`.BulkOperation`
21+
* :class:`.BulkFileReader`
22+
* :class:`.BulkFileWriter`
23+
"""
24+
25+
def __init__(self,
26+
negative_ad_group_criterion=None,
27+
campaign_name=None,
28+
ad_group_name=None,
29+
product_audience_name=None):
30+
super(BulkAdGroupNegativeProductAudienceAssociation, self).__init__()
31+
32+
self._negative_ad_group_criterion = negative_ad_group_criterion
33+
self._campaign_name = campaign_name
34+
self._ad_group_name = ad_group_name
35+
self._product_audience_name = product_audience_name
36+
37+
_MAPPINGS = [
38+
_SimpleBulkMapping(
39+
_StringTable.Status,
40+
field_to_csv=lambda c: bulk_str(c.negative_ad_group_criterion.Status),
41+
csv_to_field=lambda c, v: setattr(c.negative_ad_group_criterion, 'Status', v if v else None)
42+
),
43+
_SimpleBulkMapping(
44+
_StringTable.Id,
45+
field_to_csv=lambda c: bulk_str(c.negative_ad_group_criterion.Id),
46+
csv_to_field=lambda c, v: setattr(c.negative_ad_group_criterion, 'Id', int(v) if v else None)
47+
),
48+
_SimpleBulkMapping(
49+
_StringTable.ParentId,
50+
field_to_csv=lambda c: bulk_str(c.negative_ad_group_criterion.AdGroupId),
51+
csv_to_field=lambda c, v: setattr(c.negative_ad_group_criterion, 'AdGroupId', int(v) if v else None)
52+
),
53+
_SimpleBulkMapping(
54+
_StringTable.Campaign,
55+
field_to_csv=lambda c: c.campaign_name,
56+
csv_to_field=lambda c, v: setattr(c, 'campaign_name', v)
57+
),
58+
_SimpleBulkMapping(
59+
_StringTable.AdGroup,
60+
field_to_csv=lambda c: c.ad_group_name,
61+
csv_to_field=lambda c, v: setattr(c, 'ad_group_name', v)
62+
),
63+
_SimpleBulkMapping(
64+
_StringTable.Audience,
65+
field_to_csv=lambda c: c.product_audience_name,
66+
csv_to_field=lambda c, v: setattr(c, 'product_audience_name', v)
67+
),
68+
_SimpleBulkMapping(
69+
_StringTable.AudienceId,
70+
field_to_csv=lambda c: field_to_csv_CriterionAudienceId(c.negative_ad_group_criterion),
71+
csv_to_field=lambda c, v: csv_to_field_CriterionAudienceId(c.negative_ad_group_criterion, int(v) if v else None)
72+
),
73+
]
74+
75+
@property
76+
def negative_ad_group_criterion(self):
77+
""" Defines a Negative Ad Group Criterion """
78+
79+
return self._negative_ad_group_criterion
80+
81+
@negative_ad_group_criterion.setter
82+
def negative_ad_group_criterion(self, negative_ad_group_criterion):
83+
self._negative_ad_group_criterion = negative_ad_group_criterion
84+
85+
@property
86+
def campaign_name(self):
87+
""" Defines the name of the Campaign.
88+
89+
:rtype: str
90+
"""
91+
92+
return self._campaign_name
93+
94+
@campaign_name.setter
95+
def campaign_name(self, campaign_name):
96+
self._campaign_name = campaign_name
97+
98+
@property
99+
def ad_group_name(self):
100+
""" Defines the name of the Ad Group
101+
102+
:rtype: str
103+
"""
104+
105+
return self._ad_group_name
106+
107+
@ad_group_name.setter
108+
def ad_group_name(self, ad_group_name):
109+
self._ad_group_name = ad_group_name
110+
111+
@property
112+
def product_audience_name(self):
113+
""" Defines the name of the Product Audience
114+
115+
:rtype: str
116+
"""
117+
118+
return self._product_audience_name
119+
120+
@product_audience_name.setter
121+
def product_audience_name(self, product_audience_name):
122+
self._product_audience_name = product_audience_name
123+
124+
def process_mappings_from_row_values(self, row_values):
125+
self._negative_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V11.create('NegativeAdGroupCriterion')
126+
self._negative_ad_group_criterion.Type = 'NegativeAdGroupCriterion'
127+
self._negative_ad_group_criterion.Criterion = _CAMPAIGN_OBJECT_FACTORY_V11.create('AudienceCriterion')
128+
self._negative_ad_group_criterion.Criterion.Type = 'AudienceCriterion'
129+
row_values.convert_to_entity(self, BulkAdGroupNegativeProductAudienceAssociation._MAPPINGS)
130+
131+
def process_mappings_to_row_values(self, row_values, exclude_readonly_data):
132+
self._validate_property_not_null(self.negative_ad_group_criterion, 'negative_ad_group_criterion')
133+
self.convert_to_values(row_values, BulkAdGroupNegativeProductAudienceAssociation._MAPPINGS)
134+
135+
def read_additional_data(self, stream_reader):
136+
super(BulkAdGroupNegativeProductAudienceAssociation, self).read_additional_data(stream_reader)

0 commit comments

Comments
 (0)