Skip to content

Commit

Permalink
Merge pull request #213 from BingAds/users/qitia/v13.0.14
Browse files Browse the repository at this point in the history
v13.0.14
  • Loading branch information
qitia authored Jun 30, 2022
2 parents e7b5a61 + 13ae4fd commit 43529d9
Show file tree
Hide file tree
Showing 26 changed files with 4,066 additions and 368 deletions.
10 changes: 10 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
Release History

13.0.14(2022-06-30)
+++++++++++++++++++++++++
* 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.
* Get rid of the default ObjectCache,and use a in memory cache for ServiceClient. https://github.com/BingAds/BingAds-Python-SDK/pull/108.
* Add bulk mappings for BulkCampaignConversionGoal.
* Add bulk mappings for ad customer attribute i.e., BulkAdCustomizerAttribute, BulkCampaignAdCustomizerAttribute, BulkAdGroupAdCustomizerAttribute, BulkKeywordAdCustomizerAttribute.
* Add mappings for new fields in BulkFeed: Schedule.
* Fix issue of invalid variable: maxCpcValue. https://github.com/BingAds/BingAds-Python-SDK/pull/200#issuecomment-1085432417.
* Update suds-community version to 1.1.0.

13.0.13(2022-01-14)
+++++++++++++++++++++++++
* 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.
Expand Down
2 changes: 1 addition & 1 deletion bingads/manifest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
VERSION = '13.0.13'
VERSION = '13.0.14'
BULK_FORMAT_VERSION_6 = '6.0'
WORKING_NAME = 'BingAdsSDKPython'
USER_AGENT = '{0} {1} {2}'.format(WORKING_NAME, VERSION, sys.version_info[0:3])
53 changes: 8 additions & 45 deletions bingads/service_client.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,27 @@
from suds.client import Client, Factory, WebFault, Builder
from suds.cache import ObjectCache
from .headerplugin import HeaderPlugin
from .authorization import *
from .service_info import SERVICE_INFO_DICT
from .manifest import USER_AGENT
from .util import DictCache
from getpass import getuser
from tempfile import gettempdir
from os import path
from datetime import datetime
from suds.sudsobject import Factory as SFactory

class BingAdsBuilder (Builder):
# copy from https://github.com/suds-community/suds/blob/master/suds/builder.py. Change line
# setattr(data, type.name, value if not type.optional() or type.multi_occurrence() else None)
# to
# setattr(data, type.name, value)
# to not breaking our customer and bulk mapping.

# See https://github.com/suds-community/suds/issues/67 and https://github.com/suds-community/suds/commit/366f7f1616595b9e4163a3f90fc6e84ac0ae23f5
def __init__(self, resolver):
"""
@param resolver: A schema object name resolver.
@type resolver: L{resolver.Resolver}
"""
self.resolver = resolver


def process(self, data, type, history):
""" process the specified type then process its children """
if type in history:
return
if type.enum():
return
history.append(type)
resolved = type.resolve()
value = None

if type.multi_occurrence():
value = []
else:
if len(resolved) > 0:
if resolved.mixed():
value = SFactory.property(resolved.name)
md = value.__metadata__
md.sxtype = resolved
else:
value = SFactory.object(resolved.name)
md = value.__metadata__
md.sxtype = resolved
md.ordering = self.ordering(resolved)

setattr(data, type.name, value)
if value is not None:
data = value
if not isinstance(data, list):
self.add_attributes(data, resolved)
for child, ancestry in resolved.children():
if self.skip_child(child, ancestry):
continue
self.process(data, child, history[:])
def skip_value(self, type):
""" whether or not to skip setting the value """
return False


