Skip to content

Commit 1648081

Browse files
authored
Merge pull request BingAds#185 from BingAds/jianyun/13.0.10
release python sdk 13.0.10
2 parents fd6ae78 + c8490a0 commit 1648081

27 files changed

+1431
-196
lines changed

HISTORY.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
.. :changelog:
22
33
Release History
4+
13.0.10(2021-06-20)
5+
+++++++++++++++++++++++++
6+
7+
* Update Bing Ads API Version 13 service proxies to reflect recent interface changes. For details please see the Bing Ads API Release Notes: https://docs.microsoft.com/en-us/bingads/guides/release-notes
8+
* Add new msads.manage scope for multi-factor authentication requirement. Eventually msads.manage will be required. Learn more here: https://go.microsoft.com/fwlink/?linkid=2155062
9+
* Default OAuth scope is set to the new msads.manage scope. Can be overridden temporarily with new oAuthScope parameter (replaces requireLiveConnect).
10+
* Sandbox auth support via login.live-int.com is replaced with login.windows-ppe.net.
11+
* Add BulkVideo for video ads support
12+
* Add mappings for new fields in BulkResponsiveAd: CallToActionLanguage, Videos, Headlines, Descriptions
13+
* Add mappings for new fields in BulkAdGroup: CpvBid, CpmBid
14+
* Update ToBiddingSchemeBulkString(this BiddingScheme biddingScheme) to support MaxRoas, ManualCpv and ManualCpm
15+
* Add mappings for new fields in BulkAccount: AdClickParallelTracking, AutoApplyRecommendations, AllowImageAutoRetrieve
16+
* Conjunctive normal form (CNF) support is added to PageVisitorsRule and mapped in the BulkRemarketingList remarketing rule. Previously Microsoft Advertising only supported disjunctive normal form (DNF). You must ensure that your application can appropriately read and distinguish between CNF and DNF. Your application should no longer assume that the rule is disjunctive.
17+
* Add mapping for new MultimediaAdsBidAdjustment field in BulkCampaign and BulkAdGroup
18+
* Fix issue of DSA setting not being exported for Search Campaign
19+
420
13.0.9.1(2021-04-29)
521
+++++++++++++++++++++++++
622
* Fix issue of missing proxies.

bingads/authorization.py

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
PRODUCTION='production'
1313
SANDBOX='sandbox'
14+
MSADS_MANAGE='msads.manage'
15+
ADS_MANAGE='ads.manage'
16+
BINGADS_MANAGE='bingads.manage'
1417

1518
class AuthorizationData:
1619
""" Represents a user who intends to access the corresponding customer and account.
@@ -285,7 +288,7 @@ class OAuthAuthorization(Authentication):
285288
* :class:`.OAuthWebAuthCodeGrant`
286289
"""
287290

288-
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_connect=False, tenant='common'):
291+
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, oauth_scope=MSADS_MANAGE, tenant='common'):
289292
""" Initializes a new instance of the OAuthAuthorization class.
290293
291294
:param client_id: The client identifier corresponding to your registered application.
@@ -301,7 +304,7 @@ def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_co
301304
self._oauth_tokens = oauth_tokens
302305
self._state = None
303306
self.environment=env
304-
self._require_live_connect=require_live_connect
307+
self._oauth_scope=oauth_scope
305308
self._tenant = tenant
306309

307310
@property
@@ -384,7 +387,7 @@ class OAuthWithAuthorizationCode(OAuthAuthorization):
384387
For more information about registering a Bing Ads application, see http://go.microsoft.com/fwlink/?LinkID=511607.
385388
"""
386389

387-
def __init__(self, client_id, client_secret, redirection_uri, token_refreshed_callback=None, oauth_tokens=None, env=PRODUCTION, require_live_connect=False, tenant="common"):
390+
def __init__(self, client_id, client_secret, redirection_uri, token_refreshed_callback=None, oauth_tokens=None, env=PRODUCTION, oauth_scope=MSADS_MANAGE, tenant="common"):
388391
""" Initialize a new instance of this class.
389392
390393
:param client_id: The client identifier corresponding to your registered application.
@@ -401,7 +404,7 @@ def __init__(self, client_id, client_secret, redirection_uri, token_refreshed_ca
401404
:return:
402405
"""
403406

