diff --git a/HISTORY.rst b/HISTORY.rst
index 11908d37..f9c7cc9a 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,6 +1,13 @@
.. :changelog:
Release History
+-----------
+12.0.2(2018-12-10)
++++++++++++++++++++
+*Updated 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.
+*Added Bulk mapping for campaign experiments i.e., BulkExperiment and BulkCampaign.
+*Added Bulk mapping for action ad extensions i.e., BulkActionAdExtension, BulkAccountActionAdExtension, BulkAdGroupActionAdExtension, and BulkCampaignActionAdExtension.
+
-----------
12.0.1(2018-11-10)
+++++++++++++++++++
diff --git a/bingads/manifest.py b/bingads/manifest.py
index 7cd947ea..688b09cb 100644
--- a/bingads/manifest.py
+++ b/bingads/manifest.py
@@ -1,5 +1,5 @@
import sys
-VERSION = '12.0.1'
+VERSION = '12.0.2'
BULK_FORMAT_VERSION_5 = '5.0'
BULK_FORMAT_VERSION_6 = '6.0'
WORKING_NAME = 'BingAdsSDKPython'
diff --git a/bingads/v12/bulk/entities/__init__.py b/bingads/v12/bulk/entities/__init__.py
index 32087670..afc5d3d8 100644
--- a/bingads/v12/bulk/entities/__init__.py
+++ b/bingads/v12/bulk/entities/__init__.py
@@ -26,3 +26,4 @@
from .target_criterions import *
from .labels import *
from .bulk_offline_conversion import *
+from .bulk_experiment import *
diff --git a/bingads/v12/bulk/entities/ad_extensions/__init__.py b/bingads/v12/bulk/entities/ad_extensions/__init__.py
index e29696c6..c063d1d0 100644
--- a/bingads/v12/bulk/entities/ad_extensions/__init__.py
+++ b/bingads/v12/bulk/entities/ad_extensions/__init__.py
@@ -8,4 +8,5 @@
from .bulk_review_ad_extensions import *
from .bulk_structured_snippet_ad_extensions import *
from .bulk_sitelink_ad_extensions import *
-from .bulk_price_ad_extensions import *
\ No newline at end of file
+from .bulk_price_ad_extensions import *
+from .bulk_action_ad_extensions import *
\ No newline at end of file
diff --git a/bingads/v12/bulk/entities/ad_extensions/bulk_action_ad_extensions.py b/bingads/v12/bulk/entities/ad_extensions/bulk_action_ad_extensions.py
new file mode 100644
index 00000000..2f884206
--- /dev/null
+++ b/bingads/v12/bulk/entities/ad_extensions/bulk_action_ad_extensions.py
@@ -0,0 +1,153 @@
+from bingads.v12.internal.bulk.mappings import _SimpleBulkMapping
+from bingads.v12.internal.bulk.string_table import _StringTable
+from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V12
+
+from .common import _BulkAdExtensionBase
+from .common import _BulkAdGroupAdExtensionAssociation
+from .common import _BulkCampaignAdExtensionAssociation
+from .common import _BulkAccountAdExtensionAssociation
+
+from bingads.v12.internal.extensions import *
+
+
+_ActionAdExtension = type(_CAMPAIGN_OBJECT_FACTORY_V12.create('ActionAdExtension'))
+
+
+class BulkActionAdExtension(_BulkAdExtensionBase):
+ """ Represents a action ad extension.
+
+ This class exposes the :attr:`action_ad_extension` property that can be read and written
+ as fields of the Action Ad Extension record in a bulk file.
+
+ For more information, see Action Ad Extension at https://go.microsoft.com/fwlink/?linkid=846127.
+
+ *See also:*
+
+ * :class:`.BulkServiceManager`
+ * :class:`.BulkOperation`
+ * :class:`.BulkFileReader`
+ * :class:`.BulkFileWriter`
+ """
+
+ def __init__(self, account_id=None, ad_extension=None):
+ if ad_extension and not isinstance(ad_extension, _ActionAdExtension):
+ raise ValueError('The type of ad_extension is: {0}, should be: {1}'.format(
+ type(ad_extension),
+ 'ActionAdExtension'
+ ))
+ super(BulkActionAdExtension, self).__init__(
+ account_id=account_id,
+ ad_extension=ad_extension
+ )
+
+ @property
+ def action_ad_extension(self):
+ """ The action ad extension.
+
+ see Action Ad Extension at https://go.microsoft.com/fwlink/?linkid=846127.
+ """
+
+ return self._ad_extension
+
+ @action_ad_extension.setter
+ def action_ad_extension(self, value):
+ self._ad_extension = value
+
+ _MAPPINGS = [
+ _SimpleBulkMapping(
+ header=_StringTable.ActionType,
+ field_to_csv=lambda c: bulk_str(c.action_ad_extension.ActionType),
+ csv_to_field=lambda c, v: setattr(c.action_ad_extension, 'ActionType', v)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.FinalUrl,
+ field_to_csv=lambda c: field_to_csv_Urls(c.action_ad_extension.FinalUrls),
+ csv_to_field=lambda c, v: csv_to_field_Urls(c.action_ad_extension.FinalUrls, v)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.FinalMobileUrl,
+ field_to_csv=lambda c: field_to_csv_Urls(c.action_ad_extension.FinalMobileUrls),
+ csv_to_field=lambda c, v: csv_to_field_Urls(c.action_ad_extension.FinalMobileUrls, v)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.TrackingTemplate,
+ field_to_csv=lambda c: bulk_optional_str(c.action_ad_extension.TrackingUrlTemplate),
+ csv_to_field=lambda c, v: setattr(c.action_ad_extension, 'TrackingUrlTemplate', v if v else '')
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.Language,
+ field_to_csv=lambda c: bulk_optional_str(c.action_ad_extension.Language),
+ csv_to_field=lambda c, v: setattr(c.action_ad_extension, 'Language', v if v else '')
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.CustomParameter,
+ field_to_csv=lambda c: field_to_csv_UrlCustomParameters(c.action_ad_extension),
+ csv_to_field=lambda c, v: csv_to_field_UrlCustomParameters(c.action_ad_extension, v)
+ )
+ ]
+
+ def process_mappings_from_row_values(self, row_values):
+ self.action_ad_extension = _CAMPAIGN_OBJECT_FACTORY_V12.create('ActionAdExtension')
+ self.action_ad_extension.Type = 'ActionAdExtension'
+ super(BulkActionAdExtension, self).process_mappings_from_row_values(row_values)
+ row_values.convert_to_entity(self, BulkActionAdExtension._MAPPINGS)
+
+ def process_mappings_to_row_values(self, row_values, exclude_readonly_data):
+ self._validate_property_not_null(self.action_ad_extension, 'action_ad_extension')
+ super(BulkActionAdExtension, self).process_mappings_to_row_values(row_values, exclude_readonly_data)
+ self.convert_to_values(row_values, BulkActionAdExtension._MAPPINGS)
+
+
+class BulkAccountActionAdExtension(_BulkAccountAdExtensionAssociation):
+ """ Represents an account level action ad extension.
+
+ This class exposes properties that can be read and written
+ as fields of the Account Action Ad Extension record in a bulk file.
+
+ For more information, see Account Action Ad Extension at https://go.microsoft.com/fwlink/?linkid=846127.
+
+ *See also:*
+
+ * :class:`.BulkServiceManager`
+ * :class:`.BulkOperation`
+ * :class:`.BulkFileReader`
+ * :class:`.BulkFileWriter`
+ """
+
+ pass
+
+class BulkCampaignActionAdExtension(_BulkCampaignAdExtensionAssociation):
+ """ Represents a campaign level action ad extension.
+
+ This class exposes properties that can be read and written
+ as fields of the Campaign Action Ad Extension record in a bulk file.
+
+ For more information, see Campaign Action Ad Extension at https://go.microsoft.com/fwlink/?linkid=846127.
+
+ *See also:*
+
+ * :class:`.BulkServiceManager`
+ * :class:`.BulkOperation`
+ * :class:`.BulkFileReader`
+ * :class:`.BulkFileWriter`
+ """
+
+ pass
+
+class BulkAdGroupActionAdExtension(_BulkAdGroupAdExtensionAssociation):
+ """ Represents an ad group level Action ad extension.
+
+ This class exposes properties that can be read and written
+ as fields of the Ad Group Action Ad Extension record in a bulk file.
+
+ For more information, see Ad Group Action Ad Extension at https://go.microsoft.com/fwlink/?linkid=846127.
+
+ *See also:*
+
+ * :class:`.BulkServiceManager`
+ * :class:`.BulkOperation`
+ * :class:`.BulkFileReader`
+ * :class:`.BulkFileWriter`
+ """
+
+ pass
diff --git a/bingads/v12/bulk/entities/bulk_campaign.py b/bingads/v12/bulk/entities/bulk_campaign.py
index c0c1828b..7d483f42 100644
--- a/bingads/v12/bulk/entities/bulk_campaign.py
+++ b/bingads/v12/bulk/entities/bulk_campaign.py
@@ -409,6 +409,11 @@ def _write_website(c):
field_to_csv=lambda c: c.campaign.SubType,
csv_to_field=lambda c, v: setattr(c.campaign, 'SubType', v)
),
+ _SimpleBulkMapping(
+ header=_StringTable.ExperimentId,
+ field_to_csv=lambda c: bulk_str(c.campaign.ExperimentId),
+ csv_to_field=lambda c, v: setattr(c.campaign, 'ExperimentId', int(v) if v else None)
+ ),
]
def read_additional_data(self, stream_reader):
diff --git a/bingads/v12/bulk/entities/bulk_experiment.py b/bingads/v12/bulk/entities/bulk_experiment.py
new file mode 100644
index 00000000..4f66c6af
--- /dev/null
+++ b/bingads/v12/bulk/entities/bulk_experiment.py
@@ -0,0 +1,92 @@
+from bingads.v12.bulk.entities import QualityScoreData, PerformanceData
+from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V12
+
+from bingads.v12.internal.bulk.string_table import _StringTable
+from bingads.v12.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity
+from bingads.v12.internal.bulk.mappings import _SimpleBulkMapping, _ComplexBulkMapping
+from bingads.v12.internal.extensions import *
+
+
+class BulkExperiment(_SingleRecordBulkEntity):
+ """ Represents an experiment.
+
+ This class exposes the property :attr:`experiment` that can be read and written as fields of the Experiment record
+ in a bulk file.
+
+ For more information, see Experiment at https://go.microsoft.com/fwlink/?linkid=846127.
+
+ *See also:*
+
+ * :class:`.BulkServiceManager`
+ * :class:`.BulkOperation`
+ * :class:`.BulkFileReader`
+ * :class:`.BulkFileWriter`
+ """
+
+ def __init__(self, experiment=None):
+ super(BulkExperiment, self).__init__()
+ self._experiment = experiment
+
+ @property
+ def experiment(self):
+ """ The experiment.
+ """
+ return self._experiment
+
+ @experiment.setter
+ def experiment(self, experiment):
+ self._experiment = experiment
+
+ _MAPPINGS = [
+ _SimpleBulkMapping(
+ header=_StringTable.Id,
+ field_to_csv=lambda c: bulk_str(c.experiment.Id),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'Id', int(v) if v else None)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.Status,
+ field_to_csv=lambda c: bulk_optional_str(c.experiment.ExperimentStatus),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'ExperimentStatus', v)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.Name,
+ field_to_csv=lambda c: c.experiment.Name,
+ csv_to_field=lambda c, v: setattr(c.experiment, 'Name', v)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.StartDate,
+ field_to_csv=lambda c: bulk_date_str(c.experiment.StartDate),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'StartDate', parse_date(v))
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.EndDate,
+ field_to_csv=lambda c: bulk_date_str(c.experiment.EndDate),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'EndDate', parse_date(v))
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.TrafficSplitPercent,
+ field_to_csv=lambda c: bulk_str(c.experiment.TrafficSplitPercent),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'TrafficSplitPercent', int(v) if v else None)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.BaseCampaignId,
+ field_to_csv=lambda c: bulk_str(c.experiment.BaseCampaignId),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'BaseCampaignId', int(v) if v else None)
+ ),
+ _SimpleBulkMapping(
+ header=_StringTable.ExperimentCampaignId,
+ field_to_csv=lambda c: bulk_str(c.experiment.ExperimentCampaignId),
+ csv_to_field=lambda c, v: setattr(c.experiment, 'ExperimentCampaignId', int(v) if v else None)
+ ),
+ ]
+
+ def process_mappings_from_row_values(self, row_values):
+ self.experiment = _CAMPAIGN_OBJECT_FACTORY_V12.create('Experiment')
+ row_values.convert_to_entity(self, BulkExperiment._MAPPINGS)
+
+ def process_mappings_to_row_values(self, row_values, exclude_readonly_data):
+ self._validate_property_not_null(self._experiment, 'Experiment')
+ self.convert_to_values(row_values, BulkExperiment._MAPPINGS)
+
+ def read_additional_data(self, stream_reader):
+ super(BulkExperiment, self).read_additional_data(stream_reader)
\ No newline at end of file
diff --git a/bingads/v12/internal/bulk/bulk_object_factory.py b/bingads/v12/internal/bulk/bulk_object_factory.py
index da6e1aee..2381c0ab 100644
--- a/bingads/v12/internal/bulk/bulk_object_factory.py
+++ b/bingads/v12/internal/bulk/bulk_object_factory.py
@@ -127,6 +127,11 @@ class _BulkObjectFactory():
_StringTable.SimilarRemarketingList: _EntityInfo(lambda: BulkSimilarRemarketingList()),
_StringTable.AdGroupSimilarRemarketingListAssociation: _EntityInfo(lambda: BulkAdGroupSimilarRemarketingListAssociation()),
_StringTable.AdGroupNegativeSimilarRemarketingListAssociation: _EntityInfo(lambda: BulkAdGroupNegativeSimilarRemarketingListAssociation()),
+ _StringTable.ActionAdExtension: _EntityInfo(lambda: BulkActionAdExtension()),
+ _StringTable.AccountActionAdExtension: _EntityInfo(lambda: BulkAccountActionAdExtension()),
+ _StringTable.AdGroupActionAdExtension: _EntityInfo(lambda: BulkAdGroupActionAdExtension()),
+ _StringTable.CampaignActionAdExtension: _EntityInfo(lambda: BulkCampaignActionAdExtension()),
+ _StringTable.Experiment: _EntityInfo(lambda: BulkExperiment()),
}
ADDITIONAL_OBJECT_MAP = {
diff --git a/bingads/v12/internal/bulk/csv_headers.py b/bingads/v12/internal/bulk/csv_headers.py
index f483b11a..1ae204e5 100644
--- a/bingads/v12/internal/bulk/csv_headers.py
+++ b/bingads/v12/internal/bulk/csv_headers.py
@@ -168,6 +168,12 @@ class _CsvHeaders:
_StringTable.CampaignPriority,
_StringTable.LocalInventoryAdsEnabled,
+ # experiment
+ _StringTable.TrafficSplitPercent,
+ _StringTable.BaseCampaignId,
+ _StringTable.ExperimentCampaignId,
+ _StringTable.ExperimentId,
+
#CoOp
_StringTable.BidOption,
_StringTable.BidBoostValue,
@@ -300,6 +306,9 @@ class _CsvHeaders:
_StringTable.AdSchedule,
_StringTable.UseSearcherTimeZone,
+ # Action ad extension
+ _StringTable.ActionType,
+
# Dynamic Search Ads
_StringTable.DomainLanguage,
_StringTable.DynamicAdTargetCondition1,
diff --git a/bingads/v12/internal/bulk/string_table.py b/bingads/v12/internal/bulk/string_table.py
index 4d409f83..db3713c9 100644
--- a/bingads/v12/internal/bulk/string_table.py
+++ b/bingads/v12/internal/bulk/string_table.py
@@ -11,6 +11,11 @@ class _StringTable:
Budget = "Budget"
BudgetType = "Budget Type"
BudgetName = "Budget Name"
+ Experiment = "Experiment"
+ TrafficSplitPercent = "Traffic Split Percent"
+ BaseCampaignId = "Base Campaign Id"
+ ExperimentCampaignId = "Experiment Campaign Id"
+ ExperimentId = "Experiment Id"
BudgetId = "Budget Id"
AdGroup = "Ad Group"
Keyword = "Keyword"
@@ -119,6 +124,13 @@ class _StringTable:
CampaignCallAdExtension = "Campaign Call Ad Extension"
IsCallTrackingEnabled = "Call Tracking Enabled"
RequireTollFreeTrackingNumber = "Toll Free"
+
+ #Action Ad Extension
+ ActionAdExtension = "Action Ad Extension"
+ AccountActionAdExtension = "Account Action Ad Extension"
+ CampaignActionAdExtension = "Campaign Action Ad Extension"
+ AdGroupActionAdExtension = "Ad Group Action Ad Extension"
+ ActionType = "Action Type"
# Structured Snippet Ad Extension
StructuredSnippetAdExtension = "Structured Snippet Ad Extension"
diff --git a/bingads/v12/proxies/campaign_management_service.xml b/bingads/v12/proxies/campaign_management_service.xml
index cf163038..b5508674 100644
--- a/bingads/v12/proxies/campaign_management_service.xml
+++ b/bingads/v12/proxies/campaign_management_service.xml
@@ -237,6 +237,13 @@
+
+
+
+
+
+
+
@@ -274,20 +281,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -321,6 +314,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -331,6 +338,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1138,6 +1159,7 @@
+
diff --git a/setup.py b/setup.py
index 29bd7249..adeb286f 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
except ImportError:
from distutils.core import setup
-VERSION = '12.0.1'
+VERSION = '12.0.2'
with open('README.rst', 'r') as f:
readme = f.read()
@@ -15,7 +15,7 @@
'future',
'six',
'requests',
- 'enum34',
+ 'enum34;python_version<"3.4"',
]
setup(