class ServiceClient:
Expand All @@ -83,10 +47,9 @@ def __init__(self, service, version, authorization_data=None, environment='produ
self._version = ServiceClient._format_version(version)


# TODO This is a temp fix for set default suds temp folder with user info, suds development branch has already fixed it.
# Use in-memory cache by default.
if 'cache' not in suds_options:
location = path.join(gettempdir(), 'suds', getuser())
suds_options['cache'] = ObjectCache(location, days=1)
suds_options['cache'] = DictCache()
# set cachingpolicy to 1 to reuse WSDL cache files, otherwise will only reuse XML files
if 'cachingpolicy' not in suds_options:
suds_options['cachingpolicy'] = 1
Expand Down Expand Up @@ -348,7 +311,7 @@ def name(self):

# 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'))
'file:///' + pkg_resources.resource_filename('bingads', '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)

Expand Down
11 changes: 11 additions & 0 deletions bingads/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import time
from bingads.exceptions import TimeoutException
from suds.client import WebFault
from suds.cache import Cache


class DictCache(dict, Cache):
# .get and .clear work as intended
purge = dict.__delitem__

def put(self, id_, obj):
self[id_] = obj
return obj


class _TimeHelper(object):
"""
Expand Down
6 changes: 6 additions & 0 deletions bingads/v13/bulk/entities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,9 @@
from .bulk_image import *
from .bulk_video import *
from .feeds import *
from .bulk_campaign_conversion_goal import *
from .bulk_ad_customizer_attribute import *
from .bulk_ad_customizer_attribute_entity_base import *
from .bulk_ad_customizer_attribute_campaign import *
from .bulk_ad_customizer_attribute_ad_group import *
from .bulk_ad_customizer_attribute_keyword import *
150 changes: 150 additions & 0 deletions bingads/v13/bulk/entities/bulk_ad_customizer_attribute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V13
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 BulkAdCustomizerAttribute(_SingleRecordBulkEntity):
""" Represents an AdCustomizerAttribute.
Properties of this class and of classes that it is derived from, correspond to fields of the AdCustomizerAttribute record in a bulk file.
For more information, see AdCustomizerAttribute at https://go.microsoft.com/fwlink/?linkid=846127
*See also:*
* :class:`.BulkServiceManager`
* :class:`.BulkOperation`
* :class:`.BulkFileReader`
* :class:`.BulkFileWriter`
"""

def __init__(self, id = None, name=None, account_value=None, data_type=None, editorial_status = None, status=None):
super(BulkAdCustomizerAttribute, self).__init__()
self._id = id
self._name = name
self._account_value = account_value
self._data_type = data_type
self._editorial_status = editorial_status
self._status = status


@property
def id(self):
""" the id of bulk record
Corresponds to the 'Id' field in the bulk file.
:rtype: str
"""
return self._id

@id.setter
def id(self, value):
self._id = value

@property
def name(self):
""" the name of the ad customizer attribute
Corresponds to the 'Name' field in the bulk file.
:rtype: str
"""
return self._name

@name.setter
def name(self, value):
self._name = value

@property
def account_value(self):
""" the value of the account of ad customizer attribute
Corresponds to the 'AdCustomizer AttributeValue' field in the bulk file.
:rtype: str
"""
return self._account_value

@account_value.setter
def account_value(self, value):
self._account_value = value

@property
def data_type(self):
""" the data type of ad customizer attribute
Corresponds to the 'AdCustomizer DataType' field in the bulk file.
:rtype: str
"""
return self._data_type

@data_type.setter
def data_type(self, value):
self._data_type = value

@property
def editorial_status(self):
""" the editorial status of ad customizer attribute
Corresponds to the 'Editorial Status' field in the bulk file.
:rtype: str
"""
return self._editorial_status

@editorial_status.setter
def editorial_status(self, value):
self._editorial_status = value

@property
def status(self):
""" the status of ad customizer attribute
Corresponds to the 'Status' field in the bulk file.
:rtype: str
"""
return self._status

@status.setter
def status(self, value):
self._status = value

_MAPPINGS = [
_SimpleBulkMapping(
header=_StringTable.Id,
field_to_csv=lambda c: bulk_str(c.id),
csv_to_field=lambda c, v: setattr(c, 'id', v)
),
_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.AdCustomizerAttributeValue,
field_to_csv=lambda c: bulk_str(c.account_value),
csv_to_field=lambda c, v: setattr(c, 'account_value', v)
),
_SimpleBulkMapping(
header=_StringTable.AdCustomizerDataType,
field_to_csv=lambda c: bulk_str(c.data_type),
csv_to_field=lambda c, v: setattr(c, 'data_type', v)
),
_SimpleBulkMapping(
header=_StringTable.EditorialStatus,
field_to_csv=lambda c: bulk_str(c.editorial_status),
csv_to_field=lambda c, v: setattr(c, 'editorial_status', v)
),
_SimpleBulkMapping(
header=_StringTable.Status,
field_to_csv=lambda c: 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, BulkAdCustomizerAttribute._MAPPINGS)

def process_mappings_to_row_values(self, row_values, exclude_readonly_data):
self.convert_to_values(row_values, BulkAdCustomizerAttribute._MAPPINGS)

def read_additional_data(self, stream_reader):
super(BulkAdCustomizerAttribute, self).read_additional_data(stream_reader)
36 changes: 36 additions & 0 deletions bingads/v13/bulk/entities/bulk_ad_customizer_attribute_ad_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V13
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 *

from. import BulkAdCustomizerAttributeEntityBase

class BulkAdGroupAdCustomizerAttribute(BulkAdCustomizerAttributeEntityBase):
""" Represents an AdGroupAdCustomizerAttribute.
*See also:*
* :class:`.BulkServiceManager`
* :class:`.BulkOperation`
* :class:`.BulkFileReader`
* :class:`.BulkFileWriter`
"""

def __init__(self, id = None, name=None, parent_id=None, attribute_value=None, editorial_status = None):
super(BulkAdGroupAdCustomizerAttribute, self).__init__(id, name, parent_id, attribute_value, editorial_status)


@property
def ad_group_id(self):
""" the ad group id of bulk record
Corresponds to the 'ParentId' field in the bulk file.
:rtype: str
"""
return self._parent_id

@ad_group_id.setter
def ad_group_id(self, value):
self._parent_id = value

37 changes: 37 additions & 0 deletions bingads/v13/bulk/entities/bulk_ad_customizer_attribute_campaign.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V13
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 *

from. import BulkAdCustomizerAttributeEntityBase


class BulkCampaignAdCustomizerAttribute(BulkAdCustomizerAttributeEntityBase):
""" Represents a CampaignAdCustomizerAttribute.
*See also:*
* :class:`.BulkServiceManager`
* :class:`.BulkOperation`
* :class:`.BulkFileReader`
* :class:`.BulkFileWriter`
"""

def __init__(self, id = None, name=None, parent_id=None, attribute_value=None, editorial_status = None):
super(BulkCampaignAdCustomizerAttribute, self).__init__(id, name, parent_id, attribute_value, editorial_status)


@property
def campaign_id(self):
""" the campaign id of bulk record
Corresponds to the 'ParentId' field in the bulk file.
:rtype: str
"""
return self._parent_id

@campaign_id.setter
def campaign_id(self, value):
self._parent_id = value

Loading

0 comments on commit 43529d9

Please sign in to comment.