404-
super(OAuthWithAuthorizationCode, self).__init__(client_id, oauth_tokens=oauth_tokens, env=env, require_live_connect=require_live_connect, tenant=tenant)
407+
super(OAuthWithAuthorizationCode, self).__init__(client_id, oauth_tokens=oauth_tokens, env=env, oauth_scope=oauth_scope, tenant=tenant)
405408
self._client_secret = client_secret
406409
self._redirection_uri = redirection_uri
407410
self._token_refreshed_callback = token_refreshed_callback
@@ -412,8 +415,8 @@ def get_authorization_endpoint(self):
412415
:return: The Microsoft Account authorization endpoint.
413416
:rtype: str
414417
"""
415-
endpoint_url = _UriOAuthService.AUTHORIZE_URI[(self.environment, self._require_live_connect)]
416-
if self.environment == PRODUCTION and self._require_live_connect == False:
418+
endpoint_url = _UriOAuthService.AUTHORIZE_URI[(self.environment, self._oauth_scope)]
419+
if self.environment == PRODUCTION and (self._oauth_scope == MSADS_MANAGE or self._oauth_scope == ADS_MANAGE):
417420
endpoint_url = endpoint_url.replace('common', self.tenant);
418421

419422
endpoint = str.format(
@@ -453,7 +456,7 @@ def request_oauth_tokens_by_response_uri(self, response_uri, **kwargs):
453456
grant_type='authorization_code',
454457
environment=self.environment,
455458
code=code,
456-
requireliveconnect=self._require_live_connect,
459+
oauth_scope=self._oauth_scope,
457460
tenant=self.tenant,
458461
**kwargs
459462
)
@@ -480,8 +483,8 @@ def request_oauth_tokens_by_refresh_token(self, refresh_token):
480483
grant_type='refresh_token',
481484
refresh_token=refresh_token,
482485
environment=self.environment,
483-
scope=_UriOAuthService.SCOPE[(self.environment, self._require_live_connect)],
484-
requireliveconnect=self._require_live_connect,
486+
scope=_UriOAuthService.SCOPE[(self.environment, self._oauth_scope)],
487+
oauth_scope=self._oauth_scope,
485488
tenant = self.tenant
486489
)
487490
if self.token_refreshed_callback is not None:
@@ -543,7 +546,7 @@ class OAuthDesktopMobileAuthCodeGrant(OAuthWithAuthorizationCode):
543546
For more information about registering a Bing Ads application, see http://go.microsoft.com/fwlink/?LinkID=511607.
544547
"""
545548

546-
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_connect=False, tenant='common'):
549+
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, oauth_scope=MSADS_MANAGE, tenant='common'):
547550
""" Initializes a new instance of the this class with the specified client id.
548551
549552
:param client_id: The client identifier corresponding to your registered application.
@@ -555,10 +558,10 @@ def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_co
555558
super(OAuthDesktopMobileAuthCodeGrant, self).__init__(
556559
client_id,
557560
None,
558-
_UriOAuthService.REDIRECTION_URI[(env, require_live_connect)],
561+
_UriOAuthService.REDIRECTION_URI[(env, oauth_scope)],
559562
oauth_tokens=oauth_tokens,
560563
env=env,
561-
require_live_connect=require_live_connect,
564+
oauth_scope=oauth_scope,
562565
tenant=tenant
563566
)
564567

@@ -593,7 +596,7 @@ class OAuthDesktopMobileImplicitGrant(OAuthAuthorization):
593596
"""
594597

595598

596-
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_connect=False, tenant='common'):
599+
def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, oauth_scope=MSADS_MANAGE, tenant='common'):
597600
""" Initializes a new instance of the this class with the specified client id.
598601
599602
:param client_id: The client identifier corresponding to your registered application.
@@ -602,7 +605,7 @@ def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, require_live_co
602605
:type oauth_tokens: OAuthTokens
603606
"""
604607

605-
super(OAuthDesktopMobileImplicitGrant, self).__init__(client_id, oauth_tokens=oauth_tokens, env=env, require_live_connect=require_live_connect, tenant=tenant)
608+
super(OAuthDesktopMobileImplicitGrant, self).__init__(client_id, oauth_tokens=oauth_tokens, env=env, oauth_scope=oauth_scope, tenant=tenant)
606609

607610

608611

