diff --git a/HISTORY.rst b/HISTORY.rst index 04a046b6..cdb1873f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,34 @@ Release History +13.0.26(2026-12-09) ++++++++++++++++++++++++++ +API Updates: +* Update Bing Ads API Version 13 service proxies to reflect recent interface changes. For details please see the [Bing Ads API Release Notes](https://learn.microsoft.com/en-us/advertising/guides/release-notes?view=bingads-13). +* New report: BidStrategyReport. +* New column ConversionDelay in AccountPerformanceReport, CampaignPerformanceReport and GoalsAndFunnelsReport. +* New column CostPerConversion in AssetGroupPerformanceReport. +* New API in AdInsight Service: GetAudienceBreakdown. +* New criterion type: TopicCriterion. +* Support multiple campaignIds/adGroupIds in ImpressionBasedRemarketingList. +* New field Industry in UetTag. +* New fields TrackingUrlTemplate, FinalUrlSuffix and UrlCustomParameters in AssetGroup. +* New field IsPolitical in Campaign. +* New field CurrencyCode, ReportingTimeZone and Scope in BidStrategy. +* New field BidStrategyScope in Campaign. +* Add enum value DataDriven in AttributionModelType. +* New option NewTopicTargets and UpdateTopicTargets in GoogleImportOption. + +Bulk Mapping Updates: +* Add bulk mappings: BulkAdGroupTopicCriterion, BulkContentPlacement and BulkTopic. +* Add TrackingTemplate, FinalUrlSuffix and CustomParameter in BulkAssetGroup mapping. +* Add BidStrategyScope, CurrencyCode and TimeZone in BulkBidStrategy mapping. +* Add BidStrategyScope in BulkCampaign mapping. + +Other: +* Support for Google login is now available. You can use GoogleOAuthDesktopMobileAuthCodeGrant or GoogleOAuthWebAuthCodeGrant to sign in with a Google account and access the Bing Ads API. +* Remove pkg_resources and switch to importlib_resources. + 13.0.25.3(2025-09-12) +++++++++++++++++++++++++ * Update Bing Ads API Version 13 service proxies to reflect recent interface changes. For details please see the [Bing Ads API Release Notes](https://learn.microsoft.com/en-us/advertising/guides/release-notes?view=bingads-13). diff --git a/bingads/authorization.py b/bingads/authorization.py index 97589659..dd85e285 100644 --- a/bingads/authorization.py +++ b/bingads/authorization.py @@ -9,12 +9,13 @@ from .exceptions import OAuthTokenRequestException -PRODUCTION='production' -SANDBOX='sandbox' -MSADS_MANAGE='msads.manage' -ADS_MANAGE='ads.manage' -BINGADS_MANAGE='bingads.manage' -MSA_PROD='msa.prod' +PRODUCTION = 'production' +SANDBOX = 'sandbox' +MSADS_MANAGE = 'msads.manage' +ADS_MANAGE = 'ads.manage' +BINGADS_MANAGE = 'bingads.manage' +MSA_PROD = 'msa.prod' +GOOGLE_PROD = 'google.prod' class AuthorizationData: """ Represents a user who intends to access the corresponding customer and account. @@ -568,6 +569,62 @@ def __init__(self, client_id, oauth_tokens=None, env=PRODUCTION, oauth_scope=MSA use_msa_prod=use_msa_prod ) +class GoogleOAuthDesktopMobileAuthCodeGrant(OAuthWithAuthorizationCode): + """ Represents a Google OAuth authorization object implementing the authorization code grant flow for use in a desktop or mobile application. + + You can use an instance of this class as the AuthorizationData.Authentication property + of an :class:`.AuthorizationData` object to authenticate with Bing Ads services. + In this case the AuthenticationToken request header will be set to the corresponding OAuthTokens.AccessToken value. + + This class implements the authorization code grant flow for Google OAuth 2.0, which follows the standard OAuth 2.0 flow + as defined in detail in the Authorization Code Grant section of the OAuth 2.0 spec. + The Google access token obtained through this flow can be passed to the Bing Ads API backend in the same manner as Microsoft tokens. + """ + + def __init__(self, client_id, client_secret=None, oauth_tokens=None, env=PRODUCTION, oauth_scope=GOOGLE_PROD, tenant='common'): + """ Initializes a new instance of the this class with the specified client id. + + :param client_id: The client identifier corresponding to your registered application. + :type client_id: str + :param oauth_tokens: Contains information about OAuth access tokens received from the Microsoft Account authorization service + :type oauth_tokens: OAuthTokens + """ + + super(GoogleOAuthDesktopMobileAuthCodeGrant, self).__init__( + client_id, + client_secret, + _UriOAuthService.REDIRECTION_URI[(env, oauth_scope)], + oauth_tokens=oauth_tokens, + env=env, + oauth_scope=oauth_scope, + tenant=tenant + ) + +class GoogleOAuthWebAuthCodeGrant(OAuthWithAuthorizationCode): + """ Represents a Google OAuth authorization object implementing the authorization code grant flow for use in a web application. + + You can use an instance of this class as the AuthorizationData.Authentication property + to authenticate with Bing Ads REST API services. + """ + + def __init__(self, client_id, client_secret, redirect_url, oauth_tokens=None, env=PRODUCTION, oauth_scope=GOOGLE_PROD, tenant='common'): + """ Initializes a new instance of this class with the specified client id. + + :param client_id: The client identifier corresponding to your registered application. + :type client_id: str + :param oauth_tokens: OAuth token information + :type oauth_tokens: OAuthTokens + """ + super(GoogleOAuthWebAuthCodeGrant, self).__init__( + client_id, + client_secret, + redirect_url, + oauth_tokens=oauth_tokens, + env=env, + oauth_scope=oauth_scope, + tenant=tenant + ) + class OAuthWebAuthCodeGrant(OAuthWithAuthorizationCode): """ Represents an OAuth authorization object implementing the authorization code grant flow for use in a web application. @@ -666,33 +723,41 @@ class _UriOAuthService: def __init__(self): pass - REDIRECTION_URI={ + REDIRECTION_URI = { (PRODUCTION, MSADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/nativeclient', (PRODUCTION, ADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/nativeclient', (PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_desktop.srf', (SANDBOX, MSADS_MANAGE): 'https://login.windows-ppe.net/common/oauth2/nativeclient', - (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/nativeclient' + (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/nativeclient', + (PRODUCTION, GOOGLE_PROD): 'http://localhost', + (SANDBOX, GOOGLE_PROD): 'http://localhost' } - AUTH_TOKEN_URI={ + AUTH_TOKEN_URI = { (PRODUCTION, MSADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/token', (PRODUCTION, ADS_MANAGE): 'https://login.microsoftonline.com/common/oauth2/v2.0/token', (PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_token.srf', (SANDBOX, MSADS_MANAGE): 'https://login.windows-ppe.net/consumers/oauth2/v2.0/token', - (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/v2.0/token' + (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/v2.0/token', + (PRODUCTION, GOOGLE_PROD): 'https://oauth2.googleapis.com/token', + (SANDBOX, GOOGLE_PROD): 'https://oauth2.googleapis.com/token' } - AUTHORIZE_URI={ + AUTHORIZE_URI = { (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}', (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}', (PRODUCTION, BINGADS_MANAGE): 'https://login.live.com/oauth20_authorize.srf?client_id={0}&scope=bingads.manage&response_type={1}&redirect_uri={2}', (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', - (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&scope=https%3A%2F%2Fsi.ads.microsoft.com%2Fmsads.manage%20offline_access&response_type={1}&redirect_uri={2}' + (SANDBOX, MSA_PROD): 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={0}&scope=https%3A%2F%2Fsi.ads.microsoft.com%2Fmsads.manage%20offline_access&response_type={1}&redirect_uri={2}', + (PRODUCTION, GOOGLE_PROD): 'https://accounts.google.com/o/oauth2/v2/auth?client_id={0}&scope=openid%20email%20profile&response_type={1}&redirect_uri={2}&access_type=offline&prompt=consent', + (SANDBOX, GOOGLE_PROD): 'https://accounts.google.com/o/oauth2/v2/auth?client_id={0}&scope=openid%20email%20profile&response_type={1}&redirect_uri={2}&access_type=offline&prompt=consent' } - SCOPE={ + SCOPE = { (PRODUCTION, MSADS_MANAGE): 'https://ads.microsoft.com/msads.manage offline_access', (PRODUCTION, ADS_MANAGE): 'https://ads.microsoft.com/ads.manage offline_access', (PRODUCTION, BINGADS_MANAGE): 'bingads.manage', (SANDBOX, MSADS_MANAGE): 'https://api.ads.microsoft.com/msads.manage offline_access', - (SANDBOX, MSA_PROD): 'https://si.ads.microsoft.com/msads.manage offline_access' + (SANDBOX, MSA_PROD): 'https://si.ads.microsoft.com/msads.manage offline_access', + (PRODUCTION, GOOGLE_PROD): 'openid email profile', + (SANDBOX, GOOGLE_PROD): 'openid email profile' } @staticmethod @@ -725,4 +790,4 @@ def get_access_token(**kwargs): raise OAuthTokenRequestException(error_json.get('error'), error_json.get('error_description')) r_json = json.loads(r.text) - return OAuthTokens(r_json['access_token'], int(r_json['expires_in']), r_json['refresh_token'], r_json) + return OAuthTokens(r_json['access_token'], int(r_json['expires_in']), r_json.get('refresh_token', None), r_json) diff --git a/bingads/manifest.py b/bingads/manifest.py index 8388f538..4b09e886 100644 --- a/bingads/manifest.py +++ b/bingads/manifest.py @@ -1,5 +1,5 @@ import sys -VERSION = '13.0.25.3' +VERSION = '13.0.26' BULK_FORMAT_VERSION_6 = '6.0' WORKING_NAME = 'BingAdsSDKPython' USER_AGENT = '{0} {1} {2}'.format(WORKING_NAME, VERSION, sys.version_info[0:3]) diff --git a/bingads/service_client.py b/bingads/service_client.py index 1fe91dc8..827be933 100644 --- a/bingads/service_client.py +++ b/bingads/service_client.py @@ -18,7 +18,7 @@ def __init__(self, resolver): @type resolver: L{resolver.Resolver} """ self.resolver = resolver - + def skip_value(self, type): """ whether or not to skip setting the value """ return False @@ -45,7 +45,7 @@ def __init__(self, service, version, authorization_data=None, environment='produ self._authorization_data = authorization_data self._refresh_oauth_tokens_automatically = True self._version = ServiceClient._format_version(version) - + # Use in-memory cache by default. if 'cache' not in suds_options: @@ -68,7 +68,7 @@ def __getattr__(self, name): self.set_options(**self._options) return _ServiceCall(self, name) - + def get_response_header(self): return self.hp.get_response_header() @@ -109,7 +109,7 @@ def factory(self): """ return self.soap_client.factory - + @property def service_url(self): """ The wsdl url of service based on the specific service and environment. @@ -304,14 +304,18 @@ def name(self): return self._name -import pkg_resources +import sys +if sys.version_info >= (3, 9): + import importlib.resources as importlib_resources +else: + import importlib_resources import types from suds.sudsobject import Property from suds.sax.text import Text # this is used to create entity only. Given the sandbox should have the same contract, we are good to use sandbox wsdl. _CAMPAIGN_MANAGEMENT_SERVICE_V13 = Client( - 'file:///' + pkg_resources.resource_filename('bingads', 'v13/proxies/sandbox/campaignmanagement_service.xml'), cache=DictCache()) + 'file:///' + str(importlib_resources.files('bingads').joinpath('v13/proxies/sandbox/campaignmanagement_service.xml')), cache=DictCache()) _CAMPAIGN_OBJECT_FACTORY_V13 = _CAMPAIGN_MANAGEMENT_SERVICE_V13.factory _CAMPAIGN_OBJECT_FACTORY_V13.builder = BingAdsBuilder(_CAMPAIGN_OBJECT_FACTORY_V13.builder.resolver) diff --git a/bingads/service_info.py b/bingads/service_info.py index 07fdde25..cde29cef 100644 --- a/bingads/service_info.py +++ b/bingads/service_info.py @@ -1,11 +1,15 @@ -import pkg_resources +import sys +if sys.version_info >= (3, 9): + import importlib.resources as importlib_resources +else: + import importlib_resources _SERVICE_LIST = ['adinsight', 'bulk', 'campaignmanagement', 'customerbilling', 'customermanagement', 'reporting'] SERVICE_INFO_DICT_V13 = {} for service in _SERVICE_LIST: - SERVICE_INFO_DICT_V13[(service, 'production')] = 'file:///' + pkg_resources.resource_filename('bingads', 'v13/proxies/production/%s_service.xml' % (service)) - SERVICE_INFO_DICT_V13[(service, 'sandbox')] = 'file:///' + pkg_resources.resource_filename('bingads', 'v13/proxies/sandbox/%s_service.xml' % (service)) + SERVICE_INFO_DICT_V13[(service, 'production')] = 'file:///' + str(importlib_resources.files('bingads').joinpath('v13/proxies/production/%s_service.xml' % (service))) + SERVICE_INFO_DICT_V13[(service, 'sandbox')] = 'file:///' + str(importlib_resources.files('bingads').joinpath('v13/proxies/sandbox/%s_service.xml' % (service))) SERVICE_INFO_DICT = {13: SERVICE_INFO_DICT_V13} diff --git a/bingads/v13/bulk/entities/__init__.py b/bingads/v13/bulk/entities/__init__.py index f019fad5..8c626452 100644 --- a/bingads/v13/bulk/entities/__init__.py +++ b/bingads/v13/bulk/entities/__init__.py @@ -58,3 +58,5 @@ from .bulk_new_customer_acquisition_goal import * from .goals import * from .account_placement_exclusion_list import * +from .bulk_topic import * +from .bulk_content_placement import * diff --git a/bingads/v13/bulk/entities/bulk_asset_group.py b/bingads/v13/bulk/entities/bulk_asset_group.py index ba3831f9..da79b18f 100644 --- a/bingads/v13/bulk/entities/bulk_asset_group.py +++ b/bingads/v13/bulk/entities/bulk_asset_group.py @@ -163,6 +163,21 @@ def asset_group(self, asset_group): field_to_csv=lambda c: bulk_optional_str(c.asset_group.Path2, c.asset_group.Id), csv_to_field=lambda c, v: setattr(c.asset_group, 'Path2', v) ), + _SimpleBulkMapping( + header=_StringTable.TrackingTemplate, + field_to_csv=lambda c: bulk_optional_str(c.asset_group.TrackingUrlTemplate, c.asset_group.Id), + csv_to_field=lambda c, v: setattr(c.asset_group, 'TrackingUrlTemplate', v if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.FinalUrlSuffix, + field_to_csv=lambda c: bulk_optional_str(c.asset_group.FinalUrlSuffix, c.asset_group.Id), + csv_to_field=lambda c, v: setattr(c.asset_group, 'FinalUrlSuffix', v if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.CustomParameter, + field_to_csv=lambda c: field_to_csv_UrlCustomParameters(c.asset_group), + csv_to_field=lambda c, v: csv_to_field_UrlCustomParameters(c.asset_group, v) + ), ] diff --git a/bingads/v13/bulk/entities/bulk_bid_strategy.py b/bingads/v13/bulk/entities/bulk_bid_strategy.py index 8dc37a84..68760b4e 100644 --- a/bingads/v13/bulk/entities/bulk_bid_strategy.py +++ b/bingads/v13/bulk/entities/bulk_bid_strategy.py @@ -90,6 +90,21 @@ def account_id(self, value): field_to_csv=lambda c: bulk_str(c.bid_strategy.AssociatedCampaignType), csv_to_field=lambda c, v: setattr(c.bid_strategy, 'AssociatedCampaignType', v if v else None) ), + _SimpleBulkMapping( + header=_StringTable.BidStrategyScope, + field_to_csv=lambda c: bulk_str(c.bid_strategy.Scope), + csv_to_field=lambda c, v: csv_to_field_enum(c.bid_strategy, v, 'Scope', EntityScope) + ), + _SimpleBulkMapping( + header=_StringTable.CurrencyCode, + field_to_csv=lambda c: bulk_str(c.bid_strategy.CurrencyCode), + csv_to_field=lambda c, v: setattr(c.bid_strategy, 'CurrencyCode', v) + ), + _SimpleBulkMapping( + header=_StringTable.TimeZone, + field_to_csv=lambda c: bulk_str(c.bid_strategy.ReportingTimeZone), + csv_to_field=lambda c, v: setattr(c.bid_strategy, 'ReportingTimeZone', v) + ), _ComplexBulkMapping(bid_strategy_biddingscheme_to_csv, csv_to_bid_strategy_biddingscheme), ] diff --git a/bingads/v13/bulk/entities/bulk_campaign.py b/bingads/v13/bulk/entities/bulk_campaign.py index 0cd6f0c4..64e2d4b2 100644 --- a/bingads/v13/bulk/entities/bulk_campaign.py +++ b/bingads/v13/bulk/entities/bulk_campaign.py @@ -44,6 +44,7 @@ def __init__(self, account_id=None, campaign=None): self._destination_channel = None self._is_multi_channel_campaign = None self._should_serve_on_msan = None + self._scope = None @property def account_id(self): @@ -148,6 +149,14 @@ def should_serve_on_msan(self): def should_serve_on_msan(self, value): self._should_serve_on_msan = value + @property + def scope(self): + return self._scope + + @scope.setter + def scope(self, value): + self._scope = value + def _get_dynamic_feed_setting(self): return self._get_setting(_DynamicFeedSetting, 'DynamicFeedSetting') @@ -844,6 +853,11 @@ def _write_website(c): field_to_csv=lambda c: field_to_csv_bool(c.campaign.IsPolitical), csv_to_field=lambda c, v: setattr(c.campaign, 'IsPolitical', parse_bool(v)) ), + _SimpleBulkMapping( + header=_StringTable.BidStrategyScope, + field_to_csv=lambda c: bulk_str(c.scope), + csv_to_field=lambda c, v: csv_to_field_enum(c, v, 'scope', EntityScope) + ), ] def read_additional_data(self, stream_reader): diff --git a/bingads/v13/bulk/entities/bulk_content_placement.py b/bingads/v13/bulk/entities/bulk_content_placement.py new file mode 100644 index 00000000..8e6f59bb --- /dev/null +++ b/bingads/v13/bulk/entities/bulk_content_placement.py @@ -0,0 +1,126 @@ +from bingads.v13.internal.bulk.string_table import _StringTable +from bingads.v13.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity +from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping +from bingads.v13.internal.extensions import * + +class BulkContentPlacement(_SingleRecordBulkEntity): + """ Represents a ContentPlacement. + + This class exposes the property :attr:`brand_item` that can be read and written as fields of the ContentPlacement record + in a bulk file. + + For more information, see ContentPlacement at https://go.microsoft.com/fwlink/?linkid=846127. + + *See also:* + + * :class:`.BulkServiceManager` + * :class:`.BulkOperation` + * :class:`.BulkFileReader` + * :class:`.BulkFileWriter` + """ + + def __init__(self, id=None, parent_id=None, name=None, status=None): + super(BulkContentPlacement, self).__init__() + + self._id = id + self._parent_id = parent_id + self._name = name + self._status = status + + @property + def id(self): + """ The identifier of the ContentPlacement. + + Corresponds to the 'Id' field in the bulk file. + + :rtype: int + """ + + return self._id + + @id.setter + def id(self, id): + self._id = id + + @property + def parent_id(self): + """ The parent identifier of the ContentPlacement that contains the ContentPlacement. + + Corresponds to the 'Parent Id' field in the bulk file. + + :rtype: int + """ + + return self._parent_id + + @parent_id.setter + def parent_id(self, parent_id): + self._parent_id = parent_id + + @property + def name(self): + """ The name of the ContentPlacement. + + Corresponds to the 'Name' field in the bulk file. + + :rtype: str + """ + + return self._name + + @name.setter + def name(self, name): + self._name = name + + @property + def status(self): + """ The status + + Corresponds to the 'Status' field in the bulk file. + + :rtype: str + """ + + return self._status + + @status.setter + def status(self, status): + self._status = status + + _MAPPINGS = [ + _SimpleBulkMapping( + header=_StringTable.Id, + field_to_csv=lambda c: bulk_str(c.id), + csv_to_field=lambda c, v: setattr(c, 'id', int(v) if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.Name, + field_to_csv=lambda c: bulk_str(c.name), + csv_to_field=lambda c, v: setattr(c, 'name', v) + ), + _SimpleBulkMapping( + header=_StringTable.ParentId, + field_to_csv=lambda c: bulk_str(c.parent_id), + csv_to_field=lambda c, v: setattr(c, 'parent_id', int(v) if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.Name, + field_to_csv=lambda c: bulk_str(c.name), + csv_to_field=lambda c, v: setattr(c, 'name', v) + ), + _SimpleBulkMapping( + header=_StringTable.Status, + field_to_csv=lambda c: bulk_str(c.status), + csv_to_field=lambda c, v: setattr(c, 'status', v) + ), + ] + + def process_mappings_from_row_values(self, row_values): + + row_values.convert_to_entity(self, BulkContentPlacement._MAPPINGS) + + def process_mappings_to_row_values(self, row_values, exclude_readonly_data): + self.convert_to_values(row_values, BulkContentPlacement._MAPPINGS) + + def read_additional_data(self, stream_reader): + super(BulkContentPlacement, self).read_additional_data(stream_reader) diff --git a/bingads/v13/bulk/entities/bulk_topic.py b/bingads/v13/bulk/entities/bulk_topic.py new file mode 100644 index 00000000..5897dd96 --- /dev/null +++ b/bingads/v13/bulk/entities/bulk_topic.py @@ -0,0 +1,147 @@ +from bingads.v13.internal.bulk.string_table import _StringTable +from bingads.v13.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity +from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping +from bingads.v13.internal.extensions import * + +class BulkTopic(_SingleRecordBulkEntity): + """ Represents a topic. + + This class exposes the property :attr:`brand_item` that can be read and written as fields of the topic record + in a bulk file. + + For more information, see topic at https://go.microsoft.com/fwlink/?linkid=846127. + + *See also:* + + * :class:`.BulkServiceManager` + * :class:`.BulkOperation` + * :class:`.BulkFileReader` + * :class:`.BulkFileWriter` + """ + + def __init__(self, id=None, parent_id=None, topic_parent_id=None, name=None, status=None): + super(BulkTopic, self).__init__() + + self._id = id + self._parent_id = parent_id + self._topic_parent_id = topic_parent_id + self._name = name + self._status = status + + @property + def id(self): + """ The identifier of the topic. + + Corresponds to the 'Id' field in the bulk file. + + :rtype: int + """ + + return self._id + + @id.setter + def id(self, id): + self._id = id + + @property + def parent_id(self): + """ The parent identifier of the topic that contains the topic. + + Corresponds to the 'Parent Id' field in the bulk file. + + :rtype: int + """ + + return self._parent_id + + @parent_id.setter + def parent_id(self, parent_id): + self._parent_id = parent_id + + @property + def topic_parent_id(self): + """ The parent identifier of the topic that contains the topic. + + Corresponds to the 'Topic Parent Id' field in the bulk file. + + :rtype: int + """ + + return self._topic_parent_id + + @topic_parent_id.setter + def topic_parent_id(self, topic_parent_id): + self._topic_parent_id = topic_parent_id + + @property + def name(self): + """ The name of the topic. + + Corresponds to the 'Name' field in the bulk file. + + :rtype: str + """ + + return self._name + + @name.setter + def name(self, name): + self._name = name + + @property + def status(self): + """ The status + + Corresponds to the 'Status' field in the bulk file. + + :rtype: str + """ + + return self._status + + @status.setter + def status(self, status): + self._status = status + + _MAPPINGS = [ + _SimpleBulkMapping( + header=_StringTable.Id, + field_to_csv=lambda c: bulk_str(c.id), + csv_to_field=lambda c, v: setattr(c, 'id', int(v) if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.Name, + field_to_csv=lambda c: bulk_str(c.name), + csv_to_field=lambda c, v: setattr(c, 'name', v) + ), + _SimpleBulkMapping( + header=_StringTable.ParentId, + field_to_csv=lambda c: bulk_str(c.parent_id), + csv_to_field=lambda c, v: setattr(c, 'parent_id', int(v) if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.TopicParentId, + field_to_csv=lambda c: bulk_str(c.topic_parent_id), + csv_to_field=lambda c, v: setattr(c, 'topic_parent_id', int(v) if v else None) + ), + _SimpleBulkMapping( + header=_StringTable.Name, + field_to_csv=lambda c: bulk_str(c.name), + csv_to_field=lambda c, v: setattr(c, 'name', v) + ), + _SimpleBulkMapping( + header=_StringTable.Status, + field_to_csv=lambda c: bulk_str(c.status), + csv_to_field=lambda c, v: setattr(c, 'status', v) + ), + ] + + def process_mappings_from_row_values(self, row_values): + + row_values.convert_to_entity(self, BulkTopic._MAPPINGS) + + def process_mappings_to_row_values(self, row_values, exclude_readonly_data): + self.convert_to_values(row_values, BulkTopic._MAPPINGS) + + def read_additional_data(self, stream_reader): + super(BulkTopic, self).read_additional_data(stream_reader) diff --git a/bingads/v13/bulk/entities/target_criterions/__init__.py b/bingads/v13/bulk/entities/target_criterions/__init__.py index dafefb08..7c4f91e4 100644 --- a/bingads/v13/bulk/entities/target_criterions/__init__.py +++ b/bingads/v13/bulk/entities/target_criterions/__init__.py @@ -33,3 +33,4 @@ from .bulk_ad_group_hotel_date_selection_type_criterion import * from .bulk_ad_group_hotel_length_of_stay_criterion import * from .bulk_ad_group_genre_criterion import * +from .bulk_ad_group_topic_criterion import * diff --git a/bingads/v13/bulk/entities/target_criterions/bulk_ad_group_topic_criterion.py b/bingads/v13/bulk/entities/target_criterions/bulk_ad_group_topic_criterion.py new file mode 100644 index 00000000..ee48ee62 --- /dev/null +++ b/bingads/v13/bulk/entities/target_criterions/bulk_ad_group_topic_criterion.py @@ -0,0 +1,51 @@ +from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V13 +from bingads.v13.bulk.entities.target_criterions.bulk_ad_group_biddable_criterion import BulkAdGroupBiddableCriterion +from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping +from bingads.v13.internal.bulk.string_table import _StringTable +from bingads.v13.internal.extensions import * + + +class BulkAdGroupTopicCriterion(BulkAdGroupBiddableCriterion): + """ Represents an Ad Group Topic Criterion that can be read or written in a bulk file. + + This class exposes the :attr:`biddable_ad_group_criterion` property that can be read and written as fields of the + Ad Group Topic Criterion record in a bulk file. + + For more information, see Ad Group Topic Criterion at https://go.microsoft.com/fwlink/?linkid=846127. + + *See also:* + + * :class:`.BulkServiceManager` + * :class:`.BulkOperation` + * :class:`.BulkFileReader` + * :class:`.BulkFileWriter` + """ + + def __init__(self, + biddable_ad_group_criterion=None, + campaign_name=None, + ad_group_name=None, ): + super(BulkAdGroupTopicCriterion, self).__init__(biddable_ad_group_criterion, campaign_name, ad_group_name) + + _MAPPINGS = [ + _SimpleBulkMapping( + _StringTable.Target, + field_to_csv=lambda c: field_to_csv_TopicId(c.biddable_ad_group_criterion), + csv_to_field=lambda c, v: csv_to_field_TopicId(c.biddable_ad_group_criterion, v) + ) + ] + + def create_criterion(self): + self._biddable_ad_group_criterion.Criterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('TopicCriterion') + self._biddable_ad_group_criterion.Criterion.Type = 'TopicCriterion' + + def process_mappings_to_row_values(self, row_values, exclude_readonly_data): + super(BulkAdGroupTopicCriterion, self).process_mappings_to_row_values(row_values, exclude_readonly_data) + self.convert_to_values(row_values, BulkAdGroupTopicCriterion._MAPPINGS) + + def process_mappings_from_row_values(self, row_values): + super(BulkAdGroupTopicCriterion, self).process_mappings_from_row_values(row_values) + row_values.convert_to_entity(self, BulkAdGroupTopicCriterion._MAPPINGS) + + def read_additional_data(self, stream_reader): + super(BulkAdGroupTopicCriterion, self).read_additional_data(stream_reader) diff --git a/bingads/v13/internal/bulk/bulk_object_factory.py b/bingads/v13/internal/bulk/bulk_object_factory.py index 77f116eb..a495ebe4 100644 --- a/bingads/v13/internal/bulk/bulk_object_factory.py +++ b/bingads/v13/internal/bulk/bulk_object_factory.py @@ -180,6 +180,7 @@ class _BulkObjectFactory(): _StringTable.AdGroupNegativeIndustryCriterion: _EntityInfo(lambda: BulkAdGroupNegativeIndustryCriterion()), _StringTable.AdGroupNegativeJobFunctionCriterion: _EntityInfo(lambda: BulkAdGroupNegativeJobFunctionCriterion()), _StringTable.AdGroupGenreCriterion: _EntityInfo(lambda: BulkAdGroupGenreCriterion()), + _StringTable.AdGroupTopicCriterion: _EntityInfo(lambda: BulkAdGroupTopicCriterion()), _StringTable.Label: _EntityInfo(lambda: BulkLabel()), _StringTable.CampaignLabel: _EntityInfo(lambda: BulkCampaignLabel()), _StringTable.AdGroupLabel: _EntityInfo(lambda: BulkAdGroupLabel()), @@ -240,6 +241,8 @@ class _BulkObjectFactory(): _StringTable.AccountPlacementInclusionList: _EntityInfo(lambda: BulkAccountPlacementInclusionList()), _StringTable.AccountPlacementInclusionListItem: _EntityInfo(lambda: BulkSharedSite()), _StringTable.CampaignAccountPlacementInclusionListAssociation: _EntityInfo(lambda: BulkAccountPlacementInclusionListAssociation()), + _StringTable.Topic: _EntityInfo(lambda: BulkTopic()), + _StringTable.ContentPlacement: _EntityInfo(lambda: BulkContentPlacement()), } ADDITIONAL_OBJECT_MAP = { diff --git a/bingads/v13/internal/bulk/csv_headers.py b/bingads/v13/internal/bulk/csv_headers.py index 47924ea4..af2735d7 100644 --- a/bingads/v13/internal/bulk/csv_headers.py +++ b/bingads/v13/internal/bulk/csv_headers.py @@ -331,6 +331,7 @@ class _CsvHeaders: _StringTable.BidStrategyCommissionRate, _StringTable.BidStrategyPercentMaxCpc, _StringTable.BidStrategyTargetCostPerSale, + _StringTable.BidStrategyScope, # Ad Format Preference _StringTable.AdFormatPreference, @@ -537,7 +538,7 @@ class _CsvHeaders: _StringTable.NewCustomerAcquisitionBidOnlyMode, _StringTable.AccountPlacementListItemUrl, - + _StringTable.TopicParentId, ] @staticmethod diff --git a/bingads/v13/internal/bulk/string_table.py b/bingads/v13/internal/bulk/string_table.py index 683406e9..a6f7eaaf 100644 --- a/bingads/v13/internal/bulk/string_table.py +++ b/bingads/v13/internal/bulk/string_table.py @@ -438,6 +438,7 @@ class _StringTable: BidStrategyPercentMaxCpc = "Bid Strategy PercentMaxCpc" BidStrategyCommissionRate = "Bid Strategy CommissionRate" BidStrategyTargetCostPerSale = "Bid Strategy TargetCostPerSale" + BidStrategyScope = "Bid Strategy Scope" # Remarketing Audience = "Audience" @@ -543,6 +544,7 @@ class _StringTable: AdGroupNegativeIndustryCriterion = "Ad Group Negative Industry Criterion" AdGroupNegativeJobFunctionCriterion = "Ad Group Negative Job Function Criterion" AdGroupGenreCriterion = "Ad Group Genre Criterion" + AdGroupTopicCriterion = "Ad Group Topic Criterion" # Responsive Ad ResponsiveAd = "Responsive Ad" @@ -751,3 +753,7 @@ class _StringTable: CampaignAccountPlacementInclusionListAssociation = "Campaign Account Placement Inclusion List Association" AccountPlacementInclusionListId = "Account Placement Inclusion List Id" AccountPlacementInclusionListItemId = "Account Placement Inclusion List Item Id" + + Topic = "Topic" + ContentPlacement = "Content Placement" + TopicParentId = "Topic Parent Id" diff --git a/bingads/v13/internal/extensions.py b/bingads/v13/internal/extensions.py index 7838891f..6a391b6b 100644 --- a/bingads/v13/internal/extensions.py +++ b/bingads/v13/internal/extensions.py @@ -59,6 +59,7 @@ HotelAdvanceBookingWindowCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('HotelAdvanceBookingWindowCriterion') HotelCheckInDateCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('HotelCheckInDateCriterion') GenreCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('GenreCriterion') +TopicCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('TopicCriterion') HotelCheckInDayCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('HotelCheckInDayCriterion') HotelDateSelectionTypeCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('HotelDateSelectionTypeCriterion') HotelLengthOfStayCriterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('HotelLengthOfStayCriterion') @@ -302,7 +303,7 @@ def csv_to_status(c, v): c.ad_group.Status = AdGroupStatus.Deleted else: c.ad_group.Status = None - + def parse_bid_option(str): if str == "BidValue": return BidOption.BidValue @@ -1097,7 +1098,7 @@ def field_to_csv_UseSearcherTimeZone(bool_value, id): return DELETE_VALUE if id and id > 0 else None else: return str(bool_value) - + def csv_to_field_enum(entity, value, attr_name, enum_class): """ Generic method to convert CSV string values to enum fields on an entity. @@ -1118,7 +1119,7 @@ def csv_to_field_enum(entity, value, attr_name, enum_class): except (AttributeError, ValueError): # If the value doesn't match any enum value, set to None setattr(entity, attr_name, None) - + def csv_to_field_CampaignStatus(entity, value): if value is None or value == '': entity.Status = None @@ -1135,7 +1136,7 @@ def csv_to_field_CampaignStatus(entity, value): elif value == 'Suspended': entity.Status = CampaignStatus.Suspended else: - entity.Status = None + entity.Status = None def field_to_csv_bool(bool_value): if bool_value is None: @@ -1606,6 +1607,17 @@ def csv_to_field_GenreId(entity, value): if entity is not None and entity.Criterion is not None and isinstance(entity.Criterion,type(GenreCriterion)): setattr(entity.Criterion, "GenreId", int(value) if value else None) +def field_to_csv_TopicId(entity): + if entity is None or entity.Criterion is None or entity.Criterion.TopicId is None: + return None + return bulk_str(entity.Criterion.TopicId) + +def csv_to_field_TopicId(entity, value): + if value is None or value == '': + return + if entity is not None and entity.Criterion is not None and isinstance(entity.Criterion,type(TopicCriterion)): + setattr(entity.Criterion, "TopicId", int(value) if value else None) + def field_to_csv_EndDate(entity): if entity is None or entity.Criterion is None or entity.Criterion.EndDate is None: return None diff --git a/bingads/v13/proxies/production/adinsight_service.xml b/bingads/v13/proxies/production/adinsight_service.xml index 8a659060..484cf4d9 100644 --- a/bingads/v13/proxies/production/adinsight_service.xml +++ b/bingads/v13/proxies/production/adinsight_service.xml @@ -466,6 +466,7 @@ + @@ -685,10 +686,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -718,13 +780,13 @@ - + - - + + @@ -800,13 +862,13 @@ - + - + - + @@ -859,7 +921,7 @@ - + @@ -902,10 +964,10 @@ - + - + @@ -953,10 +1015,10 @@ - + - + @@ -1031,9 +1093,9 @@ - + - + @@ -1067,7 +1129,7 @@ - + @@ -1120,7 +1182,7 @@ - + @@ -1488,7 +1550,7 @@ - + @@ -1640,7 +1702,7 @@ - + @@ -1788,7 +1850,7 @@ - + @@ -1815,7 +1877,7 @@ - + @@ -3012,9 +3074,9 @@ - + - + @@ -3062,7 +3124,7 @@ - + @@ -3083,7 +3145,7 @@ - + @@ -3144,7 +3206,7 @@ - + @@ -3322,6 +3384,9 @@ + + + @@ -3407,11 +3472,57 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + 2 + + + + + + + 4 + + + + + + @@ -3872,6 +3983,9 @@ + + + @@ -3925,9 +4039,9 @@ - - - + + + @@ -4241,6 +4355,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -4260,7 +4398,7 @@ - + @@ -4284,7 +4422,7 @@ - + @@ -4308,7 +4446,7 @@ - + @@ -4332,7 +4470,7 @@ - + @@ -4356,7 +4494,7 @@ - + @@ -4380,7 +4518,7 @@ - + @@ -4404,7 +4542,7 @@ - + @@ -4428,7 +4566,7 @@ - + @@ -4452,7 +4590,7 @@ - + @@ -4476,7 +4614,7 @@ - + @@ -4500,7 +4638,7 @@ - + @@ -4524,7 +4662,7 @@ - + @@ -4548,7 +4686,7 @@ - + @@ -4572,7 +4710,7 @@ - + @@ -4596,7 +4734,7 @@ - + @@ -4620,7 +4758,7 @@ - + @@ -4644,7 +4782,7 @@ - + @@ -4668,7 +4806,7 @@ - + @@ -4692,7 +4830,7 @@ - + @@ -4716,7 +4854,7 @@ - + @@ -4740,7 +4878,7 @@ - + @@ -4764,7 +4902,7 @@ - + @@ -4788,7 +4926,7 @@ - + @@ -4812,7 +4950,7 @@ - + @@ -4836,7 +4974,7 @@ - + @@ -4890,6 +5028,12 @@ + + + + + + @@ -5228,6 +5372,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bingads/v13/proxies/production/campaignmanagement_service.xml b/bingads/v13/proxies/production/campaignmanagement_service.xml index 43423f23..0499696f 100644 --- a/bingads/v13/proxies/production/campaignmanagement_service.xml +++ b/bingads/v13/proxies/production/campaignmanagement_service.xml @@ -1192,6 +1192,13 @@ + + + + + + + @@ -1282,6 +1289,13 @@ + + + + + + + @@ -1668,6 +1682,8 @@ + + @@ -5389,13 +5405,6 @@ - - - - - - - @@ -6007,8 +6016,29 @@ + + + + + + + + + + + + + + + + + + + + + @@ -6038,6 +6068,7 @@ + @@ -6053,6 +6084,7 @@ + @@ -6105,6 +6137,7 @@ + @@ -7268,6 +7301,7 @@ + @@ -7285,6 +7319,7 @@ + @@ -7525,6 +7560,17 @@ + + + + + + + + + + + @@ -8175,6 +8221,13 @@ + + + + + + + @@ -8318,6 +8371,13 @@ + + + + + + + @@ -8587,6 +8647,20 @@ + + + + 33554432 + + + + + + + 67108864 + + + @@ -10399,9 +10473,7 @@ - - @@ -10416,9 +10488,6 @@ - - - @@ -10430,88 +10499,13 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bingads/v13/proxies/production/customerbilling_service.xml b/bingads/v13/proxies/production/customerbilling_service.xml index e4ad96a2..629fa63d 100644 --- a/bingads/v13/proxies/production/customerbilling_service.xml +++ b/bingads/v13/proxies/production/customerbilling_service.xml @@ -224,6 +224,24 @@ + + + + + + + + + + + + + + + + + + @@ -279,6 +297,12 @@ + + + + + + @@ -1100,6 +1124,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -1175,6 +1221,12 @@ + + + + + + @@ -1437,6 +1489,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/bingads/v13/proxies/production/reporting_service.xml b/bingads/v13/proxies/production/reporting_service.xml index d6d4fa4e..709a1552 100644 --- a/bingads/v13/proxies/production/reporting_service.xml +++ b/bingads/v13/proxies/production/reporting_service.xml @@ -2056,6 +2056,10 @@ + + + + @@ -2064,11 +2068,24 @@ + + + + + + + + + + + + + @@ -2290,18 +2307,6 @@ - - - - - - - - - - - - @@ -4595,6 +4600,8 @@ + + @@ -4938,16 +4945,19 @@ - + - + + + + diff --git a/bingads/v13/proxies/sandbox/adinsight_service.xml b/bingads/v13/proxies/sandbox/adinsight_service.xml index 5c4ce362..0716ac42 100644 --- a/bingads/v13/proxies/sandbox/adinsight_service.xml +++ b/bingads/v13/proxies/sandbox/adinsight_service.xml @@ -466,6 +466,7 @@ + @@ -685,10 +686,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -718,13 +780,13 @@ - + - - + + @@ -800,13 +862,13 @@ - + - + - + @@ -859,7 +921,7 @@ - + @@ -902,10 +964,10 @@ - + - + @@ -953,10 +1015,10 @@ - + - + @@ -1031,9 +1093,9 @@ - + - + @@ -1067,7 +1129,7 @@ - + @@ -1120,7 +1182,7 @@ - + @@ -1488,7 +1550,7 @@ - + @@ -1640,7 +1702,7 @@ - + @@ -1788,7 +1850,7 @@ - + @@ -1815,7 +1877,7 @@ - + @@ -3012,9 +3074,9 @@ - + - + @@ -3062,7 +3124,7 @@ - + @@ -3083,7 +3145,7 @@ - + @@ -3144,7 +3206,7 @@ - + @@ -3322,6 +3384,9 @@ + + + @@ -3334,6 +3399,7 @@ + @@ -3407,11 +3473,57 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + 2 + + + + + + + 4 + + + + + + @@ -3762,6 +3874,7 @@ + @@ -3872,6 +3985,9 @@ + + + @@ -3925,9 +4041,9 @@ - - - + + + @@ -4241,6 +4357,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -4260,7 +4400,7 @@ - + @@ -4284,7 +4424,7 @@ - + @@ -4308,7 +4448,7 @@ - + @@ -4332,7 +4472,7 @@ - + @@ -4356,7 +4496,7 @@ - + @@ -4380,7 +4520,7 @@ - + @@ -4404,7 +4544,7 @@ - + @@ -4428,7 +4568,7 @@ - + @@ -4452,7 +4592,7 @@ - + @@ -4476,7 +4616,7 @@ - + @@ -4500,7 +4640,7 @@ - + @@ -4524,7 +4664,7 @@ - + @@ -4548,7 +4688,7 @@ - + @@ -4572,7 +4712,7 @@ - + @@ -4596,7 +4736,7 @@ - + @@ -4620,7 +4760,7 @@ - + @@ -4644,7 +4784,7 @@ - + @@ -4668,7 +4808,7 @@ - + @@ -4692,7 +4832,7 @@ - + @@ -4716,7 +4856,7 @@ - + @@ -4740,7 +4880,7 @@ - + @@ -4764,7 +4904,7 @@ - + @@ -4788,7 +4928,7 @@ - + @@ -4812,7 +4952,7 @@ - + @@ -4836,7 +4976,7 @@ - + @@ -4890,6 +5030,12 @@ + + + + + + @@ -5228,6 +5374,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bingads/v13/proxies/sandbox/campaignmanagement_service.xml b/bingads/v13/proxies/sandbox/campaignmanagement_service.xml index 60bb0ee0..eb86b049 100644 --- a/bingads/v13/proxies/sandbox/campaignmanagement_service.xml +++ b/bingads/v13/proxies/sandbox/campaignmanagement_service.xml @@ -1192,6 +1192,13 @@ + + + + + + + @@ -1282,6 +1289,13 @@ + + + + + + + @@ -1668,6 +1682,8 @@ + + @@ -4705,6 +4721,7 @@ + @@ -5389,13 +5406,6 @@ - - - - - - - @@ -6007,8 +6017,29 @@ + + + + + + + + + + + + + + + + + + + + + @@ -6038,6 +6069,7 @@ + @@ -6053,6 +6085,7 @@ + @@ -6105,6 +6138,7 @@ + @@ -7016,6 +7050,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + + + + 4 + + + + + + + + + + + + + + + + @@ -7268,6 +7358,7 @@ + @@ -7285,6 +7376,7 @@ + @@ -7525,6 +7617,17 @@ + + + + + + + + + + + @@ -8169,12 +8272,26 @@ + + + + + + + + + + + + + + @@ -8318,6 +8435,13 @@ + + + + + + + @@ -8587,6 +8711,27 @@ + + + + 33554432 + + + + + + + 67108864 + + + + + + + 134217728 + + + @@ -10399,9 +10544,7 @@ - - @@ -10416,9 +10559,6 @@ - - - @@ -10430,88 +10570,13 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -10557,6 +10622,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -15155,6 +15331,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -16272,6 +16520,24 @@ + + + + + + + + + + + + + + + + + + @@ -20554,6 +20820,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bingads/v13/proxies/sandbox/customerbilling_service.xml b/bingads/v13/proxies/sandbox/customerbilling_service.xml index 6594f522..159e5da1 100644 --- a/bingads/v13/proxies/sandbox/customerbilling_service.xml +++ b/bingads/v13/proxies/sandbox/customerbilling_service.xml @@ -224,6 +224,24 @@ + + + + + + + + + + + + + + + + + + @@ -279,6 +297,12 @@ + + + + + + @@ -1100,6 +1124,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -1175,6 +1221,12 @@ + + + + + + @@ -1437,6 +1489,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/bingads/v13/proxies/sandbox/reporting_service.xml b/bingads/v13/proxies/sandbox/reporting_service.xml index 3f73b4a5..05fb8c73 100644 --- a/bingads/v13/proxies/sandbox/reporting_service.xml +++ b/bingads/v13/proxies/sandbox/reporting_service.xml @@ -2056,6 +2056,10 @@ + + + + @@ -2064,11 +2068,24 @@ + + + + + + + + + + + + + @@ -2290,18 +2307,6 @@ - - - - - - - - - - - - @@ -4595,6 +4600,8 @@ + + @@ -4938,16 +4945,19 @@ - + - + + + + diff --git a/requirements.txt b/requirements.txt index 56cfaf5e..67124777 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ coverage flake8 setuptools Sphinx +importlib_resources>=5; python_version < "3.9" \ No newline at end of file diff --git a/setup.py b/setup.py index 3aaa4fa4..3d597113 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ except ImportError: from distutils.core import setup -VERSION = '13.0.25.3' +VERSION = '13.0.26' with open('README.rst', 'r') as f: readme = f.read() @@ -14,6 +14,7 @@ 'suds-community>=1.1.0', 'requests', 'enum34;python_version<"3.9"', + 'importlib_resources>=5; python_version<"3.9"', ] setup(