diff --git a/HISTORY.rst b/HISTORY.rst index ab20460a..d4a2737f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,9 +1,15 @@ .. :changelog: Release History +12.13.5(2019-09-12) ++++++++++++++++++++++++++ +* Map the Bid Adjustment column to a BidMultiplier via BulkAdGroupProductPartition. +* Updated Bing Ads API version 12 and 13 service proxies to reflect recent interface changes. For more information please see the Bing Ads API Release Notes: https://docs.microsoft.com/en-us/advertising/guides/release-notes. + + 12.13.4.1(2019-08-23) +++++++++++++++++++++++++ -* Write TextAsset and ImageAsset to the Bulk upload file without the Type explicitly set. +* Write TextAsset and ImageAsset to the Bulk upload file without the Type explicitly set. 12.13.4(2019-08-09) +++++++++++++++++++++++++ diff --git a/bingads/manifest.py b/bingads/manifest.py index 88787d96..ee303345 100644 --- a/bingads/manifest.py +++ b/bingads/manifest.py @@ -1,5 +1,5 @@ import sys -VERSION = '12.13.4.1' +VERSION = '12.13.5' BULK_FORMAT_VERSION_5 = '5.0' BULK_FORMAT_VERSION_6 = '6.0' WORKING_NAME = 'BingAdsSDKPython' diff --git a/bingads/v12/bulk/entities/bulk_ad_group_product_partition.py b/bingads/v12/bulk/entities/bulk_ad_group_product_partition.py index 1b2e52dc..fb8c182f 100644 --- a/bingads/v12/bulk/entities/bulk_ad_group_product_partition.py +++ b/bingads/v12/bulk/entities/bulk_ad_group_product_partition.py @@ -1,7 +1,7 @@ from bingads.v12.bulk.entities import * from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V12 from bingads.v12.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity -from bingads.v12.internal.bulk.mappings import _SimpleBulkMapping +from bingads.v12.internal.bulk.mappings import _SimpleBulkMapping, _ComplexBulkMapping from bingads.v12.internal.bulk.string_table import _StringTable from bingads.v12.bulk.entities.common import PerformanceData # from bingads.v12.internal.extensions import bulk_str @@ -9,7 +9,67 @@ _BiddableAdGroupCriterion = type(_CAMPAIGN_OBJECT_FACTORY_V12.create('BiddableAdGroupCriterion')) _NegativeAdGroupCriterion = type(_CAMPAIGN_OBJECT_FACTORY_V12.create('NegativeAdGroupCriterion')) - +_FixedBid = type(_CAMPAIGN_OBJECT_FACTORY_V12.create('FixedBid')) + +def csv_to_bidding(row_values, entity): + success, exclude = row_values.try_get_value(_StringTable.IsExcluded) + if exclude is None: + exclude = '' + exclude = exclude.lower() + if exclude == 'yes' or exclude == 'true': + is_excluded = True + elif exclude == 'no' or exclude == 'false': + is_excluded = False + else: + raise ValueError('IsExcluded can only be set to TRUE|FALSE in Ad Group Product Partition row') + if is_excluded: + product_partition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductPartition') + product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductCondition') + product_partition.Type = 'ProductPartition' + + negative_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V12.create('NegativeAdGroupCriterion') + negative_ad_group_criterion.Criterion = product_partition + negative_ad_group_criterion.Type = 'NegativeAdGroupCriterion' + + entity.ad_group_criterion = negative_ad_group_criterion + else: + product_partition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductPartition') + product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductCondition') + product_partition.Type = 'ProductPartition' + + bid = _CAMPAIGN_OBJECT_FACTORY_V12.create('FixedBid') + bid.Type = 'FixedBid' + + biddable_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V12.create('BiddableAdGroupCriterion') + biddable_ad_group_criterion.Criterion = product_partition + + success, bid_value = row_values.try_get_value(_StringTable.Bid) + if success and bid_value is not None and bid_value != '': + bid.Amount = float(bid_value) + else: + success, bid_value = row_values.try_get_value(_StringTable.BidAdjustment) + if success and bid_value is not None and bid_value != '': + bid = _CAMPAIGN_OBJECT_FACTORY_V12.create('BidMultiplier') + bid.Type = 'BidMultiplier' + bid.Multiplier = float(bid_value) + + biddable_ad_group_criterion.CriterionBid = bid + biddable_ad_group_criterion.Type = 'BiddableAdGroupCriterion' + + entity.ad_group_criterion = biddable_ad_group_criterion + +def bidding_to_csv(entity, row_values): + if isinstance(entity.ad_group_criterion, _NegativeAdGroupCriterion): + row_values[_StringTable.IsExcluded] = 'True' + else: + row_values[_StringTable.IsExcluded] = 'False' + bid = entity.ad_group_criterion.CriterionBid + if bid is None: + return + if isinstance(bid, _FixedBid): + row_values[_StringTable.Bid] = fixed_bid_bulk_str(bid) + else: + row_values[_StringTable.BidAdjustment] = bid_multiplier_bulk_str(bid) class BulkAdGroupProductPartition(_SingleRecordBulkEntity): """ Represents an Ad Group Criterion that can be read or written in a bulk file. @@ -38,56 +98,7 @@ def __init__(self, self._ad_group_name = ad_group_name self._performance_data = None - @classmethod - def _read_is_excluded(cls, entity, row_value): - if row_value is None: - row_value = '' - row_value = row_value.lower() - if row_value == 'yes' or row_value == 'true': - is_excluded = True - elif row_value == 'no' or row_value == 'false': - is_excluded = False - else: - raise ValueError('IsExcluded can only be set to TRUE|FALSE in Ad Group Product Partition row') - if is_excluded: - product_partition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductPartition') - product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductCondition') - product_partition.Type = 'ProductPartition' - - negative_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V12.create('NegativeAdGroupCriterion') - negative_ad_group_criterion.Criterion = product_partition - negative_ad_group_criterion.Type = 'NegativeAdGroupCriterion' - - entity.ad_group_criterion = negative_ad_group_criterion - else: - product_partition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductPartition') - product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V12.create('ProductCondition') - product_partition.Type = 'ProductPartition' - - fixed_bid = _CAMPAIGN_OBJECT_FACTORY_V12.create('FixedBid') - fixed_bid.Type = 'FixedBid' - - biddable_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V12.create('BiddableAdGroupCriterion') - biddable_ad_group_criterion.Criterion = product_partition - biddable_ad_group_criterion.CriterionBid = fixed_bid - biddable_ad_group_criterion.Type = 'BiddableAdGroupCriterion' - - entity.ad_group_criterion = biddable_ad_group_criterion - - @classmethod - def _write_bid(cls, entity): - criterion = entity.ad_group_criterion - if isinstance(criterion, _BiddableAdGroupCriterion) and \ - criterion.CriterionBid is not None: - return fixed_bid_bulk_str(entity.ad_group_criterion.CriterionBid) - - @classmethod - def _read_bid(cls, entity, row_value): - if isinstance(entity.ad_group_criterion, _BiddableAdGroupCriterion): - entity.ad_group_criterion.CriterionBid = parse_fixed_bid(row_value) - else: - pass - + @classmethod def _write_destination_url(cls, entity): if isinstance(entity.ad_group_criterion, _BiddableAdGroupCriterion): @@ -135,11 +146,9 @@ def _read_destination_url(cls, entity, row_value): pass _MAPPINGS = [ - _SimpleBulkMapping( - _StringTable.IsExcluded, - field_to_csv=lambda c: 'True' if isinstance(c.ad_group_criterion, _NegativeAdGroupCriterion) else 'False', - csv_to_field=lambda c, v: BulkAdGroupProductPartition._read_is_excluded(c, v) - ), + + _ComplexBulkMapping(bidding_to_csv, csv_to_bidding), + _SimpleBulkMapping( _StringTable.Status, field_to_csv=lambda c: c.ad_group_criterion.Status, @@ -186,11 +195,7 @@ def _read_destination_url(cls, entity, row_value): field_to_csv=lambda c: BulkAdGroupProductPartition._get_condition_attribute(c), csv_to_field=lambda c, v: setattr(c.ad_group_criterion.Criterion.Condition, 'Attribute', v) ), - _SimpleBulkMapping( - _StringTable.Bid, - field_to_csv=lambda c: BulkAdGroupProductPartition._write_bid(c), - csv_to_field=lambda c, v: BulkAdGroupProductPartition._read_bid(c, v) - ), + _SimpleBulkMapping( _StringTable.DestinationUrl, field_to_csv=lambda c: BulkAdGroupProductPartition._write_destination_url(c), diff --git a/bingads/v12/internal/extensions.py b/bingads/v12/internal/extensions.py index 4e3e1f91..4bd8bb1d 100644 --- a/bingads/v12/internal/extensions.py +++ b/bingads/v12/internal/extensions.py @@ -562,6 +562,11 @@ def fixed_bid_bulk_str(value): return None return bulk_str(value.Amount) +def bid_multiplier_bulk_str(value): + if value is None or not hasattr(value, 'Multiplier') or value.Multiplier is None: + return None + return bulk_str(value.Multiplier) + def parse_minute(value): minute_number = int(value) if minute_number == 0: diff --git a/bingads/v13/bulk/entities/bulk_ad_group_product_partition.py b/bingads/v13/bulk/entities/bulk_ad_group_product_partition.py index 3acd00da..80490aa9 100644 --- a/bingads/v13/bulk/entities/bulk_ad_group_product_partition.py +++ b/bingads/v13/bulk/entities/bulk_ad_group_product_partition.py @@ -1,14 +1,74 @@ from bingads.v13.bulk.entities import * from bingads.service_client import _CAMPAIGN_OBJECT_FACTORY_V13 from bingads.v13.internal.bulk.entities.single_record_bulk_entity import _SingleRecordBulkEntity -from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping +from bingads.v13.internal.bulk.mappings import _SimpleBulkMapping, _ComplexBulkMapping from bingads.v13.internal.bulk.string_table import _StringTable # from bingads.v13.internal.extensions import bulk_str from bingads.v13.internal.extensions import * _BiddableAdGroupCriterion = type(_CAMPAIGN_OBJECT_FACTORY_V13.create('BiddableAdGroupCriterion')) _NegativeAdGroupCriterion = type(_CAMPAIGN_OBJECT_FACTORY_V13.create('NegativeAdGroupCriterion')) - +_FixedBid = type(_CAMPAIGN_OBJECT_FACTORY_V13.create('FixedBid')) + +def csv_to_bidding(row_values, entity): + success, exclude = row_values.try_get_value(_StringTable.IsExcluded) + if exclude is None: + exclude = '' + exclude = exclude.lower() + if exclude == 'yes' or exclude == 'true': + is_excluded = True + elif exclude == 'no' or exclude == 'false': + is_excluded = False + else: + raise ValueError('IsExcluded can only be set to TRUE|FALSE in Ad Group Product Partition row') + if is_excluded: + product_partition = _CAMPAIGN_OBJECT_FACTORY_V13.create('ProductPartition') + product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V13.create('ProductCondition') + product_partition.Type = 'ProductPartition' + + negative_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('NegativeAdGroupCriterion') + negative_ad_group_criterion.Criterion = product_partition + negative_ad_group_criterion.Type = 'NegativeAdGroupCriterion' + + entity.ad_group_criterion = negative_ad_group_criterion + else: + product_partition = _CAMPAIGN_OBJECT_FACTORY_V13.create('ProductPartition') + product_partition.Condition = _CAMPAIGN_OBJECT_FACTORY_V13.create('ProductCondition') + product_partition.Type = 'ProductPartition' + + bid = _CAMPAIGN_OBJECT_FACTORY_V13.create('FixedBid') + bid.Type = 'FixedBid' + + biddable_ad_group_criterion = _CAMPAIGN_OBJECT_FACTORY_V13.create('BiddableAdGroupCriterion') + biddable_ad_group_criterion.Criterion = product_partition + + success, bid_value = row_values.try_get_value(_StringTable.Bid) + if success and bid_value is not None and bid_value != '': + bid.Amount = float(bid_value) + else: + success, bid_value = row_values.try_get_value(_StringTable.BidAdjustment) + if success and bid_value is not None and bid_value != '': + bid = _CAMPAIGN_OBJECT_FACTORY_V13.create('BidMultiplier') + bid.Type = 'BidMultiplier' + bid.Multiplier = float(bid_value) + + biddable_ad_group_criterion.CriterionBid = bid + biddable_ad_group_criterion.Type = 'BiddableAdGroupCriterion' + + entity.ad_group_criterion = biddable_ad_group_criterion + +def bidding_to_csv(entity, row_values): + if isinstance(entity.ad_group_criterion, _NegativeAdGroupCriterion): + row_values[_StringTable.IsExcluded] = 'True' + else: + row_values[_StringTable.IsExcluded] = 'False' + bid = entity.ad_group_criterion.CriterionBid + if bid is None: + return + if isinstance(bid, _FixedBid): + row_values[_StringTable.Bid] = fixed_bid_bulk_str(bid) + else: + row_values[_StringTable.BidAdjustment] = bid_multiplier_bulk_str(bid) class BulkAdGroupProductPartition(_SingleRecordBulkEntity): """ Represents an Ad Group Criterion that can be read or written in a bulk file. @@ -134,11 +194,8 @@ def _read_destination_url(cls, entity, row_value): pass _MAPPINGS = [ - _SimpleBulkMapping( - _StringTable.IsExcluded, - field_to_csv=lambda c: 'True' if isinstance(c.ad_group_criterion, _NegativeAdGroupCriterion) else 'False', - csv_to_field=lambda c, v: BulkAdGroupProductPartition._read_is_excluded(c, v) - ), + _ComplexBulkMapping(bidding_to_csv, csv_to_bidding), + _SimpleBulkMapping( _StringTable.Status, field_to_csv=lambda c: c.ad_group_criterion.Status, @@ -185,11 +242,6 @@ def _read_destination_url(cls, entity, row_value): field_to_csv=lambda c: BulkAdGroupProductPartition._get_condition_attribute(c), csv_to_field=lambda c, v: setattr(c.ad_group_criterion.Criterion.Condition, 'Attribute', v) ), - _SimpleBulkMapping( - _StringTable.Bid, - field_to_csv=lambda c: BulkAdGroupProductPartition._write_bid(c), - csv_to_field=lambda c, v: BulkAdGroupProductPartition._read_bid(c, v) - ), _SimpleBulkMapping( _StringTable.DestinationUrl, field_to_csv=lambda c: BulkAdGroupProductPartition._write_destination_url(c), diff --git a/bingads/v13/internal/extensions.py b/bingads/v13/internal/extensions.py index 22f3511c..27b634d6 100644 --- a/bingads/v13/internal/extensions.py +++ b/bingads/v13/internal/extensions.py @@ -562,6 +562,11 @@ def fixed_bid_bulk_str(value): return None return bulk_str(value.Amount) +def bid_multiplier_bulk_str(value): + if value is None or not hasattr(value, 'Multiplier') or value.Multiplier is None: + return None + return bulk_str(value.Multiplier) + def parse_minute(value): minute_number = int(value) if minute_number == 0: diff --git a/bingads/v13/proxies/campaign_management_service.xml b/bingads/v13/proxies/campaign_management_service.xml index 96151e27..6926fee7 100644 --- a/bingads/v13/proxies/campaign_management_service.xml +++ b/bingads/v13/proxies/campaign_management_service.xml @@ -1707,11 +1707,25 @@ + + + + + + + + + + + + + + @@ -2017,6 +2031,7 @@ + @@ -2039,6 +2054,17 @@ + + + + + + + + + + + @@ -2184,6 +2210,7 @@ + diff --git a/setup.py b/setup.py index d5813dd6..a7252ff3 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ except ImportError: from distutils.core import setup -VERSION = '12.13.4.1' +VERSION = '12.13.5' with open('README.rst', 'r') as f: readme = f.read()