From 27f104128a39f40f58d9aedd21e3a98f6811b7de Mon Sep 17 00:00:00 2001 From: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Date: Wed, 15 Feb 2023 16:20:35 -0800 Subject: [PATCH 01/28] Update str converter (#78) --- pinterest/ads/conversion_tags.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pinterest/ads/conversion_tags.py b/pinterest/ads/conversion_tags.py index f30b17e..ad3bd80 100644 --- a/pinterest/ads/conversion_tags.py +++ b/pinterest/ads/conversion_tags.py @@ -200,7 +200,7 @@ def get_all( def _map_function(obj): return ConversionTag( - ad_account_id = ad_account_id, + ad_account_id = str(ad_account_id), conversion_tag_id = obj.get('id'), client = client, _model_data = obj.to_dict() @@ -235,7 +235,7 @@ def get_page_visit_conversion_tag_events( Returns: list[ConversionEventResponse]: List of ConversionTagEvent """ - params = {"ad_account_id" : ad_account_id} + params = {"ad_account_id" : str(ad_account_id)} def _map_function(obj): return ConversionEventResponse( @@ -275,7 +275,7 @@ def get_ocpm_eligible_conversion_tag_events( list[ConversionEventResponse]: List of ConversionTagEvent """ api_response = ConversionTagsApi(api_client=cls._get_client(client)).ocpm_eligible_conversion_tags_get( - ad_account_id = ad_account_id, + ad_account_id = str(ad_account_id), **kwargs, ) From 7d99b035b21a5064fce269d61b1af93d2669cee2 Mon Sep 17 00:00:00 2001 From: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:40:46 -0800 Subject: [PATCH 02/28] Fix conversion tag null config (#82) * Update generated client to 0.1.7 * Fix null configs for converion_tag * Linting * Feature fix/conversion tag configs (#83) * Linting * Update test --- integration_tests/ads/test_conversion_tags.py | 32 +++++++++++++++++- pinterest/ads/conversion_tags.py | 33 +++++++++++++++---- pinterest/organic/pins.py | 6 ++-- requirements.txt | 2 +- setup.py | 2 +- tests/src/pinterest/ads/test_ad_groups.py | 1 + 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/integration_tests/ads/test_conversion_tags.py b/integration_tests/ads/test_conversion_tags.py index 6fb6f35..8fb0689 100644 --- a/integration_tests/ads/test_conversion_tags.py +++ b/integration_tests/ads/test_conversion_tags.py @@ -18,12 +18,42 @@ def test_create_conversion_tag_success(self): """ conversion_tag = ConversionTag.create( ad_account_id = DEFAULT_AD_ACCOUNT_ID, - name = "Test Conversion Tag" + name = "Test Conversion Tag", + aem_enabled = None, + md_frequency = None, + aem_fnln_enabled = None, + aem_ph_enabled = None, + aem_ge_enabled = None, + aem_db_enabled = None, + aem_loc_enabled = None, ) assert conversion_tag assert getattr(conversion_tag, "_id") assert getattr(conversion_tag, "_name") == "Test Conversion Tag" + assert getattr(conversion_tag.configs, "aem_enabled") == False + + def test_create_conversion_tag_with_configs_success(self): + """ + Test creating a new Conversion Tag successfully + """ + conversion_tag = ConversionTag.create( + ad_account_id = DEFAULT_AD_ACCOUNT_ID, + name = "Test Conversion Tag", + aem_enabled = True, + md_frequency = 1.2, + aem_fnln_enabled = None, + aem_ph_enabled = None, + aem_ge_enabled = None, + aem_db_enabled = None, + aem_loc_enabled = None, + ) + + assert conversion_tag + assert getattr(conversion_tag, "_id") + assert getattr(conversion_tag, "_name") == "Test Conversion Tag" + assert getattr(conversion_tag.configs, "aem_enabled") == True + assert getattr(conversion_tag.configs, "md_frequency") == 1.2 class TestGetConversionTag(BaseTestCase): """ diff --git a/pinterest/ads/conversion_tags.py b/pinterest/ads/conversion_tags.py index ad3bd80..7d0d81f 100644 --- a/pinterest/ads/conversion_tags.py +++ b/pinterest/ads/conversion_tags.py @@ -109,13 +109,13 @@ def create( cls, ad_account_id : str, name : str, - aem_enabled : bool = False, - md_frequency : float = 0.0, - aem_fnln_enabled : bool = False, - aem_ph_enabled : bool = False, - aem_ge_enabled : bool = False, - aem_db_enabled : bool = False, - aem_loc_enabled : bool = False, + aem_enabled : bool = None, + md_frequency : float = None, + aem_fnln_enabled : bool = None, + aem_ph_enabled : bool = None, + aem_ge_enabled : bool = None, + aem_db_enabled : bool = None, + aem_loc_enabled : bool = None, client:PinterestSDKClient = None, **kwargs ) -> ConversionTag: @@ -147,6 +147,24 @@ def create( Args: ad_account_id (str): ConversionTag's Ad Account ID name (str): ConversionTag name + aem_enabled (bool=False, Nullable): Whether Automatic Enhanced Match email is enabled. See\ + Enhanced match for more information. + + md_frequency (float=1.0, Nullable): Metadata ingestion frequency. + aem_fnln_enabled (bool=False, Nullable): Whether Automatic Enhanced Match name is enabled. See\ + Enhanced match for more information. + + aem_ph_enabled (bool=False, Nullable): Whether Automatic Enhanced Match phone is enabled. See\ + Enhanced match for more information. + + aem_ge_enabled (bool=False, Nullable): Whether Automatic Enhanced Match gender is enabled. See\ + Enhanced match for more information. + + aem_db_enabled (bool=False, Nullable): Whether Automatic Enhanced Match birthdate is enabled. See\ + Enhanced match for more information. + + aem_loc_enabled (bool=False, Nullable): Whether Automatic Enhanced Match location is enabled. See\ + Enhanced match for more information. Returns: ConversionTag: ConversionTag Object @@ -171,6 +189,7 @@ def create( map_fn = lambda obj : obj, ) + return cls( ad_account_id = response.ad_account_id, conversion_tag_id = response.id, diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index e1693c4..a36c620 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -152,7 +152,6 @@ def create( client:PinterestSDKClient = None, **kwargs ) -> Pin: - # pylint: disable=too-many-arguments """ Create a Pin on a board or board section owned by the "operation user_account". @@ -197,6 +196,7 @@ def create( Returns: Pin: Pin object """ + # pylint: disable=too-many-arguments, no-value-for-parameter if not client: client = cls._get_client() @@ -212,9 +212,9 @@ def create( board_section_id=board_section_id, media_source=media_source, parent_pin_id=parent_pin_id, - **kwargs + **kwargs, ) - ) + ) # pylint: disable=no-value-for-parameter verify_api_response(api_response) return Pin(pin_id=getattr(api_response, "id"), client=client) diff --git a/requirements.txt b/requirements.txt index 00a7736..101fee2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Pinterest-Generated-Client==0.1.5 +Pinterest-Generated-Client==0.1.7 python-dateutil==2.8.2 six==1.16.0 urllib3==1.26.12 diff --git a/setup.py b/setup.py index 2fc5213..edadc0c 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def _get_prod_version(): "python-dateutil", "python-dotenv==0.20.0", "six==1.16.0", - "Pinterest-Generated-Client==0.1.5" + "Pinterest-Generated-Client==0.1.7" ] long_description = (Path(__file__).parent / "README.md").read_text() diff --git a/tests/src/pinterest/ads/test_ad_groups.py b/tests/src/pinterest/ads/test_ad_groups.py index 396c1b3..0b795cc 100644 --- a/tests/src/pinterest/ads/test_ad_groups.py +++ b/tests/src/pinterest/ads/test_ad_groups.py @@ -84,6 +84,7 @@ def test_create_ad_group_model_new_ad_group(self, ad_group_get_mock, ad_group_cr campaign_id=self.test_campaign_id, billable_event='CLICKTHROUGH', name='SDK_TEST_CLIENT_ADGROUP', + auto_targeting_enabled=False ) assert created_ad_group From 17cd8985d2175d65cfae55d63e602ecf90d18979 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Wed, 22 Mar 2023 17:22:59 -0700 Subject: [PATCH 03/28] Bug Fix logic for getting all pages in _list() method of BaseModel (#85) Calling .get_all() would return a list of entities and a Bookmark model that returns a new entity_list on each Bookmark.get_next() call which performs API calls only when necessary to fetch a new set, and eventually returns all entities for the parent. --- pinterest/utils/base_model.py | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/pinterest/utils/base_model.py b/pinterest/utils/base_model.py index 0f85ef0..5152a92 100644 --- a/pinterest/utils/base_model.py +++ b/pinterest/utils/base_model.py @@ -132,28 +132,21 @@ def _list( items = [] bookmark = None - # Python do while, always execute at least 1 - while True: - http_response = cls._call_method( - cls._get_api_instance(api, client), - list_fn.__name__, - params, - **kwargs - ) + http_response = cls._call_method( + cls._get_api_instance(api, client), + list_fn.__name__, + params, + **kwargs + ) - verify_api_response(http_response) - - items = http_response.get('items', []) - bookmark = http_response.get('bookmark', None) - # Only execute 1 if page size is set - if page_size is not None: - break - # Set the new bookmark - if bookmark is not None: - kwargs["bookmark"] = bookmark - # if bookmark is none this mean all items is extracted. - else: - break + verify_api_response(http_response) + + items = http_response.get('items', []) + bookmark = http_response.get('bookmark', None) + + # Set the new bookmark + if bookmark is not None: + kwargs["bookmark"] = bookmark kwargs.update(params) bookmark_model = Bookmark( From e93b9bbcaa58e08a5cda4064a6bd672897ea53ba Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Thu, 23 Mar 2023 14:12:30 -0700 Subject: [PATCH 04/28] Bug Fix: Pass constructor client to `PinterestBaseModel` init (#87) As a result of the `PinterestBaseModel.__init__` getting called without the passed in client from the `Ad` model constructor, the requests failed resulting in an Auth error. This PR should fix the problem. --- pinterest/ads/ads.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 1a9ef7a..6695953 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -70,6 +70,7 @@ def __init__( generated_api_get_fn="ads_get", generated_api_get_fn_args={"ad_account_id": ad_account_id, "ad_id": ad_id}, model_attribute_types = AdResponse.openapi_types, + client=client, ) self._ad_account_id = str(ad_account_id) self._populate_fields(**kwargs) From c8225a25036870dbdcb18e1391c16274f396432d Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Tue, 28 Mar 2023 14:31:28 -0700 Subject: [PATCH 05/28] pass the client to the bookmark get_next function (#89) --- pinterest/utils/bookmark.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pinterest/utils/bookmark.py b/pinterest/utils/bookmark.py index d05a868..87364f3 100644 --- a/pinterest/utils/bookmark.py +++ b/pinterest/utils/bookmark.py @@ -43,6 +43,7 @@ def get_next(self) -> tuple[list[object], Bookmark]: Bookmark: Bookmark Object for pagination if present, else None. """ self.model_fn_args['bookmark'] = self.bookmark_token + self.model_fn_args['client'] = self.client if 'kwargs' in self.model_fn_args: kwargs = self.model_fn_args.get('kwargs') del self.model_fn_args['kwargs'] From b603f9176cc634828045b8a40f670785bfb44237 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Wed, 29 Mar 2023 10:18:19 -0700 Subject: [PATCH 06/28] Change model_fn for bookmark to list_fn instead of hardcodeing (#90) --- pinterest/utils/base_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinterest/utils/base_model.py b/pinterest/utils/base_model.py index 5152a92..0663066 100644 --- a/pinterest/utils/base_model.py +++ b/pinterest/utils/base_model.py @@ -152,7 +152,7 @@ def _list( bookmark_model = Bookmark( bookmark_token=bookmark, model=cls, - model_fn='get_all', + model_fn=list_fn.__name__, model_fn_args=kwargs, client=client, ) if bookmark else None From fb0afed4994f6857286be58e9b54d55bace00270 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Wed, 29 Mar 2023 12:29:30 -0700 Subject: [PATCH 07/28] Revert "Change model_fn for bookmark to list_fn instead of hardcodeing (#90)" (#94) This reverts commit b603f9176cc634828045b8a40f670785bfb44237. --- pinterest/utils/base_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinterest/utils/base_model.py b/pinterest/utils/base_model.py index 0663066..5152a92 100644 --- a/pinterest/utils/base_model.py +++ b/pinterest/utils/base_model.py @@ -152,7 +152,7 @@ def _list( bookmark_model = Bookmark( bookmark_token=bookmark, model=cls, - model_fn=list_fn.__name__, + model_fn='get_all', model_fn_args=kwargs, client=client, ) if bookmark else None From 58098203367c3965a950c35e5d88b6c4a951f672 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Wed, 29 Mar 2023 16:08:01 -0700 Subject: [PATCH 08/28] Add cron workflow for integration test (#97) --- .github/workflows/cron_integrationtests.yml | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/cron_integrationtests.yml diff --git a/.github/workflows/cron_integrationtests.yml b/.github/workflows/cron_integrationtests.yml new file mode 100644 index 0000000..923c4d3 --- /dev/null +++ b/.github/workflows/cron_integrationtests.yml @@ -0,0 +1,37 @@ +name: Cron Integration Tests + +on: + schedule: + - cron: "0 17 * * *" + +jobs: + build: + environment: integ + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Run all integration tests + env: + PINTEREST_REFRESH_ACCESS_TOKEN: ${{ secrets.CI_REFRESH_ACCESS_TOKEN }} + PINTEREST_APP_SECRET: ${{ secrets.CI_APP_SECRET }} + PINTEREST_APP_ID: ${{ secrets.CI_APP_ID }} + PINTEREST_API_URI: ${{ secrets.CI_HOST_URI }} + CONVERSION_ACCESS_TOKEN: ${{ secrets.CI_CONVERSION_ACCESS_TOKEN }} + DEFAULT_BOARD_ID: ${{ secrets.CI_DEFAULT_BOARD_ID }} + DEFAULT_BOARD_NAME: ${{ secrets.CI_DEFAULT_BOARD_NAME }} + DEFAULT_PIN_ID: ${{ secrets.CI_DEFAULT_PIN_ID }} + DEFAULT_BOARD_SECTION_ID: ${{ secrets.CI_DEFAULT_BOARD_SECTION_ID }} + OWNER_USER_ID: ${{ secrets.CI_OWNER_USER_ID }} + DEFAULT_AD_ACCOUNT_ID: ${{ secrets.CI_DEFAULT_AD_ACCOUNT_ID }} + run: | + python -m pip install --upgrade pip + make install_dev + make integration_tests From c8ff3c9e669f99b8aaf22dbf7aceda628b74de5a Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:24:11 -0700 Subject: [PATCH 09/28] Add missing attributes to AdAccount model --- pinterest/ads/ad_accounts.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index cd8b8ad..084ec16 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -44,6 +44,8 @@ def __init__( self._country = None self._currency = None self._permissions = None + self._created_time = None + self._updated_time = None PinterestBaseModel.__init__( self, @@ -86,6 +88,16 @@ def permissions(self) -> list[str]: # pylint: disable=missing-function-docstring return self._permissions + @property + def created_time(self) -> int: + # pylint: disable=missing-function-docstring + return self._created_time + + @property + def updated_time(self) -> int: + # pylint: disable=missing-function-docstring + return self._updated_time + @classmethod def create( cls, From 4cd5586d7cc3cbfe769816263a27bd28a0c2331b Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:24:30 -0700 Subject: [PATCH 10/28] Update spend cap for campaigns based on server side validation --- integration_tests/utils/ads_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/utils/ads_utils.py b/integration_tests/utils/ads_utils.py index bc766e2..f791d3d 100644 --- a/integration_tests/utils/ads_utils.py +++ b/integration_tests/utils/ads_utils.py @@ -108,7 +108,7 @@ def __init__(self, client=None): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) self.campaign_id = self.campaign._id @@ -124,7 +124,7 @@ def get_default_params(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) def create_new_campaign(self, **kwargs): From 1f1e1f69e9b4f85f46f2dc78d065d1fdf83490ef Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:32:54 -0700 Subject: [PATCH 11/28] Update campaign model properties --- pinterest/ads/campaigns.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index d18bb6e..f5ebf5f 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -55,6 +55,7 @@ def __init__( self._type = None self._is_flexible_daily_budgets = None self._is_campaign_budget_optimization = None + self._summary_status = None PinterestBaseModel.__init__( self, @@ -148,6 +149,11 @@ def is_campaign_budget_optimization(self) -> bool: # pylint: disable=missing-function-docstring return self._is_campaign_budget_optimization + @property + def summary_status(self) -> str: + # pylint: disable=missing-function-docstring + return self._summary_status + @classmethod def create( From e675756586f4cc1c1494a56ca6816d2818ea8c6a Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:35:46 -0700 Subject: [PATCH 12/28] Update ad_group model properties and utils --- integration_tests/utils/ads_utils.py | 4 ++++ pinterest/ads/ad_groups.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/integration_tests/utils/ads_utils.py b/integration_tests/utils/ads_utils.py index f791d3d..5aaa22d 100644 --- a/integration_tests/utils/ads_utils.py +++ b/integration_tests/utils/ads_utils.py @@ -142,6 +142,8 @@ def __init__(self, client=None): billable_event="IMPRESSION", client=self.test_client, name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) self.ad_group_id = self.ad_group._id @@ -158,6 +160,8 @@ def get_default_params(self): billable_event="IMPRESSION", client=self.test_client, name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) def create_new_ad_group(self, **kwargs): diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index a6150e5..492e5e4 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -63,6 +63,7 @@ def __init__( self._summary_status = None self._feed_profile_id = None self._dca_assets = None + self._optimization_goal_metadata = None PinterestBaseModel.__init__( self, @@ -201,6 +202,11 @@ def dca_assets(self): #pylint: disable=missing-function-docstring return self._dca_assets + @property + def optimization_goal_metadata(self): + #pylint: disable=missing-function-docstring + return self._optimization_goal_metadata + @classmethod def create( From 70c159c5fd01aa8a63b7470b7bf94a9e7c3b347f Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:50:16 -0700 Subject: [PATCH 13/28] Update pin_creation in Pin model --- pinterest/organic/pins.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index a36c620..56e6d62 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -5,6 +5,7 @@ from openapi_generated.pinterest_client.api.pins_api import PinsApi from openapi_generated.pinterest_client.model.pin import Pin as GeneratedPin +from openapi_generated.pinterest_client.model.pin_create import PinCreate as GeneratedPinCreate from openapi_generated.pinterest_client.model.inline_object import InlineObject from pinterest.client import PinterestSDKClient @@ -202,7 +203,7 @@ def create( client = cls._get_client() api_response = PinsApi(client).pins_create( - pin=GeneratedPin( + pin_create=GeneratedPinCreate( link=link, title=title, description=description, From 44d41a2f936bb035b966d1014b749505ae917e6e Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 11:51:19 -0700 Subject: [PATCH 14/28] update .gitignore to exclude dev logs --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e53ae66..dd08ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ target/ .idea # Config files -.env \ No newline at end of file +.env +http_logs.txt \ No newline at end of file From 71fd145b063594486aabff104068f8205370e3d5 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Wed, 29 Mar 2023 12:08:00 -0700 Subject: [PATCH 15/28] Update make file phony targets As integration_tests was a folder too, a PHONY target needed to be added for the makefile target. --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 894858a..4496108 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: integration_tests lint unit_tests + install: @echo pip install pip install -r requirements.txt @@ -55,4 +57,4 @@ flake: flake8 . --count --show-source --statistics flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics -lint: pylint flake \ No newline at end of file +lint: pylint flake From 3f5104b497d4ecb3786ecfb95d6f88b32d31386c Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 12:30:46 -0700 Subject: [PATCH 16/28] Change spend caps for campaign tests --- integration_tests/ads/test_campaigns.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/integration_tests/ads/test_campaigns.py b/integration_tests/ads/test_campaigns.py index 9d5c287..f56cb01 100644 --- a/integration_tests/ads/test_campaigns.py +++ b/integration_tests/ads/test_campaigns.py @@ -30,7 +30,7 @@ def test_create_campaign_success(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) assert campaign @@ -52,7 +52,7 @@ def test_create_campaign_failure_without_budget(self): self.assertRaisesRegex( SdkException, - r"^[\S\s]*campaigns should have a campaign level budget[\S\s]*$", + r"^[\S\s]*code 2384[\S\s]*$", Campaign.create, **campaign_arguments) @@ -65,7 +65,7 @@ def test_create_campaign_failure_incorrect_objective_type(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="INCORRECT_OBJECTIVE_TYPE", - daily_spend_cap=10, + daily_spend_cap=10000000, ) with self.assertRaisesRegex(ApiValueError, "Invalid"): Campaign.create(**campaign_arguments) @@ -184,6 +184,8 @@ def test_list_ad_groups(self): campaign_id=getattr(campaign, '_id'), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) ) From ea209aeb0fede40c22a52dadf7d75bd5e86293d7 Mon Sep 17 00:00:00 2001 From: rushabhvaria Date: Wed, 29 Mar 2023 12:57:18 -0700 Subject: [PATCH 17/28] Change config for base model creating an ad_group --- integration_tests/ads/test_ad_groups.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration_tests/ads/test_ad_groups.py b/integration_tests/ads/test_ad_groups.py index ebbcd24..0a339c7 100644 --- a/integration_tests/ads/test_ad_groups.py +++ b/integration_tests/ads/test_ad_groups.py @@ -23,6 +23,8 @@ def test_create_ad_group_success(self): campaign_id=getattr(self.ad_group_utils.campaign, "_id"), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) assert ad_group @@ -136,6 +138,8 @@ def test_get_list_with_campaign_ids_success(self): campaign_id=getattr(new_campaign, "_id"), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) new_ad_groups = AdGroup.get_all( From 0e2649485659c2e9a6997e238755a4fd3d6b4fb2 Mon Sep 17 00:00:00 2001 From: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:16:52 -0700 Subject: [PATCH 18/28] Fix model and tests: conversion tag, conversion event and pin (#96) * Update conversion tag * Update conversion event * Update pin model --- .../ads/test_conversion_events.py | 10 ++- integration_tests/ads/test_conversion_tags.py | 4 + integration_tests/organic/test_pins.py | 6 +- pinterest/ads/conversion_events.py | 17 ++-- pinterest/ads/conversion_tags.py | 14 ++-- pinterest/organic/pins.py | 24 +++--- .../pinterest/ads/test_conversion_events.py | 3 +- .../src/pinterest/ads/test_conversion_tags.py | 77 ++++++++++++++----- tests/src/pinterest/organic/test_pins.py | 2 +- 9 files changed, 105 insertions(+), 52 deletions(-) diff --git a/integration_tests/ads/test_conversion_events.py b/integration_tests/ads/test_conversion_events.py index 8179592..35d7df1 100644 --- a/integration_tests/ads/test_conversion_events.py +++ b/integration_tests/ads/test_conversion_events.py @@ -23,6 +23,8 @@ def test_send_conversion_success(self): raw_user_data = dict( em = ["964bbaf162703657e787eb4455197c8b35c18940c75980b0285619fe9b8acec8"] #random hash256 ) + raw_custom_data = dict() + conversion_events = [ Conversion.create_conversion_event( event_name = "add_to_cart", @@ -30,6 +32,7 @@ def test_send_conversion_success(self): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data= raw_custom_data, ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] @@ -64,6 +67,8 @@ def test_send_conversion_fail(self): raw_user_data = dict( em = ["test_non_hashed_email@pinterest.com"] ) + raw_custom_data = dict() + conversion_events = [ Conversion.create_conversion_event( event_name = "add_to_cart", @@ -71,6 +76,7 @@ def test_send_conversion_fail(self): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data = raw_custom_data, ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] @@ -87,5 +93,5 @@ def test_send_conversion_fail(self): assert response.num_events_processed == 2 assert len(response.events) == 2 - assert response.events[0].warning_message == "'em' is not in sha256 hashed format." - assert response.events[1].warning_message == "'em' is not in sha256 hashed format." + assert response.events[0].warning_message #warning returned + assert response.events[1].warning_message #warning returned diff --git a/integration_tests/ads/test_conversion_tags.py b/integration_tests/ads/test_conversion_tags.py index 8fb0689..b21f540 100644 --- a/integration_tests/ads/test_conversion_tags.py +++ b/integration_tests/ads/test_conversion_tags.py @@ -97,6 +97,10 @@ def test_get_list_success(self): conversion_tags = ConversionTag.get_all(ad_account_id = ad_account_id) assert len(conversion_tags) == NUMBER_OF_NEW_CONVERSION_TAG + for conversion_tag in conversion_tags: + assert conversion_tag.name == "SDK_TEST_CONVERSION_TAG" + assert conversion_tag.ad_account_id == ad_account_id + class TestGetPageVsitConversionTag(BaseTestCase): """ diff --git a/integration_tests/organic/test_pins.py b/integration_tests/organic/test_pins.py index 52719c1..7ce8379 100644 --- a/integration_tests/organic/test_pins.py +++ b/integration_tests/organic/test_pins.py @@ -12,6 +12,7 @@ from integration_tests.base_test import BaseTestCase from integration_tests.config import DEFAULT_PIN_ID from integration_tests.config import DEFAULT_BOARD_ID +from integration_tests.config import DEFAULT_AD_ACCOUNT_ID from integration_tests.config import DEFAULT_BOARD_SECTION_ID class TestGetPin(BaseTestCase): @@ -23,12 +24,12 @@ class TestGetPin(BaseTestCase): ({ 'description': 'PIN is PUBLIC', 'pin_id': DEFAULT_PIN_ID, - 'ad_account_id': None, + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, },), ({ 'description': 'PIN in PROTECTED BOARD', 'pin_id': DEFAULT_PIN_ID, - 'ad_account_id': None, + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, },) ] ) @@ -62,6 +63,7 @@ def test_create_and_delete_pin_success(self): "data": "string", 'url':'https://i.pinimg.com/564x/28/75/e9/2875e94f8055227e72d514b837adb271.jpg' }, + ad_account_id = DEFAULT_AD_ACCOUNT_ID, client=self.test_client ) diff --git a/pinterest/ads/conversion_events.py b/pinterest/ads/conversion_events.py index 90b0a39..f4904d2 100644 --- a/pinterest/ads/conversion_events.py +++ b/pinterest/ads/conversion_events.py @@ -3,11 +3,12 @@ """ from __future__ import annotations -from openapi_generated.pinterest_client.api.conversion_events_api import ConversionEventsApi from openapi_generated.pinterest_client.model.conversion_events import ConversionEvents +from openapi_generated.pinterest_client.api.conversion_events_api import ConversionEventsApi from openapi_generated.pinterest_client.model.conversion_events_data import ConversionEventsData -from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents from openapi_generated.pinterest_client.model.conversion_events_user_data import ConversionEventsUserData +from openapi_generated.pinterest_client.model.conversion_events_custom_data import ConversionEventsCustomData +from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents from pinterest.client import PinterestSDKClient from pinterest.utils.base_model import PinterestBaseModel @@ -26,6 +27,7 @@ def create_conversion_event( event_time : int, event_id : str, user_data : dict, + custom_data : dict, event_source_url : str = None, partner_name : str = None, app_id : str = None, @@ -39,7 +41,7 @@ def create_conversion_event( language : str = None, **kwargs ) -> ConversionEventsData: - """ Create Conversion Event Data to be sent + """ Create Conversion Event Data to be sent. Args: event_name (str): The type of the user event, Enum: "add_to_cart", "checkout", "custom", @@ -51,6 +53,7 @@ def create_conversion_event( between events ingested via both the conversion API and Pinterest tracking user_data (dict): Object containing customer information data. Note, it is required at least one of 1) em, 2) hashed_maids or 3) pair client_ip_address + client_user_agent. + custom_data (dict): Object containing other custom data. event_source_url (str, optional): URL of the web conversion event partner_name (str, optional): The third party partner name responsible to send the event to Conversion API on behalf of the adverstiser. Only send this field if Pinterest has worked @@ -74,6 +77,7 @@ def create_conversion_event( event_time = event_time, event_id = event_id, user_data = ConversionEventsUserData(**user_data), + custom_data = ConversionEventsCustomData(**custom_data), event_source_url = event_source_url, partner_name = partner_name, app_id = app_id, @@ -98,12 +102,11 @@ def send_conversion_events( **kwargs, )-> tuple(int, int, list[ConversionApiResponseEvents]): """ - Send conversion events to Pinterest API for Conversions + Send conversion events to Pinterest API for Conversions. - Note: Highly recommend to use create_client_with_token (with Conversion Access Token) to create new client - for this functionality. + Note: Highly recommend to use create_client_with_token (with Conversion Access Token) to create different + client for this functionality. """ - response = ConversionEventsApi(api_client=cls._get_client(client)).events_create( ad_account_id = str(ad_account_id), conversion_events = ConversionEvents( diff --git a/pinterest/ads/conversion_tags.py b/pinterest/ads/conversion_tags.py index 7d0d81f..48bf358 100644 --- a/pinterest/ads/conversion_tags.py +++ b/pinterest/ads/conversion_tags.py @@ -30,7 +30,7 @@ def __init__( **kwargs ) -> None: """ - Initialize Conversion Tag Object + Initialize Conversion Tag Object. Args: ad_account_id (str): ConversionTag's Ad Account ID @@ -208,14 +208,14 @@ def get_all( Get a list of ConversionTag, filter by specified arguments Args: - ad_account_id (str): _description_ - filter_deleted (bool, optional): _description_. Defaults to False. - client (_type_, optional): _description_. Defaults to PinterestSDKClient=None. + ad_account_id (str): Unique identifier of an ad account. + filter_deleted (bool=False, optional): Filter out deleted tags. + client (_type_, optional): PinterestSDKClient Object. Uses the default client, if not provided. Returns: list[ConversionTag]: List of ConversionTags """ - params = {"ad_account_id" : ad_account_id, "filter_deleted": filter_deleted} + params = {"ad_account_id" : str(ad_account_id), "filter_deleted": filter_deleted} def _map_function(obj): return ConversionTag( @@ -245,7 +245,7 @@ def get_page_visit_conversion_tag_events( **kwargs ) -> tuple[list[ConversionEventResponse], Bookmark]: """ - Get page visit conversion tag events for an ad account + Get page visit conversion tag events for an ad account. Args: ad_account (str): Ad Account ID @@ -284,7 +284,7 @@ def get_ocpm_eligible_conversion_tag_events( **kwargs ) -> tuple[str, list[ConversionEventResponse]]: """ - Get OCPM eligible conversion tag events for an Ad Account + Get OCPM eligible conversion tag events for an Ad Account. Args: ad_account_id (str): Ad Account ID diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index 56e6d62..5e7829c 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -141,16 +141,16 @@ def __repr__(self): @classmethod def create( cls, - board_id:str, - media_source:dict, - link:str = None, - title:str = None, - description:str = None, - dominant_color:str = None, - alt_text:str = None, - board_section_id:str = None, - parent_pin_id:str = None, - client:PinterestSDKClient = None, + board_id : str, + media_source : dict, + link : str = None, + title : str = None, + description : str = None, + dominant_color : str = None, + alt_text : str = None, + board_section_id : str = None, + parent_pin_id : str = None, + client : PinterestSDKClient = None, **kwargs ) -> Pin: """ @@ -213,8 +213,8 @@ def create( board_section_id=board_section_id, media_source=media_source, parent_pin_id=parent_pin_id, - **kwargs, - ) + ), + **kwargs, ) # pylint: disable=no-value-for-parameter verify_api_response(api_response) diff --git a/tests/src/pinterest/ads/test_conversion_events.py b/tests/src/pinterest/ads/test_conversion_events.py index 5034b9e..dbda9c6 100644 --- a/tests/src/pinterest/ads/test_conversion_events.py +++ b/tests/src/pinterest/ads/test_conversion_events.py @@ -1,11 +1,9 @@ from unittest import TestCase from unittest.mock import patch - from openapi_generated.pinterest_client.model.conversion_api_response import ConversionApiResponse from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents - from pinterest.ads.conversion_events import Conversion class TestConversionEvent(TestCase): @@ -46,6 +44,7 @@ def test_send_conversion_event_success(self, create_mock): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data = dict(), ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] diff --git a/tests/src/pinterest/ads/test_conversion_tags.py b/tests/src/pinterest/ads/test_conversion_tags.py index 6a01a20..30c6f1f 100644 --- a/tests/src/pinterest/ads/test_conversion_tags.py +++ b/tests/src/pinterest/ads/test_conversion_tags.py @@ -159,33 +159,57 @@ def test_get_list_conversion_tag(self, list_mock, get_mock): enhanced_match_status = EnhancedMatchStatusType("VALIDATION_COMPLETE"), id = self.test_conversion_tag_id, last_fired_time_ms = float(1599030000000), - name = "ACME Checkout Test Tag", + name = "ACME Checkout Test Tag 1", status = EntityStatus("ACTIVE"), version = "3", configs = test_configs - ) + ), + ConversionTagResponse( + ad_account_id = self.test_ad_account_id, + code_snippet = "