From a7d283c24a1e32b0d1b2cb2c21a262fb692bab53 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 7 Oct 2020 14:52:46 -0700 Subject: [PATCH 1/7] Add support of an array of price buckets and standard Prebid price granularities such as auto or dense --- settings.py | 8 ++-- tasks/add_new_prebid_partner.py | 33 ++++++++++++--- tasks/price_utils.py | 71 +++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/settings.py b/settings.py index 7348509..6194e94 100644 --- a/settings.py +++ b/settings.py @@ -80,10 +80,10 @@ PREBID_BIDDER_CODE = None -# Price buckets. This should match your Prebid settings for the partner. See: -# http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity -# FIXME: this should be an array of buckets. See: -# https://github.com/prebid/Prebid.js/blob/8fed3d7aaa814e67ca3efc103d7d306cab8c692c/src/cpmBucketManager.js +# Price buckets. This should match your Prebid settings for the partner. +# Can be an a single bucket, an array of buckerts or string for standard Prebid +# price granularities such as 'medium', 'dense' or 'auto' +# See: http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity PREBID_PRICE_BUCKETS = { 'precision': 2, 'min' : 0, diff --git a/tasks/add_new_prebid_partner.py b/tasks/add_new_prebid_partner.py index b3797d5..de0a928 100755 --- a/tasks/add_new_prebid_partner.py +++ b/tasks/add_new_prebid_partner.py @@ -6,6 +6,7 @@ import sys from builtins import input from pprint import pprint +from collections import OrderedDict from colorama import init @@ -29,6 +30,7 @@ get_prices_summary_string, micro_amount_to_num, num_to_str, + get_prebid_standard_bucketing ) # Colorama for cross-platform support for colored logging. @@ -317,15 +319,34 @@ def main(): if bidder_code is None: raise MissingSettingException('PREBID_BIDDER_CODE') - price_buckets = getattr(settings, 'PREBID_PRICE_BUCKETS', None) - if price_buckets is None: - raise MissingSettingException('PREBID_PRICE_BUCKETS') + price_buckets = None + price_buckets_config = getattr(settings, 'PREBID_PRICE_BUCKETS', None) + if price_buckets_config is None: + raise MissingSettingException('PREBID_PRICE_BUCKETS') + elif isinstance(price_buckets_config, dict): + price_buckets = [ price_buckets_config ] + elif isinstance(price_buckets_config, list): + price_buckets = price_buckets_config + elif isinstance(price_buckets_config, str): + price_buckets = get_prebid_standard_bucketing(price_buckets_config) + if price_buckets == None: + raise BadSettingException('Unknown price bucketing scheme \'{0}\' for PREBID_PRICE_BUCKETS'.format(price_buckets_config)) + else: + raise BadSettingException('The setting "PREBID_PRICE_BUCKETS" ' + 'is an invalid type.') + + for pb in price_buckets: + check_price_buckets_validity(pb) + + prices = [] + for pb in price_buckets: + prices.extend(get_prices_array(pb)) - check_price_buckets_validity(price_buckets) + # Remove duplicates price buckets + prices = list(OrderedDict.fromkeys(prices)) - prices = get_prices_array(price_buckets) prices_summary = get_prices_summary_string(prices, - price_buckets['precision']) + price_buckets[0]['precision']) logger.info( u""" diff --git a/tasks/price_utils.py b/tasks/price_utils.py index f0d4522..137d2ec 100644 --- a/tasks/price_utils.py +++ b/tasks/price_utils.py @@ -37,7 +37,7 @@ def num_to_str(num, precision=2): Returns: a string """ - return '%.{0}f'.format(str(precision)) % num + return '%.{0}f'.format(str(precision)) % num def get_prices_array(price_bucket): """ @@ -48,7 +48,7 @@ def get_prices_array(price_bucket): price_bucket (object): the price bucket configuration Returns: an array of integers: every price bucket cutoff from: - int(round(price_bucket['min'] * 10**6, precision)) to + int(round(price_bucket['min'] * 10**6, precision)) to int(round(price_bucket['max'] * 10**6, precision)) """ start_cpm = price_bucket['min'] if price_bucket['min'] >=0 else 0.00 @@ -65,7 +65,7 @@ def get_prices_array(price_bucket): while current_cpm_micro_amount <= end_cpm_micro_amount: prices.append(current_cpm_micro_amount) current_cpm_micro_amount += increment_micro_amount - + return prices def get_prices_summary_string(prices_array, precision=2): @@ -94,3 +94,68 @@ def get_prices_summary_string(prices_array, precision=2): ) return summary + +prebid_bucketing_schemes = { + "low": [{ + 'precision': 2, + 'min' : 0, + 'max' : 5, + 'increment': 0.50, + }], + "medium": [{ + 'precision': 2, + 'min' : 0, + 'max' : 20, + 'increment': 0.10, + }], + # High will try to create too many line items at once resulting in an error + #"high": [{ + # 'precision': 2, + # 'min' : 0, + # 'max' : 20, + # 'increment': 0.01, + #}], + "auto": [ + { + 'precision': 2, + 'min' : 0, + 'max' : 3, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 3, + 'max' : 8, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 8, + 'max' : 20, + 'increment': 0.50, + }], + "dense": [{ + 'precision': 2, + 'min' : 0, + 'max' : 3, + 'increment': 0.01 + }, + { + 'precision': 2, + 'min' : 3, + 'max' : 8, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 8, + 'max' : 20, + 'increment': 0.50, + }] +} + +def get_prebid_standard_bucketing(bucketing_scheme): + if bucketing_scheme in prebid_bucketing_schemes: + return prebid_bucketing_schemes[bucketing_scheme] + else: + return None From 301e469e9c86eaaa93c4cc6cef3580c55af5b41d Mon Sep 17 00:00:00 2001 From: Luca Date: Wed, 3 Feb 2021 12:16:08 +0100 Subject: [PATCH 2/7] Update creative snippet --- dfp/creative_snippet.html | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dfp/creative_snippet.html b/dfp/creative_snippet.html index 14cc5f5..b3e2f7a 100644 --- a/dfp/creative_snippet.html +++ b/dfp/creative_snippet.html @@ -1,13 +1,14 @@ From d827b2ded03e744bb2463e48439810f5f7482d7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Mar 2021 21:46:41 +0000 Subject: [PATCH 3/7] Bump pyyaml from 5.1.2 to 5.4 Bumps [pyyaml](https://github.com/yaml/pyyaml) from 5.1.2 to 5.4. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/5.1.2...5.4) Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0e068af..5164aad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ future==0.17.1 googleads==25.0.0 mock==3.0.5 pycryptodome==3.9.0 -PyYAML==5.1.2 +PyYAML==5.4 From 211ab1373fb8c994d8860896d89ca049c04e63b6 Mon Sep 17 00:00:00 2001 From: Kevin Jennison Date: Wed, 22 Sep 2021 15:32:00 -0400 Subject: [PATCH 4/7] Update API version --- dfp/associate_line_items_and_creatives.py | 2 +- dfp/create_creatives.py | 2 +- dfp/create_custom_targeting.py | 4 ++-- dfp/create_line_items.py | 2 +- dfp/create_orders.py | 2 +- dfp/get_ad_units.py | 2 +- dfp/get_advertisers.py | 4 ++-- dfp/get_custom_targeting.py | 4 ++-- dfp/get_orders.py | 4 ++-- dfp/get_placements.py | 2 +- dfp/get_users.py | 2 +- tests_integration/helpers/archive_order_by_name.py | 2 +- tests_integration/helpers/get_advertiser_by_name.py | 2 +- tests_integration/helpers/get_custom_targeting_by_key_name.py | 4 ++-- tests_integration/helpers/get_line_items_for_order.py | 2 +- tests_integration/helpers/get_order_by_name.py | 2 +- tests_integration/helpers/get_placement_by_name.py | 2 +- tests_integration/integration_test_new_prebid_partner.py | 4 ++-- 18 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dfp/associate_line_items_and_creatives.py b/dfp/associate_line_items_and_creatives.py index 3ab4c1c..0bb5e57 100644 --- a/dfp/associate_line_items_and_creatives.py +++ b/dfp/associate_line_items_and_creatives.py @@ -19,7 +19,7 @@ def make_licas(line_item_ids, creative_ids, size_overrides=[]): """ dfp_client = get_client() lica_service = dfp_client.GetService( - 'LineItemCreativeAssociationService', version='v202008') + 'LineItemCreativeAssociationService', version='v202108') sizes = [] diff --git a/dfp/create_creatives.py b/dfp/create_creatives.py index 4e6e61e..d060125 100644 --- a/dfp/create_creatives.py +++ b/dfp/create_creatives.py @@ -21,7 +21,7 @@ def create_creatives(creatives): """ dfp_client = get_client() creative_service = dfp_client.GetService('CreativeService', - version='v202008') + version='v202108') creatives = creative_service.createCreatives(creatives) # Return IDs of created line items. diff --git a/dfp/create_custom_targeting.py b/dfp/create_custom_targeting.py index d0e6e0a..c348ea7 100644 --- a/dfp/create_custom_targeting.py +++ b/dfp/create_custom_targeting.py @@ -23,7 +23,7 @@ def create_targeting_key(name, display_name=None, key_type='FREEFORM'): dfp_client = get_client() custom_targeting_service = dfp_client.GetService('CustomTargetingService', - version='v202008') + version='v202108') if display_name is None: display_name = name @@ -60,7 +60,7 @@ def create_targeting_value(name, key_id): dfp_client = get_client() custom_targeting_service = dfp_client.GetService('CustomTargetingService', - version='v202008') + version='v202108') values_config = [ { diff --git a/dfp/create_line_items.py b/dfp/create_line_items.py index 3d0ac4a..e8aec6c 100755 --- a/dfp/create_line_items.py +++ b/dfp/create_line_items.py @@ -14,7 +14,7 @@ def create_line_items(line_items): an array: an array of created line item IDs """ dfp_client = get_client() - line_item_service = dfp_client.GetService('LineItemService', version='v202008') + line_item_service = dfp_client.GetService('LineItemService', version='v202108') line_items = line_item_service.createLineItems(line_items) # Return IDs of created line items. diff --git a/dfp/create_orders.py b/dfp/create_orders.py index 8295ae4..07c3c78 100755 --- a/dfp/create_orders.py +++ b/dfp/create_orders.py @@ -65,7 +65,7 @@ def create_order(order_name, advertiser_id, trafficker_id): create_order_config(name=order_name, advertiser_id=advertiser_id, trafficker_id=trafficker_id) ] - order_service = dfp_client.GetService('OrderService', version='v202008') + order_service = dfp_client.GetService('OrderService', version='v202108') orders = order_service.createOrders(orders) order = orders[0] diff --git a/dfp/get_ad_units.py b/dfp/get_ad_units.py index 8364869..2324182 100755 --- a/dfp/get_ad_units.py +++ b/dfp/get_ad_units.py @@ -27,7 +27,7 @@ def get_ad_unit_by_name(ad_unit_name): dfp_client = get_client() ad_unit_service = dfp_client.GetService('InventoryService', - version='v202008') + version='v202108') query = 'WHERE name = :name' values = [ diff --git a/dfp/get_advertisers.py b/dfp/get_advertisers.py index 39626d7..cd6a48e 100755 --- a/dfp/get_advertisers.py +++ b/dfp/get_advertisers.py @@ -25,7 +25,7 @@ def create_advertiser(name): an integer: the advertiser's DFP ID """ dfp_client = get_client() - company_service = dfp_client.GetService('CompanyService', version='v202008') + company_service = dfp_client.GetService('CompanyService', version='v202108') advertisers_config = [ { @@ -53,7 +53,7 @@ def get_advertiser_id_by_name(name): an integer: the advertiser's DFP ID """ dfp_client = get_client() - company_service = dfp_client.GetService('CompanyService', version='v202008') + company_service = dfp_client.GetService('CompanyService', version='v202108') # Filter by name. query = 'WHERE name = :name' diff --git a/dfp/get_custom_targeting.py b/dfp/get_custom_targeting.py index 72dd062..0df1437 100755 --- a/dfp/get_custom_targeting.py +++ b/dfp/get_custom_targeting.py @@ -21,7 +21,7 @@ def get_key_id_by_name(name): dfp_client = get_client() custom_targeting_service = dfp_client.GetService('CustomTargetingService', - version='v202008') + version='v202108') # Get a key by name. query = ('WHERE name = :name') @@ -57,7 +57,7 @@ def get_targeting_by_key_name(name): dfp_client = get_client() custom_targeting_service = dfp_client.GetService('CustomTargetingService', - version='v202008') + version='v202108') # Get a key by name. query = ('WHERE name = :name') diff --git a/dfp/get_orders.py b/dfp/get_orders.py index 5ccaa86..839da7a 100755 --- a/dfp/get_orders.py +++ b/dfp/get_orders.py @@ -22,7 +22,7 @@ def get_order_by_name(order_name): """ dfp_client = get_client() - order_service = dfp_client.GetService('OrderService', version='v202008') + order_service = dfp_client.GetService('OrderService', version='v202108') # Filter by name. query = 'WHERE name = :name' @@ -60,7 +60,7 @@ def get_all_orders(print_orders=False): dfp_client = get_client() # Initialize appropriate service. - order_service = dfp_client.GetService('OrderService', version='v202008') + order_service = dfp_client.GetService('OrderService', version='v202108') # Create a statement to select orders. statement = ad_manager.FilterStatement() diff --git a/dfp/get_placements.py b/dfp/get_placements.py index 0f64e27..ce4d2ff 100755 --- a/dfp/get_placements.py +++ b/dfp/get_placements.py @@ -27,7 +27,7 @@ def get_placement_by_name(placement_name): dfp_client = get_client() placement_service = dfp_client.GetService('PlacementService', - version='v202008') + version='v202108') query = 'WHERE name = :name' values = [ diff --git a/dfp/get_users.py b/dfp/get_users.py index 3c03fb8..e93141f 100755 --- a/dfp/get_users.py +++ b/dfp/get_users.py @@ -21,7 +21,7 @@ def get_user_id_by_email(email_address): an integer: the user's DFP ID """ dfp_client = get_client() - user_service = dfp_client.GetService('UserService', version='v202008') + user_service = dfp_client.GetService('UserService', version='v202108') # Filter by email address. query = 'WHERE email = :email' diff --git a/tests_integration/helpers/archive_order_by_name.py b/tests_integration/helpers/archive_order_by_name.py index 9f64fe6..1c447aa 100644 --- a/tests_integration/helpers/archive_order_by_name.py +++ b/tests_integration/helpers/archive_order_by_name.py @@ -17,7 +17,7 @@ def archive_order_by_name(order_name): None """ client = get_client() - order_service = client.GetService('OrderService', version='v202008') + order_service = client.GetService('OrderService', version='v202108') statement = (ad_manager.StatementBuilder() .Where('name = :name') diff --git a/tests_integration/helpers/get_advertiser_by_name.py b/tests_integration/helpers/get_advertiser_by_name.py index 45968fa..1fd172f 100644 --- a/tests_integration/helpers/get_advertiser_by_name.py +++ b/tests_integration/helpers/get_advertiser_by_name.py @@ -18,7 +18,7 @@ def get_advertiser_by_name(advertiser_name): client = get_client() company_service = client.GetService('CompanyService', - version='v202008') + version='v202108') statement = (ad_manager.StatementBuilder() .Where('name = :name') diff --git a/tests_integration/helpers/get_custom_targeting_by_key_name.py b/tests_integration/helpers/get_custom_targeting_by_key_name.py index 5e692f6..dd16e70 100644 --- a/tests_integration/helpers/get_custom_targeting_by_key_name.py +++ b/tests_integration/helpers/get_custom_targeting_by_key_name.py @@ -18,7 +18,7 @@ def get_key_by_name(key_name): client = get_client() custom_targeting_service = client.GetService('CustomTargetingService', - version='v202008') + version='v202108') statement = (ad_manager.StatementBuilder() .Where('name = :name') @@ -46,7 +46,7 @@ def get_custom_targeting_by_key_name(key_name): client = get_client() custom_targeting_service = client.GetService('CustomTargetingService', - version='v202008') + version='v202108') statement = (ad_manager.StatementBuilder() .Where('customTargetingKeyId = :customTargetingKeyId') diff --git a/tests_integration/helpers/get_line_items_for_order.py b/tests_integration/helpers/get_line_items_for_order.py index 4612bb3..d98cfd4 100644 --- a/tests_integration/helpers/get_line_items_for_order.py +++ b/tests_integration/helpers/get_line_items_for_order.py @@ -17,7 +17,7 @@ def get_line_items_for_order(order_id): """ print('Getting line items for order ID {0}...'.format(order_id)) client = get_client() - line_item_service = client.GetService('LineItemService', version='v202008') + line_item_service = client.GetService('LineItemService', version='v202108') statement = (ad_manager.StatementBuilder() .Where('OrderId = :order_id') .WithBindVariable('order_id', order_id)) diff --git a/tests_integration/helpers/get_order_by_name.py b/tests_integration/helpers/get_order_by_name.py index 9f9c405..90ff6de 100644 --- a/tests_integration/helpers/get_order_by_name.py +++ b/tests_integration/helpers/get_order_by_name.py @@ -20,7 +20,7 @@ def get_order_by_name(order_name): print('Getting order with order name {0}...'.format(order_name)) client = get_client() - order_service = client.GetService('OrderService', version='v202008') + order_service = client.GetService('OrderService', version='v202108') statement = (ad_manager.StatementBuilder() .Where('name = :name') diff --git a/tests_integration/helpers/get_placement_by_name.py b/tests_integration/helpers/get_placement_by_name.py index 8bb8a57..3fbea45 100644 --- a/tests_integration/helpers/get_placement_by_name.py +++ b/tests_integration/helpers/get_placement_by_name.py @@ -18,7 +18,7 @@ def get_placement_by_name(placement_name): client = get_client() placement_service = client.GetService('PlacementService', - version='v202008') + version='v202108') statement = (ad_manager.StatementBuilder() .Where('name = :name') diff --git a/tests_integration/integration_test_new_prebid_partner.py b/tests_integration/integration_test_new_prebid_partner.py index 41d2d48..806c54e 100644 --- a/tests_integration/integration_test_new_prebid_partner.py +++ b/tests_integration/integration_test_new_prebid_partner.py @@ -57,8 +57,8 @@ def tearDown(self): archive_order_by_name(order_name) # TODO: delete custom targeting keys and values - # https://developers.google.com/doubleclick-publishers/docs/reference/v202008/CustomTargetingService.DeleteCustomTargetingKeys - # https://developers.google.com/doubleclick-publishers/docs/reference/v202008/CustomTargetingService.DeleteCustomTargetingValues + # https://developers.google.com/doubleclick-publishers/docs/reference/v202108/CustomTargetingService.DeleteCustomTargetingKeys + # https://developers.google.com/doubleclick-publishers/docs/reference/v202108/CustomTargetingService.DeleteCustomTargetingValues @patch.multiple('settings', DFP_USER_EMAIL_ADDRESS=email, From 1526aa28daa8b64c3621ce11549a4aae7c02ed60 Mon Sep 17 00:00:00 2001 From: Kevin Jennison Date: Wed, 22 Sep 2021 15:40:49 -0400 Subject: [PATCH 5/7] Upgrade dependencies --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0e068af..baf6009 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -colorama==0.4.1 -future==0.17.1 -googleads==25.0.0 -mock==3.0.5 -pycryptodome==3.9.0 -PyYAML==5.1.2 +colorama==0.4.4 +future==0.18.2 +googleads==29.0.0 +mock==4.0.3 +pycryptodome==3.10.1 +PyYAML==5.4.1 From 95c9ebe89fe1397ff4bac2d074fb1774a8dc5388 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 7 Oct 2020 14:52:46 -0700 Subject: [PATCH 6/7] Add support of an array of price buckets and standard Prebid price granularities such as auto or dense --- settings.py | 8 ++-- tasks/add_new_prebid_partner.py | 33 ++++++++++++--- tasks/price_utils.py | 71 +++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 13 deletions(-) diff --git a/settings.py b/settings.py index 7348509..6194e94 100644 --- a/settings.py +++ b/settings.py @@ -80,10 +80,10 @@ PREBID_BIDDER_CODE = None -# Price buckets. This should match your Prebid settings for the partner. See: -# http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity -# FIXME: this should be an array of buckets. See: -# https://github.com/prebid/Prebid.js/blob/8fed3d7aaa814e67ca3efc103d7d306cab8c692c/src/cpmBucketManager.js +# Price buckets. This should match your Prebid settings for the partner. +# Can be an a single bucket, an array of buckerts or string for standard Prebid +# price granularities such as 'medium', 'dense' or 'auto' +# See: http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity PREBID_PRICE_BUCKETS = { 'precision': 2, 'min' : 0, diff --git a/tasks/add_new_prebid_partner.py b/tasks/add_new_prebid_partner.py index b3797d5..de0a928 100755 --- a/tasks/add_new_prebid_partner.py +++ b/tasks/add_new_prebid_partner.py @@ -6,6 +6,7 @@ import sys from builtins import input from pprint import pprint +from collections import OrderedDict from colorama import init @@ -29,6 +30,7 @@ get_prices_summary_string, micro_amount_to_num, num_to_str, + get_prebid_standard_bucketing ) # Colorama for cross-platform support for colored logging. @@ -317,15 +319,34 @@ def main(): if bidder_code is None: raise MissingSettingException('PREBID_BIDDER_CODE') - price_buckets = getattr(settings, 'PREBID_PRICE_BUCKETS', None) - if price_buckets is None: - raise MissingSettingException('PREBID_PRICE_BUCKETS') + price_buckets = None + price_buckets_config = getattr(settings, 'PREBID_PRICE_BUCKETS', None) + if price_buckets_config is None: + raise MissingSettingException('PREBID_PRICE_BUCKETS') + elif isinstance(price_buckets_config, dict): + price_buckets = [ price_buckets_config ] + elif isinstance(price_buckets_config, list): + price_buckets = price_buckets_config + elif isinstance(price_buckets_config, str): + price_buckets = get_prebid_standard_bucketing(price_buckets_config) + if price_buckets == None: + raise BadSettingException('Unknown price bucketing scheme \'{0}\' for PREBID_PRICE_BUCKETS'.format(price_buckets_config)) + else: + raise BadSettingException('The setting "PREBID_PRICE_BUCKETS" ' + 'is an invalid type.') + + for pb in price_buckets: + check_price_buckets_validity(pb) + + prices = [] + for pb in price_buckets: + prices.extend(get_prices_array(pb)) - check_price_buckets_validity(price_buckets) + # Remove duplicates price buckets + prices = list(OrderedDict.fromkeys(prices)) - prices = get_prices_array(price_buckets) prices_summary = get_prices_summary_string(prices, - price_buckets['precision']) + price_buckets[0]['precision']) logger.info( u""" diff --git a/tasks/price_utils.py b/tasks/price_utils.py index f0d4522..137d2ec 100644 --- a/tasks/price_utils.py +++ b/tasks/price_utils.py @@ -37,7 +37,7 @@ def num_to_str(num, precision=2): Returns: a string """ - return '%.{0}f'.format(str(precision)) % num + return '%.{0}f'.format(str(precision)) % num def get_prices_array(price_bucket): """ @@ -48,7 +48,7 @@ def get_prices_array(price_bucket): price_bucket (object): the price bucket configuration Returns: an array of integers: every price bucket cutoff from: - int(round(price_bucket['min'] * 10**6, precision)) to + int(round(price_bucket['min'] * 10**6, precision)) to int(round(price_bucket['max'] * 10**6, precision)) """ start_cpm = price_bucket['min'] if price_bucket['min'] >=0 else 0.00 @@ -65,7 +65,7 @@ def get_prices_array(price_bucket): while current_cpm_micro_amount <= end_cpm_micro_amount: prices.append(current_cpm_micro_amount) current_cpm_micro_amount += increment_micro_amount - + return prices def get_prices_summary_string(prices_array, precision=2): @@ -94,3 +94,68 @@ def get_prices_summary_string(prices_array, precision=2): ) return summary + +prebid_bucketing_schemes = { + "low": [{ + 'precision': 2, + 'min' : 0, + 'max' : 5, + 'increment': 0.50, + }], + "medium": [{ + 'precision': 2, + 'min' : 0, + 'max' : 20, + 'increment': 0.10, + }], + # High will try to create too many line items at once resulting in an error + #"high": [{ + # 'precision': 2, + # 'min' : 0, + # 'max' : 20, + # 'increment': 0.01, + #}], + "auto": [ + { + 'precision': 2, + 'min' : 0, + 'max' : 3, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 3, + 'max' : 8, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 8, + 'max' : 20, + 'increment': 0.50, + }], + "dense": [{ + 'precision': 2, + 'min' : 0, + 'max' : 3, + 'increment': 0.01 + }, + { + 'precision': 2, + 'min' : 3, + 'max' : 8, + 'increment': 0.05, + }, + { + 'precision': 2, + 'min' : 8, + 'max' : 20, + 'increment': 0.50, + }] +} + +def get_prebid_standard_bucketing(bucketing_scheme): + if bucketing_scheme in prebid_bucketing_schemes: + return prebid_bucketing_schemes[bucketing_scheme] + else: + return None From b4c2326925105feb6539dea617915948b58420c8 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Mon, 28 Feb 2022 12:01:01 -0800 Subject: [PATCH 7/7] Add tests and update README --- README.md | 2 +- settings.py | 4 +- tests/test_add_new_prebid_partner.py | 69 ++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e23441f..3ef4f51 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Setting | Description | Type `DFP_TARGETED_PLACEMENT_NAMES` | The names of GAM placements the line items should target | array of strings `DFP_PLACEMENT_SIZES` | The creative sizes for the targeted placements | array of objects (e.g., `[{'width': '728', 'height': '90'}]`) `PREBID_BIDDER_CODE` | The value of [`hb_bidder`](http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.bidderSettings) for this partner | string -`PREBID_PRICE_BUCKETS` | The [price granularity](http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity); used to set `hb_pb` for each line item | object +`PREBID_PRICE_BUCKETS` | The [price granularity](https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-Price-Granularity); used to set `hb_pb` for each line item. Can use Prebid standard granularities such as `medium` or `dense`, a single custom CPM bucket object or an array of custom CPM bucket objects | object, arary or string `DFP_VIDEO_AD_TYPE` | Set to true to create video ad units and creatives | boolean `DFP_VAST_REDIRECT_URL` | The redirect URL to use for video ad creatives (only used if `DFP_VIDEO_AD_TYPE` is set to True) | string diff --git a/settings.py b/settings.py index 6194e94..b785f1b 100644 --- a/settings.py +++ b/settings.py @@ -81,9 +81,9 @@ PREBID_BIDDER_CODE = None # Price buckets. This should match your Prebid settings for the partner. -# Can be an a single bucket, an array of buckerts or string for standard Prebid +# Can be an a single bucket, an array of buckets or string for standard Prebid # price granularities such as 'medium', 'dense' or 'auto' -# See: http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.setPriceGranularity +# See: https://docs.prebid.org/dev-docs/publisher-api-reference/setConfig.html#setConfig-Price-Granularity PREBID_PRICE_BUCKETS = { 'precision': 2, 'min' : 0, diff --git a/tests/test_add_new_prebid_partner.py b/tests/test_add_new_prebid_partner.py index 6a2b223..8efeb52 100644 --- a/tests/test_add_new_prebid_partner.py +++ b/tests/test_add_new_prebid_partner.py @@ -185,6 +185,75 @@ def test_user_confirmation_accepted(self, mock_input, tasks.add_new_prebid_partner.main() mock_setup_partners.assert_called_once() + @patch('tasks.add_new_prebid_partner.setup_partner') + @patch('tasks.add_new_prebid_partner.input', return_value='y') + def test_prebid_medium_price_bucket_granularity(self, mock_input, mock_setup_partners, mock_dfp_client): + """ + Sets prices buckets correctly based on prebid auto granularity + """ + settings.PREBID_PRICE_BUCKETS="medium" + + tasks.add_new_prebid_partner.main() + args, kwargs = mock_setup_partners.call_args + self.assertEqual(len(args[7]), 201) + self.assertEqual(args[7][100], 10000000) + self.assertEqual(args[7][101], 10100000) + self.assertEqual(args[7][-1], 20000000) + + @patch('tasks.add_new_prebid_partner.setup_partner') + @patch('tasks.add_new_prebid_partner.input', return_value='y') + def test_prebid_auto_price_bucket_granularity(self, mock_input, mock_setup_partners, mock_dfp_client): + """ + Sets prices buckets correctly based on prebid auto granularity + """ + settings.PREBID_PRICE_BUCKETS="auto" + + tasks.add_new_prebid_partner.main() + args, kwargs = mock_setup_partners.call_args + self.assertEqual(len(args[7]), 185) + self.assertEqual(args[7][100], 5000000) + self.assertEqual(args[7][-1], 20000000) + + @patch('tasks.add_new_prebid_partner.setup_partner') + @patch('tasks.add_new_prebid_partner.input', return_value='y') + def test_prebid_dense_price_bucket_granularity(self, mock_input, mock_setup_partners, mock_dfp_client): + """ + Sets prices buckets correctly based on prebid dense granularity + """ + settings.PREBID_PRICE_BUCKETS="dense" + + tasks.add_new_prebid_partner.main() + args, kwargs = mock_setup_partners.call_args + self.assertEqual(len(args[7]), 425) + self.assertEqual(args[7][100], 1000000) + self.assertEqual(args[7][-1], 20000000) + + @patch('tasks.add_new_prebid_partner.setup_partner') + @patch('tasks.add_new_prebid_partner.input', return_value='y') + def test_price_bucket_array(self, mock_input, mock_setup_partners, mock_dfp_client): + """ + Sets prices buckets correctly when passing an array of buckets + """ + settings.PREBID_PRICE_BUCKETS=[ { + 'precision': 2, + 'min' : 0, + 'max' : 1, + 'increment': 0.01, + }, + { + 'precision': 2, + 'min' : 1, + 'max' : 2, + 'increment': 0.10, + }] + + tasks.add_new_prebid_partner.main() + args, kwargs = mock_setup_partners.call_args + self.assertEqual(len(args[7]), 111) + self.assertEqual(args[7][50], 500000) + self.assertEqual(args[7][105], 1500000) + self.assertEqual(args[7][-1], 2000000) + @patch('settings.DFP_NUM_CREATIVES_PER_LINE_ITEM', 5, create=True) @patch('tasks.add_new_prebid_partner.setup_partner') @patch('tasks.add_new_prebid_partner.input', return_value='y')