@@ -613,14 +616,14 @@ def get_authorization_endpoint(self):
613616
:rtype: str
614617
"""
615618

616-
endpoint_url = _UriOAuthService.AUTHORIZE_URI[(self.environment, self._require_live_connect)]
617-
if self.environment == PRODUCTION and self._require_live_connect == False:
619+
endpoint_url = _UriOAuthService.AUTHORIZE_URI[(self.environment, self._oauth_scope)]
620+
if self.environment == PRODUCTION and (self._oauth_scope == MSADS_MANAGE or self._oauth_scope == ADS_MANAGE):
618621
endpoint_url = endpoint_url.replace('common', self.tenant);
619622
endpoint = str.format(
620623
endpoint_url,
621624
self.client_id,
622625
'token',
623-
_UriOAuthService.REDIRECTION_URI[(self.environment, self._require_live_connect)],
626+
_UriOAuthService.REDIRECTION_URI[(self.environment, self._oauth_scope)],
624627
)
625628

626629
return endpoint if self.state is None else endpoint + '&state=' + self.state
@@ -650,7 +653,7 @@ def extract_access_token_from_uri(self, redirection_uri):
650653

651654
@property
652655
def redirection_uri(self):
653-
return _UriOAuthService.REDIRECTION_URI[(self.environment, self._require_live_connect)]
656+
return _UriOAuthService.REDIRECTION_URI[(self.environment, self._oauth_scope)]
654657

655658

656659
class _UriOAuthService:
@@ -659,21 +662,29 @@ class _UriOAuthService:
659662
def __init__(self):
660663
pass
661664

662-
REDIRECTION_URI={(PRODUCTION, True):'https://login.live.com/oauth20_desktop.srf',
663-
(PRODUCTION, False):'https://login.microsoftonline.com/common/oauth2/nativeclient',
664-
(SANDBOX, False):'https://login.live-int.com/oauth20_desktop.srf'
665+
REDIRECTION_URI={
666+
(PRODUCTION, MSADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/nativeclient',
667+
(PRODUCTION, ADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/nativeclient',
668+
(PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_desktop.srf',
669+
(SANDBOX, MSADS_MANAGE): 'https://login.windows-ppe.net/common/oauth2/nativeclient'
665670
}
666-
AUTH_TOKEN_URI={(PRODUCTION, True):'https://login.live.com/oauth20_token.srf',
667-
(PRODUCTION, False):'https://login.microsoftonline.com/common/oauth2/v2.0/token',
668-
(SANDBOX, False):'https://login.live-int.com/oauth20_token.srf'
671+
AUTH_TOKEN_URI={
672+
(PRODUCTION, MSADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
673+
(PRODUCTION, ADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
674+
(PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_token.srf',
675+
(SANDBOX, MSADS_MANAGE): 'https://login.windows-ppe.net/consumers/oauth2/v2.0/token'
669676
}
670-
AUTHORIZE_URI={(PRODUCTION, True):'https://login.live.com/oauth20_authorize.srf?client_id={0}&scope=bingads.manage&response_type={1}&redirect_uri={2}',
671-
(PRODUCTION, False):'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&scope=https%3A%2F%2Fads.microsoft.com%2Fads.manage%20offline_access&response_type={1}&redirect_uri={2}',
672-
(SANDBOX, False):'https://login.live-int.com/oauth20_authorize.srf?client_id={0}&scope=bingads.manage&response_type={1}&redirect_uri={2}&prompt=login'
677+
AUTHORIZE_URI={
678+
(PRODUCTION, MSADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&scope=https%3A%2F%2Fads.microsoft.com%2Fmsads.manage%20offline_access&response_type={1}&redirect_uri={2}',
679+
(PRODUCTION, ADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&scope=https%3A%2F%2Fads.microsoft.com%2Fads.manage%20offline_access&response_type={1}&redirect_uri={2}',
680+
(PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_authorize.srf?client_id={0}&scope=bingads.manage&response_type={1}&redirect_uri={2}',
681+
(SANDBOX, MSADS_MANAGE): 'https://login.windows-ppe.net/consumers/oauth2/v2.0/authorize?client_id={0}&scope=https://api.ads.microsoft.com/msads.manage%20offline_access&response_type={1}&redirect_uri={2}&prompt=login'
673682
}
674-
SCOPE={(PRODUCTION, True):'bingads.manage',
675-
(PRODUCTION, False):'https://ads.microsoft.com/ads.manage offline_access',
676-
(SANDBOX, False):'bingads.manage'
683+
SCOPE={
684+
(PRODUCTION, MSADS_MANAGE): 'https://ads.microsoft.com/msads.manage offline_access',
685+
(PRODUCTION, ADS_MANAGE): 'https://ads.microsoft.com/ads.manage offline_access',
686+
(PRODUCTION, BINGADS_MANAGE): 'bingads.manage',
687+
(SANDBOX, MSADS_MANAGE): 'https://api.ads.microsoft.com/msads.manage offline_access'
677688
}
678689

679690
@staticmethod
@@ -689,10 +700,10 @@ def get_access_token(**kwargs):
689700
if 'client_secret' in kwargs and kwargs['client_secret'] is None:
690701
del kwargs['client_secret']
691702

692-
if 'requireliveconnect' in kwargs and kwargs['requireliveconnect'] == True:
703+
if 'oauth_scope' in kwargs and kwargs['oauth_scope'] == 'bingads.manage':
693704
del kwargs['tenant']
694705

695-
auth_token_url = _UriOAuthService.AUTH_TOKEN_URI[(kwargs['environment'], kwargs['requireliveconnect'])]
706+
auth_token_url = _UriOAuthService.AUTH_TOKEN_URI[(kwargs['environment'], kwargs['oauth_scope'])]
696707

697708
if 'tenant' in kwargs and kwargs['tenant'] is not None:
698709
auth_token_url = auth_token_url.replace('common', kwargs['tenant'])

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 = '13.0.9.1'
2+
VERSION = '13.0.10'
33
BULK_FORMAT_VERSION_6 = '6.0'
44
WORKING_NAME = 'BingAdsSDKPython'
55
USER_AGENT = '{0} {1} {2}'.format(WORKING_NAME, VERSION, sys.version_info[0:3])

bingads/v13/bulk/entities/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@
3030
from .bulk_offline_conversion import *
3131
from .bulk_experiment import *
3232
from .bulk_image import *
33+
from .bulk_video import *
3334
from .feeds import *

bingads/v13/bulk/entities/audiences/bulk_campaign_negative_audience_association.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping
55
from bingads.v13.internal.bulk.string_table import _StringTable
66
from bingads.v13.internal.extensions import *
7-
from docutils.nodes import row
87

98

109
class BulkCampaignNegativeAudienceAssociation(BulkCampaignNegativeCriterion):

bingads/v13/bulk/entities/audiences/bulk_customer_list_item.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def __init__(self, audience=None, parent_id=None, sub_type=None, text = None):
3131
def audience(self):
3232
"""
3333
the audience, see more detail at: https://go.microsoft.com/fwlink/?linkid=846127
34+
35+
:rtype: str
3436
"""
3537
return self._audience
3638

@@ -87,7 +89,7 @@ def text(self, value):
8789
_SimpleBulkMapping(
8890
header=_StringTable.Audience,
8991
field_to_csv=lambda c: bulk_str(c.audience),
90-
csv_to_field=lambda c, v: setattr(c, 'audience', ve)
92+
csv_to_field=lambda c, v: setattr(c, 'audience', v)
9193
),
9294
_SimpleBulkMapping(
9395
header=_StringTable.SubType,

bingads/v13/bulk/entities/bulk_account.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def __init__(self, account_id=None, customer_id=None, sync_time=None):
2828
self._profile_expansion_enabled = None
2929
self._tracking_url_template = None
3030
self._final_url_suffix = None
31+
self._ad_click_parallel_tracking=None
32+
self._auto_apply_recommendations = None
33+
self._allow_image_auto_retrieve = None
3134

3235
@property
3336
def id(self):
@@ -111,6 +114,45 @@ def final_url_suffix(self):
111114
@final_url_suffix.setter
112115
def final_url_suffix(self, v):
113116
self._final_url_suffix = v
117+
118+
@property
119+
def ad_click_parallel_tracking(self):
120+
""" The setting of parallel tracking in the account.
121+
122+
:return: The setting of parallel tracking of the account
123+
:rtype: bool
124+
"""
125+
return self._ad_click_parallel_tracking
126+
127+
@ad_click_parallel_tracking.setter
128+
def ad_click_parallel_tracking(self, v):
129+
self._ad_click_parallel_tracking = v
130+
131+
@property
132+
def allow_image_auto_retrieve(self):
133+
""" The setting of allowing image auto retrieve in the account.
134+
135+
:return: The setting of allowing image auto retrieve of the account
136+
:rtype: bool
137+
"""
138+
return self._allow_image_auto_retrieve
139+
140+
@allow_image_auto_retrieve.setter
141+
def allow_image_auto_retrieve(self, v):
142+
self._allow_image_auto_retrieve = v
143+
144+
@property
145+
def auto_apply_recommendations(self):
146+
""" The setting of allowing image auto retrieve in the account.
147+
148+
:return: The setting of allowing image auto retrieve of the account
149+
:rtype: dict
150+
"""
151+
return self._auto_apply_recommendations
152+
153+
@auto_apply_recommendations.setter
154+
def auto_apply_recommendations(self, v):
155+
self._auto_apply_recommendations = v
114156

115157
_MAPPINGS = [
116158
_SimpleBulkMapping(
@@ -153,6 +195,21 @@ def final_url_suffix(self, v):
153195
field_to_csv=lambda c: bulk_str(c.profile_expansion_enabled),
154196
csv_to_field=lambda c, v: setattr(c, '_profile_expansion_enabled', parse_bool(v))
155197
),
198+
_SimpleBulkMapping(
199+
header=_StringTable.AdClickParallelTracking,
200+
field_to_csv=lambda c: bulk_str(c.ad_click_parallel_tracking),
201+
csv_to_field=lambda c, v: setattr(c, 'ad_click_parallel_tracking', parse_bool(v))
202+
),
203+
_SimpleBulkMapping(
204+
header=_StringTable.AllowImageAutoRetrieve,
205+
field_to_csv=lambda c: bulk_str(c.allow_image_auto_retrieve),
206+
csv_to_field=lambda c, v: setattr(c, 'allow_image_auto_retrieve', parse_bool(v))
207+
),
208+
_SimpleBulkMapping(
209+
header=_StringTable.AutoApplyRecommendations,
210+
field_to_csv=lambda c: dict_bulk_str(c.auto_apply_recommendations, ';'),
211+
csv_to_field=lambda c, v: setattr(c, 'auto_apply_recommendations', parse_dict(v))
212+
)
156213
]
157214

158215
def process_mappings_from_row_values(self, row_values):

bingads/v13/bulk/entities/bulk_ad_group.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ def quality_score_data(self):
189189
field_to_csv=lambda c: ad_group_bid_bulk_str(c.ad_group.CpcBid),
190190
csv_to_field=lambda c, v: setattr(c.ad_group, 'CpcBid', parse_ad_group_bid(v))
191191
),
192+
_SimpleBulkMapping(
193+
header=_StringTable.CpvBid,
194+
field_to_csv=lambda c: ad_group_bid_bulk_str(c.ad_group.CpvBid),
195+
csv_to_field=lambda c, v: setattr(c.ad_group, 'CpvBid', parse_ad_group_bid(v))
196+
),
197+
_SimpleBulkMapping(
198+
header=_StringTable.CpmBid,
199+
field_to_csv=lambda c: ad_group_bid_bulk_str(c.ad_group.CpmBid),
200+
csv_to_field=lambda c, v: setattr(c.ad_group, 'CpmBid', parse_ad_group_bid(v))
201+
),
192202
_SimpleBulkMapping(
193203
header=_StringTable.Language,
194204
field_to_csv=lambda c: bulk_str(c.ad_group.Language),
@@ -243,6 +253,15 @@ def quality_score_data(self):
243253
field_to_csv=lambda c: c.ad_group.AdGroupType,
244254
csv_to_field=lambda c, v: setattr(c.ad_group, 'AdGroupType', v)
245255
),
256+
_SimpleBulkMapping(
257+
header=_StringTable.MultiMediaAdBidAdjustment,
258+
field_to_csv=lambda c: bulk_str(c.ad_group.MultimediaAdsBidAdjustment),
259+
csv_to_field=lambda c, v: setattr(
260+
c.ad_group,
261+
'MultimediaAdsBidAdjustment',
262+
int(v) if v else None
263+
)
264+
),
246265
]
247266

248267

0 commit comments

Comments
 (0)