From 60a0844ae59b66e4a70fa64f0a8c810ff23b1074 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Tue, 20 Dec 2022 14:18:02 -0500 Subject: [PATCH 01/35] Analytics base model outline and fields (#34) * Analytics base model outline and fields * Fix lint errors --- .pylintrc | 4 +-- pinterest/utils/analytics.py | 57 ++++++++++++++++++++++++++++++++++ pinterest/utils/validations.py | 7 +++++ 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 pinterest/utils/analytics.py create mode 100644 pinterest/utils/validations.py diff --git a/.pylintrc b/.pylintrc index ba02b0f..f7d2ba9 100644 --- a/.pylintrc +++ b/.pylintrc @@ -48,7 +48,7 @@ ignore=CVS # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\' represents the directory delimiter on Windows systems, it # can't be used as an escape character. -ignore-paths=generated,.venv,venv,docs,samples,package_test,integration_tests +ignore-paths=generated,.venv,venv,docs,samples,package_test,integration_tests,pinterest/utils/validations.py # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores @@ -473,7 +473,7 @@ max-returns=6 max-statements=50 # Minimum number of public methods for a class (see R0903). -min-public-methods=2 +min-public-methods=1 [STRING] diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py new file mode 100644 index 0000000..cb79f28 --- /dev/null +++ b/pinterest/utils/analytics.py @@ -0,0 +1,57 @@ +""" +Analytics Class for Pinterest Python SDK +""" +from __future__ import annotations + +from pinterest.utils.validations import AdsEntityType + +class AnalyticsUtils(): + """ + Utility class with functions to make model specific analytics api calls. + """ + @classmethod + def get_ad_entity_analytics(cls): + # pylint: disable=missing-function-docstring + # added as an example placeholder + pass + + +class AnalyticsResponse(): + """ + AnalyticsResponse model + """ + def __init__( + self, + entity_type:str, + fields:list[str], + raw_response:dict, + ) -> None: + """ + Initialize an Ads Analytics object. + + Args: + entity_type (str): Entity Type identifier. Enum: ad_account, campaign, ad_group, ad. + fields (list[str]): _description_ + raw_response (dict): _description_ + """ + self._entity_type = AdsEntityType(entity_type).name + self._fields = fields + self._raw_response = raw_response + + @property + def entity_type(self) -> str: + # pylint: disable=missing-function-docstring + return self._entity_type + + @property + def fields(self) -> list[str]: + # pylint: disable=missing-function-docstring + return self._fields + + @property + def raw_response(self) -> dict: + # pylint: disable=missing-function-docstring + return self._raw_response + + def __str__(self) -> str: + return f"{self.raw_response}" diff --git a/pinterest/utils/validations.py b/pinterest/utils/validations.py new file mode 100644 index 0000000..a965781 --- /dev/null +++ b/pinterest/utils/validations.py @@ -0,0 +1,7 @@ +from enum import Enum + +def AdsEntityType(Enum): + AD_ACCOUNT = "ad_account" + CAMPAIGN = "campaign" + ADGROUP = "ad_group" + AD = "ad" From 5637518c477463c209bd9ae045a4fd80225818f5 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Sat, 24 Dec 2022 15:54:34 -0500 Subject: [PATCH 02/35] Ad Account `get_analytics` (#40) * AnalyticsUtils _get_ad_entity_analytics helper method * Implement get_analytics() for AdAccount model * Make the helper function get_ad_entity_analytics public --- pinterest/ads/ad_accounts.py | 73 ++++++++++++++++++++++++++++++++++++ pinterest/utils/analytics.py | 39 ++++++++++++++++--- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index cd8b8ad..dd38b94 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -2,6 +2,7 @@ AdAccount Class for Pinterest Python SDK """ from __future__ import annotations +from datetime import date from openapi_generated.pinterest_client.model.country import Country from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner @@ -17,6 +18,7 @@ from pinterest.ads.customer_lists import CustomerList from pinterest.utils.base_model import PinterestBaseModel from pinterest.utils.bookmark import Bookmark +from pinterest.utils.analytics import AnalyticsResponse, AnalyticsUtils class AdAccount(PinterestBaseModel): @@ -247,3 +249,74 @@ def list_customer_lists( client=self._client, **kwargs ) + + def get_analytics( + self, + start_date:date, + end_date:date, + columns:list[str], + granularity:str, + click_window_days:int=30, + engagement_window_days:int=30, + view_window_days:int=1, + conversion_report_time:str="TIME_OF_AD_ACTION", + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified ad_account_id, filtered by the specified options. + + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + Args: + start_date (date): Metric report start date (UTC). + end_date (date): Metric report end date (UTC). + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser'scurrency. For example, if the advertiser's currency is GBP (British pound sterling), all + MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no value, + it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a pin click action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use + their defined attribution windows.. Defaults to 30. + engagement_window_days (int, optional): Enum: 1 7 30 60 Number of days to use as the conversion attribution + window for an engagement action. Engagements include saves, closeups, link clicks, and carousel card + swipes. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their defined attribution + windows. Defaults to 30. + view_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a view action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their + defined attribution windows. Defaults to 1. + conversion_report_time (str, optional): Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION". The date by which + the conversion metrics returned from this endpoint will be reported. There are two dates associated + with a conversion event: the date that the user interacted with the ad, and the date that the user + completed a conversion event. Defaults to "TIME_OF_AD_ACTION". + + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.id + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdAccountsApi, + analytics_fn=AdAccountsApi.ad_account_analytics, + ad_entity=AdAccount, + client=self._client + ) + + return ad_account_analytics_response diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index cb79f28..8415b0f 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -2,18 +2,47 @@ Analytics Class for Pinterest Python SDK """ from __future__ import annotations +from typing import Callable from pinterest.utils.validations import AdsEntityType +from pinterest.utils.base_model import PinterestBaseModel + +from pinterest.client import PinterestSDKClient + class AnalyticsUtils(): """ Utility class with functions to make model specific analytics api calls. """ @classmethod - def get_ad_entity_analytics(cls): - # pylint: disable=missing-function-docstring - # added as an example placeholder - pass + def get_ad_entity_analytics( + cls, + params:list, + api:type, + analytics_fn: Callable, + ad_entity: PinterestBaseModel, + client:PinterestSDKClient = None, + **kwargs + ) -> AnalyticsResponse: + """ + Helper function used to get ad entity analytics. + + Args: + params (list): List of params + api (type): + analytics_fn (Callable): + ad_entity (PinterestBaseModel): + client (PinterestSDKClient, optional): + + Returns: + AnalyticsResponse: + """ + + return AnalyticsResponse( + entity_type=ad_entity, + fields=params.get('columns'), + raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) + ) class AnalyticsResponse(): @@ -22,7 +51,7 @@ class AnalyticsResponse(): """ def __init__( self, - entity_type:str, + entity_type:PinterestBaseModel, fields:list[str], raw_response:dict, ) -> None: From 2cbfdc8f4bd83983a4d127fb0f237f890d0c2616 Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Mon, 23 Jan 2023 23:11:19 +0530 Subject: [PATCH 03/35] implement `get_targeting_analytics` for AdAccount model (#43) --- pinterest/ads/ad_accounts.py | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index dd38b94..b71943e 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -4,9 +4,17 @@ from __future__ import annotations from datetime import date +<<<<<<< HEAD from openapi_generated.pinterest_client.model.country import Country from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner from openapi_generated.pinterest_client.model.currency import Currency +======= +from pinterest.generated.client.model.country import Country +from pinterest.generated.client.model.ad_account_owner import AdAccountOwner +from pinterest.generated.client.model.currency import Currency +from pinterest.generated.client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType +from pinterest.generated.client.model.conversion_report_attribution_type import ConversionReportAttributionType +>>>>>>> 06554c5 (implement `get_targeting_analytics` for AdAccount model (#43)) from openapi_generated.pinterest_client.api.ad_accounts_api import AdAccountsApi from openapi_generated.pinterest_client.model.ad_account import AdAccount as GeneratedAdAccount @@ -320,3 +328,85 @@ def get_analytics( ) return ad_account_analytics_response + + def get_targeting_analytics( + self, + start_date:date, + end_date:date, + targeting_types:list[str], + columns:list[str], + granularity:str, + click_window_days:int=30, + engagement_window_days:int=30, + view_window_days:int=1, + conversion_report_time:str = "TIME_OF_AD_ACTION", + attribution_types:str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified ad_account_id, filtered by the specified options. + + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + Args: + start_date (date): Metric report start date (UTC). + end_date (date): Metric report end date (UTC). + targeting_types (list[str]): Items Enum: "KEYWORD" "APPTYPE" "GENDER" "LOCATION" "PLACEMENT" "COUNTRY" + "TARGETED_INTEREST" "PINNER_INTEREST" "AUDIENCE_INCLUDE" "AUDIENCE_EXCLUDE" "GEO" "AGE_BUCKET" "REGION" + Targeting type breakdowns for the report. The reporting per targeting type + is independent from each other. + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser'scurrency. For example, if the advertiser's currency is GBP (British pound sterling), all + MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no value, + it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a pin click action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use + their defined attribution windows.. Defaults to 30. + engagement_window_days (int, optional): Enum: 1 7 30 60 Number of days to use as the conversion attribution + window for an engagement action. Engagements include saves, closeups, link clicks, and carousel card + swipes. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their defined attribution + windows. Defaults to 30. + view_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a view action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their + defined attribution windows. Defaults to 1. + conversion_report_time (str, optional): Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION". The date by which + the conversion metrics returned from this endpoint will be reported. There are two dates associated + with a conversion event: the date that the user interacted with the ad, and the date that the user + completed a conversion event. Defaults to "TIME_OF_AD_ACTION". + attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" + List of types of attribution for the conversion report + + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.id + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + if attribution_types: + kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) + + ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdAccountsApi, + analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, + ad_entity=AdAccount, + client=self._client + ) + + return ad_account_analytics_response From e19375e260199157eb4e6b36da386894e2279db3 Mon Sep 17 00:00:00 2001 From: Dante Date: Thu, 26 Jan 2023 15:02:20 -0400 Subject: [PATCH 04/35] Update with last main changes (#61) * New setup for documentation (#42) * Remove old doc and Sphinx (#33) * Add documentation generator - `lazydocs` (#36) * Add lazydocs and script to generate spec file * Add skeleton spec and script to generate doc * Add comments and readme * Update README.md * Update format and comments Co-authored-by: Rushabh Varia * add overview file and update script * Update script logic (#39) * Update script to work with overview file * Remove function * Generate documentation using script.py (#41) * Generate doc using script.py * Add update ignored files * Regenerate with organics * Link overview file to README.md * Add ignore module for lazydocs (#44) * Add ignore module for lazydocs * Add emtpy file to ignore list * Run doc gen script * Correct base_url * Regenerate Co-authored-by: Rushabh Varia * remove: beta message from README.md (#50) * Change generated client package name and version (#52) * feat(dependecy): change generated client package name and version * change(lint): fix lint issue (#53) * feat(version): change release (#55) * Update contact info and source_base_url (#57) * Update contact info * Update source_base_url * Regen * Point to doc page on devsite (#58) * Add actions to test and publish artifact (#59) * feat(ci): add test publish action * feat(ci): add github action to publish to pypi * fix: linter (#60) * Analytics base model outline and fields (#34) * Analytics base model outline and fields * Fix lint errors * Ad Account `get_analytics` (#40) * AnalyticsUtils _get_ad_entity_analytics helper method * Implement get_analytics() for AdAccount model * Make the helper function get_ad_entity_analytics public * fix: merge conflict Co-authored-by: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Co-authored-by: Rushabh Varia --- pinterest/ads/ad_accounts.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index b71943e..3a1fce4 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -4,6 +4,7 @@ from __future__ import annotations from datetime import date +<<<<<<< HEAD <<<<<<< HEAD from openapi_generated.pinterest_client.model.country import Country from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner @@ -16,6 +17,13 @@ from pinterest.generated.client.model.conversion_report_attribution_type import ConversionReportAttributionType >>>>>>> 06554c5 (implement `get_targeting_analytics` for AdAccount model (#43)) +======= +from openapi_generated.pinterest_client.model.country import Country +from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner +from openapi_generated.pinterest_client.model.currency import Currency +from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType +>>>>>>> b14fe65 (Update with last main changes (#61)) from openapi_generated.pinterest_client.api.ad_accounts_api import AdAccountsApi from openapi_generated.pinterest_client.model.ad_account import AdAccount as GeneratedAdAccount from openapi_generated.pinterest_client.model.ad_account_create_request import AdAccountCreateRequest From 06ef79e4bb4ccce1d93daea3bcad0638e67542dc Mon Sep 17 00:00:00 2001 From: Dante Date: Tue, 31 Jan 2023 14:51:01 -0400 Subject: [PATCH 05/35] feat(analytics): add get_targeting_analytics to ad group (#66) --- pinterest/ads/ad_groups.py | 104 +++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index a6150e5..a15f776 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -3,6 +3,12 @@ """ from __future__ import annotations +from datetime import date + +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType + +from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType + from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi from openapi_generated.pinterest_client.model.action_type import ActionType @@ -15,6 +21,7 @@ from pinterest.utils.base_model import PinterestBaseModel from pinterest.ads.ads import Ad from pinterest.utils.bookmark import Bookmark +from pinterest.utils.analytics import AnalyticsResponse, AnalyticsUtils class AdGroup(PinterestBaseModel): @@ -477,3 +484,100 @@ def disable_auto_targeting(self): bool: true if ad group disable auto_targeting_enabled """ return self.update_fields(auto_targeting_enabled=False) + + def get_targeting_analytics( + self, + start_date: date, + end_date: date, + targeting_types: list[str], + columns: list[str], + granularity: str, + click_window_days: int = 30, + engagement_window_days: int = 30, + view_window_days: int = 1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + attribution_types: str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get targeting analytics for one or more ad groups. For the requested ad group(s) and metrics, the response will + include the requested metric information (e.g. SPEND_IN_DOLLAR) for the requested target type + (e.g. "age_bucket") for applicable values (e.g. "45-49"). + + - The token's user_account must either be the Owner of the specified ad account, or have one of the + necessary roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD + targeting_types (list[str]): Example: targeting_types=APPTYPE + Targeting type breakdowns for the report. The reporting per targeting type is independent from + each other. + columns (list[str]): Example: columns=SPEND_IN_DOLLAR + Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as MICRO_DOLLARS + returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, or $0.000001 - + one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of the advertiser's + currency. + + For example, if the advertiser's currency is GBP (British pound sterling), all MICRO_DOLLARS fields will + be in GBP microunits (1/1,000,000 British pound). + + If a column has no value, it may not be returned + + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Example: click_window_days=1 + Number of days to use as the conversion attribution window for a pin click action. Applies to Pinterest + Tag conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 30 days. + engagement_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements include + saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion metrics. + Prior conversion tags use their defined attribution windows. If not specified, defaults to 30 days. + view_window_days (int, optional): Default: 1 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a view action. Applies to Pinterest Tag + conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 1 day. + conversion_report_time (str, optional): Default: "TIME_OF_AD_ACTION" + Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION" + Example: conversion_report_time=TIME_OF_AD_ACTION + The date by which the conversion metrics returned from this endpoint will be reported. There are two + dates associated with a conversion event: the date that the user interacted with the ad, and the date + that the user completed a conversion event. + attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" + Example: attribution_types=INDIVIDUAL + List of types of attribution for the conversion report + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.ad_account_id + kwargs['ad_group_ids'] = [self.id] + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + if attribution_types: + kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) + + return AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdGroupsApi, + analytics_fn=AdGroupsApi.ad_groups_targeting_analytics_get, + ad_entity=AdGroup, + client=self._client + ) From d9c0967e1b02247284a1296f9ef929712b4514c4 Mon Sep 17 00:00:00 2001 From: Dante Date: Tue, 31 Jan 2023 14:51:14 -0400 Subject: [PATCH 06/35] Add get targeting analytics for ads (#65) * feat(analtics): add get_targeting_analytics to ads * feat(analytic): fix lint * fix: linter --- pinterest/ads/ads.py | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 1a9ef7a..d911f8e 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -3,6 +3,12 @@ """ from __future__ import annotations +from datetime import date + +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType + +from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType + from openapi_generated.pinterest_client.api.ads_api import AdsApi from openapi_generated.pinterest_client.model.ad_response import AdResponse @@ -14,6 +20,7 @@ from pinterest.client import PinterestSDKClient from pinterest.utils.base_model import PinterestBaseModel from pinterest.utils.bookmark import Bookmark +from pinterest.utils.analytics import AnalyticsResponse, AnalyticsUtils class Ad(PinterestBaseModel): @@ -416,3 +423,94 @@ def update_fields(self, **kwargs) -> bool: update_fn=AdsApi.ads_update, **kwargs ) + + def get_targeting_analytics( + self, + start_date: date, + end_date: date, + targeting_types: list[str], + columns: list[str], + granularity: str, + click_window_days: int = 30, + engagement_window_days: int = 30, + view_window_days: int = 1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + attribution_types: str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get targeting analytics for one or more ads. For the requested ad(s) and metrics, the response will include the + requested metric information (e.g. SPEND_IN_DOLLAR) for the requested target type (e.g. "age_bucket") for + applicable values (e.g. "45-49"). + + The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD + targeting_types (list[str]): Targeting type breakdowns for the report. The reporting per targeting type + is independent from each other. + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser's currency. For example, if the advertiser's currency is GBP (British pound sterling), + all MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no + value, it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Example: click_window_days=1 + Number of days to use as the conversion attribution window for a pin click action. Applies to Pinterest + Tag conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 30 days. + engagement_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements + include saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion + metrics. Prior conversion tags use their defined attribution windows. If not specified, defaults to + 30 days. + view_window_days (int, optional): Default: 1 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a view action. Applies to Pinterest Tag + conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 1 day. + conversion_report_time (str, optional): Default: "TIME_OF_AD_ACTION" + Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION" + Example: conversion_report_time=TIME_OF_AD_ACTION + The date by which the conversion metrics returned from this endpoint will be reported. There are two + dates associated with a conversion event: the date that the user interacted with the ad, and the date + that the user completed a conversion event. + attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" + Example: attribution_types=INDIVIDUAL + List of types of attribution for the conversion report + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.ad_account_id + kwargs['ad_ids'] = [self.id] + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + if attribution_types: + kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) + + return AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdsApi, + analytics_fn=AdsApi.ad_targeting_analytics_get, + ad_entity=Ad, + client=self._client + ) From b2afb5bb18bb5a339a0ee8ef0b85fd0f09608240 Mon Sep 17 00:00:00 2001 From: Dante Date: Tue, 31 Jan 2023 22:56:53 -0400 Subject: [PATCH 07/35] Add Ad group analytics (#64) * feat(analytic): add analytics ad group * Update ad_groups.py --- pinterest/ads/ad_groups.py | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index a15f776..d728ac0 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -581,3 +581,76 @@ def get_targeting_analytics( ad_entity=AdGroup, client=self._client ) + + def get_analytics( + self, + start_date: date, + end_date: date, + columns: list[str], + granularity: str, + click_window_days: int = 30, + engagement_window_days: int = 30, + view_window_days: int = 1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified ad groups in the specified ad_account_id, filtered by the specified options. + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser's currency.For example, if the advertiser's currency is GBP (British pound sterling), + all MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no + value, it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements include + saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion metrics. + Prior conversion tags use their defined attribution windows. If not specified, defaults to 30 days. + engagement_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements include + saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion metrics. + Prior conversion tags use their defined attribution windows. If not specified, defaults to 30 days. + view_window_days (int, optional): Default: 1 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a view action. Applies to Pinterest Tag + conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 1 day. + conversion_report_time (str, optional): Default: "TIME_OF_AD_ACTION" + Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION" + Example: conversion_report_time=TIME_OF_AD_ACTION + The date by which the conversion metrics returned from this endpoint will be reported. There are two + dates associated with a conversion event: the date that the user interacted with the ad, and the date + that the user completed a conversion event. + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_group_ids'] = [self.id] + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + return AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdGroupsApi, + analytics_fn=AdGroupsApi.ad_groups_analytics, + ad_entity=AdGroupsApi, + client=self._client + ) From 2a64f1feb0bcf4ce8575fff900e094d678e116f7 Mon Sep 17 00:00:00 2001 From: Dante Date: Tue, 31 Jan 2023 22:57:25 -0400 Subject: [PATCH 08/35] Add get ads analytics (#62) * feat(ads): add get analytics function --- pinterest/ads/ads.py | 90 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index d911f8e..0049285 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -5,12 +5,11 @@ from datetime import date -from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType - from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType -from openapi_generated.pinterest_client.api.ads_api import AdsApi +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType +from openapi_generated.pinterest_client.api.ads_api import AdsApi from openapi_generated.pinterest_client.model.ad_response import AdResponse from openapi_generated.pinterest_client.model.ad_create_request import AdCreateRequest from openapi_generated.pinterest_client.model.creative_type import CreativeType @@ -424,6 +423,89 @@ def update_fields(self, **kwargs) -> bool: **kwargs ) + def get_analytics( + self, + start_date: date, + end_date: date, + columns: list[str], + granularity: str, + click_window_days: int = 30, + engagement_window_days: int = 30, + view_window_days: int = 1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified ads in the specified ad_account_id, filtered by the specified options. + + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD. + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD. + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser's currency.For example, if the advertiser's currency is GBP (British pound sterling), + all MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound).If a column has no value, + it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): + Default: 30 + Enum: 1 7 30 60 + Example: click_window_days=1 + Number of days to use as the conversion attribution window for a pin click action. Applies to Pinterest + Tag conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 30 days. + engagement_window_days (int, optional): + Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements include + saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion metrics. + Prior conversion tags use their defined attribution windows. If not specified, defaults to 30 days. + view_window_days (int, optional): + Default: 1 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a view action. Applies to Pinterest Tag + conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 1 day. + conversion_report_time (str, optional): + Default: "TIME_OF_AD_ACTION" + Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION" + Example: conversion_report_time=TIME_OF_AD_ACTION + The date by which the conversion metrics returned from this endpoint will be reported. There are two + dates associated with a conversion event: the date that the user interacted with the ad, and the date + that the user completed a conversion event. + + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.ad_account_id + kwargs['ad_ids'] = [self.id] + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + return AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdsApi, + analytics_fn=AdsApi.ads_analytics, + ad_entity=Ad, + client=self._client + ) + def get_targeting_analytics( self, start_date: date, @@ -442,10 +524,8 @@ def get_targeting_analytics( Get targeting analytics for one or more ads. For the requested ad(s) and metrics, the response will include the requested metric information (e.g. SPEND_IN_DOLLAR) for the requested target type (e.g. "age_bucket") for applicable values (e.g. "45-49"). - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary roles granted to them via Business Access: Admin, Analyst, Campaign Manager. - Args: start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD From b7e7eb7054d89b96c987b179bf3b3b7a778a4aca Mon Sep 17 00:00:00 2001 From: Dante Date: Tue, 31 Jan 2023 22:58:33 -0400 Subject: [PATCH 09/35] feat: add analytics and targeting (#72) --- pinterest/ads/campaigns.py | 183 +++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index d18bb6e..5f5c6ec 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -3,6 +3,12 @@ """ from __future__ import annotations +from datetime import date + +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType + +from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType + from openapi_generated.pinterest_client.api.campaigns_api import CampaignsApi from openapi_generated.pinterest_client.model.campaign_response import CampaignResponse @@ -16,6 +22,7 @@ from pinterest.utils.error_handling import verify_api_response from pinterest.utils.base_model import PinterestBaseModel from pinterest.utils.bookmark import Bookmark +from pinterest.utils.analytics import AnalyticsUtils, AnalyticsResponse class Campaign(PinterestBaseModel): @@ -532,3 +539,179 @@ def list_ad_groups( order=order, bookmark=bookmark, **kwargs ) + + def get_analytics( + self, + start_date: date, + end_date: date, + columns: list[str], + granularity: str, + click_window_days: int = 30, + engagement_window_days: int = 30, + view_window_days: int = 1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified campaigns in the specified ad_account_id, filtered by the specified options. + + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + - If granularity is not HOUR, the furthest back you can are allowed to pull data is 914 days before the current + date in UTC time and the max time range supported is 186 days. + + - If granularity is HOUR, the furthest back you can are allowed to pull data is 8 days before the current date + in UTC time and the max time range supported is 3 days. + + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD + columns (list[str]): Example: columns=SPEND_IN_DOLLAR + Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as MICRO_DOLLARS + returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, or $0.000001 + - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of the + advertiser's currency. + + For example, if the advertiser's currency is GBP (British pound sterling), all MICRO_DOLLARS fields + will be in GBP microunits (1/1,000,000 British pound). + + If a column has no value, it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a pin click action. Applies to Pinterest + Tag conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 30 days. + engagement_window_days (int, optional): Default: 30 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for an engagement action. Engagements include + saves, closeups, link clicks, and carousel card swipes. Applies to Pinterest Tag conversion metrics. + Prior conversion tags use their defined attribution windows. If not specified, defaults to 30 days. + view_window_days (int, optional): Default: 1 + Enum: 1 7 30 60 + Number of days to use as the conversion attribution window for a view action. Applies to Pinterest Tag + conversion metrics. Prior conversion tags use their defined attribution windows. If not specified, + defaults to 1 day. + conversion_report_time (str, optional): Default: "TIME_OF_AD_ACTION" + Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION" + Example: conversion_report_time=TIME_OF_AD_ACTION + The date by which the conversion metrics returned from this endpoint will be reported. There are two + dates associated with a conversion event: the date that the user interacted with the ad, and the date + that the user completed a conversion event. + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.ad_account_id + kwargs['campaign_ids'] = [self.id] + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + + return AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=CampaignsApi, + analytics_fn=CampaignsApi.campaigns_analytics, + ad_entity=Campaign, + client=self._client + ) + + def get_targeting_analytics( + self, + start_date: date, + end_date: date, + targeting_types: list[str], + columns: list[str], + granularity: str, + click_window_days: int=30, + engagement_window_days: int=30, + view_window_days: int=1, + conversion_report_time: str = "TIME_OF_AD_ACTION", + attribution_types: str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get targeting analytics for one or more campaigns. For the requested account and metrics, the response will + include the requested metric information (e.g. SPEND_IN_DOLLAR) for the requested target type + (e.g. "age_bucket") for applicable values (e.g. "45-49"). + + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + + - If granularity is not HOUR, the furthest back you can are allowed to pull data is 914 days before the current + date in UTC time and the max time range supported is 186 days. + + - If granularity is HOUR, the furthest back you can are allowed to pull data is 8 days before the current date + in UTC time and the max time range supported is 3 days. + + Args: + start_date (date): Metric report start date (UTC). + end_date (date): Metric report end date (UTC). + targeting_types (list[str]): Items Enum: "KEYWORD" "APPTYPE" "GENDER" "LOCATION" "PLACEMENT" "COUNTRY" + "TARGETED_INTEREST" "PINNER_INTEREST" "AUDIENCE_INCLUDE" "AUDIENCE_EXCLUDE" "GEO" "AGE_BUCKET" "REGION" + Targeting type breakdowns for the report. The reporting per targeting type + is independent from each other. + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser'scurrency. For example, if the advertiser's currency is GBP (British pound sterling), all + MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no value, + it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a pin click action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use + their defined attribution windows.. Defaults to 30. + engagement_window_days (int, optional): Enum: 1 7 30 60 Number of days to use as the conversion attribution + window for an engagement action. Engagements include saves, closeups, link clicks, and carousel card + swipes. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their defined attribution + windows. Defaults to 30. + view_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a view action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their + defined attribution windows. Defaults to 1. + conversion_report_time (str, optional): Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION". The date by which + the conversion metrics returned from this endpoint will be reported. There are two dates associated + with a conversion event: the date that the user interacted with the ad, and the date that the user + completed a conversion event. Defaults to "TIME_OF_AD_ACTION". + attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" + List of types of attribution for the conversion report + + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.ad_account_id + kwargs['campaign_ids'] = [self.id] + kwargs['end_date'] = end_date + kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + if attribution_types: + kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) + + ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=CampaignsApi, + analytics_fn=CampaignsApi.campaign_targeting_analytics_get, + ad_entity=Campaign, + client=self._client + ) + + return ad_account_analytics_response From 93aef8e38ac80c6f48b41980439b19ab2cf989d7 Mon Sep 17 00:00:00 2001 From: Dante Date: Thu, 2 Feb 2023 20:13:38 -0400 Subject: [PATCH 10/35] Add get pin analytic (#73) * feat: add get pin analytic * fix campaigns param * Add missing line * fix ad_entity ad group * refactor: change param and method name --- pinterest/ads/ad_accounts.py | 8 ++--- pinterest/ads/ad_groups.py | 8 ++--- pinterest/ads/ads.py | 8 ++--- pinterest/ads/campaigns.py | 9 +++--- pinterest/organic/pins.py | 62 ++++++++++++++++++++++++++++++++++++ pinterest/utils/analytics.py | 18 +++++------ 6 files changed, 88 insertions(+), 25 deletions(-) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index 3a1fce4..740d198 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -327,11 +327,11 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time - ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdAccountsApi, analytics_fn=AdAccountsApi.ad_account_analytics, - ad_entity=AdAccount, + entity=AdAccount, client=self._client ) @@ -409,11 +409,11 @@ def get_targeting_analytics( if attribution_types: kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) - ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdAccountsApi, analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, - ad_entity=AdAccount, + entity=AdAccount, client=self._client ) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index d728ac0..5425d38 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -574,11 +574,11 @@ def get_targeting_analytics( if attribution_types: kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) - return AnalyticsUtils.get_ad_entity_analytics( + return AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdGroupsApi, analytics_fn=AdGroupsApi.ad_groups_targeting_analytics_get, - ad_entity=AdGroup, + entity=AdGroup, client=self._client ) @@ -647,10 +647,10 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time - return AnalyticsUtils.get_ad_entity_analytics( + return AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdGroupsApi, analytics_fn=AdGroupsApi.ad_groups_analytics, - ad_entity=AdGroupsApi, + entity=AdGroup, client=self._client ) diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 0049285..54e18c5 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -498,11 +498,11 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time - return AnalyticsUtils.get_ad_entity_analytics( + return AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdsApi, analytics_fn=AdsApi.ads_analytics, - ad_entity=Ad, + entity=Ad, client=self._client ) @@ -587,10 +587,10 @@ def get_targeting_analytics( if attribution_types: kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) - return AnalyticsUtils.get_ad_entity_analytics( + return AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdsApi, analytics_fn=AdsApi.ad_targeting_analytics_get, - ad_entity=Ad, + entity=Ad, client=self._client ) diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index 5f5c6ec..f7c7898 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -618,11 +618,11 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time - return AnalyticsUtils.get_ad_entity_analytics( + return AnalyticsUtils.get_entity_analytics( params=kwargs, api=CampaignsApi, analytics_fn=CampaignsApi.campaigns_analytics, - ad_entity=Campaign, + entity=Campaign, client=self._client ) @@ -695,6 +695,7 @@ def get_targeting_analytics( """ kwargs['ad_account_id'] = self.ad_account_id kwargs['campaign_ids'] = [self.id] + kwargs['start_date'] = start_date kwargs['end_date'] = end_date kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] kwargs['columns'] = columns @@ -706,11 +707,11 @@ def get_targeting_analytics( if attribution_types: kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) - ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, api=CampaignsApi, analytics_fn=CampaignsApi.campaign_targeting_analytics_get, - ad_entity=Campaign, + entity=Campaign, client=self._client ) diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index e1693c4..abb297b 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -3,6 +3,8 @@ """ from __future__ import annotations +from datetime import date + 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.inline_object import InlineObject @@ -10,6 +12,8 @@ from pinterest.client import PinterestSDKClient from pinterest.utils.base_model import PinterestBaseModel from pinterest.utils.error_handling import verify_api_response +from pinterest.utils.analytics import AnalyticsResponse, AnalyticsUtils + class Pin(PinterestBaseModel): """ @@ -275,3 +279,61 @@ def save( verify_api_response(api_response) self._populate_fields(_model_data=api_response.to_dict()) + + def get_analytics( + self, + start_date: date, + end_date: date, + app_types: str = "ALL", + metric_types: str | list[str] = "ALL", + split_field: str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for a Pin owned by the "operation user_account" - or on a group board that has been shared with + this account. + + - By default, the "operation user_account" is the token user_account. + + Optional: Business Access: Specify an ad_account_id (obtained via List ad accounts) to use the owner of that + ad_account as the "operation user_account". In order to do this, the token user_account must have one of the + following Business Access roles on the ad_account: + + - For Pins on public or protected boards: Admin, Analyst. + - For Pins on secret boards: Admin. + + Args: + start_date (date): Metric report start date (UTC). Format: YYYY-MM-DD + end_date (date): Metric report end date (UTC). Format: YYYY-MM-DD + app_types (str): Default: "ALL" + Enum: "ALL" "MOBILE" "TABLET" "WEB" + Apps or devices to get data for, default is all. + metric_types (list[str], str): + - Standard Pin metric types: "OUTBOUND_CLICK" "PIN_CLICK" "IMPRESSION" "SAVE" "SAVE_RATE" + - Video Pin metric types :"OUTBOUND_CLICK" "IMPRESSION" "SAVE" "QUARTILE_95_PERCENT_VIEW" + "VIDEO_10S_VIEW" "VIDEO_AVG_WATCH_TIME" "VIDEO_MRC_VIEW" "VIDEO_START" "VIDEO_V50_WATCH_TIME" + Pin metric types to get data for, default is all. + split_field (str): Default: "NO_SPLIT" + Enum: "NO_SPLIT" "APP_TYPE" + How to split the data into groups. Not including this param means data won't be split. + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + if self._ad_account_id: + kwargs['ad_account_id'] = self._ad_account_id + if app_types: + kwargs['app_types'] = app_types + if split_field: + kwargs['split_field'] = split_field + + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['metric_types'] = metric_types + + return AnalyticsUtils.get_entity_analytics( + params=kwargs, + api=PinsApi, + analytics_fn=PinsApi.pins_analytics, + entity=Pin, + client=self._client + ) diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index 8415b0f..81b6293 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -10,18 +10,18 @@ from pinterest.client import PinterestSDKClient -class AnalyticsUtils(): +class AnalyticsUtils: """ Utility class with functions to make model specific analytics api calls. """ @classmethod - def get_ad_entity_analytics( + def get_entity_analytics( cls, - params:list, - api:type, + params: list, + api: type, analytics_fn: Callable, - ad_entity: PinterestBaseModel, - client:PinterestSDKClient = None, + entity: PinterestBaseModel, + client: PinterestSDKClient = None, **kwargs ) -> AnalyticsResponse: """ @@ -31,7 +31,7 @@ def get_ad_entity_analytics( params (list): List of params api (type): analytics_fn (Callable): - ad_entity (PinterestBaseModel): + entity (PinterestBaseModel): client (PinterestSDKClient, optional): Returns: @@ -39,8 +39,8 @@ def get_ad_entity_analytics( """ return AnalyticsResponse( - entity_type=ad_entity, - fields=params.get('columns'), + entity_type=entity, + fields=params.get('columns', []), raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) ) From e6f78af56c254a2478b9f2193bffe140c366471a Mon Sep 17 00:00:00 2001 From: dfana Date: Wed, 8 Feb 2023 00:07:50 -0400 Subject: [PATCH 11/35] fix conflicts --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5d2b6d9..f702ac1 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,6 @@ campaign.set_lifetime_budget( * Documentation is hosted on [Developer Site](https://developers.pinterest.com/docs/sdk/). - ## Exceptions See `pinterest.utils.sdk_exceptions` for a list of exceptions which may be thrown by the SDK. From 060890f03c76aeb88ea012e7fe7b81d52ee38877 Mon Sep 17 00:00:00 2001 From: dfana Date: Wed, 8 Feb 2023 00:09:37 -0400 Subject: [PATCH 12/35] fix conflicts --- pinterest/ads/ad_accounts.py | 7 +++++++ pinterest/ads/ad_groups.py | 5 +++++ pinterest/ads/ads.py | 5 +++++ pinterest/ads/campaigns.py | 9 +++++++++ pinterest/organic/pins.py | 3 +++ 5 files changed, 29 insertions(+) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index 740d198..5236093 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -21,9 +21,16 @@ from openapi_generated.pinterest_client.model.country import Country from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner from openapi_generated.pinterest_client.model.currency import Currency +<<<<<<< HEAD from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType +<<<<<<< HEAD >>>>>>> b14fe65 (Update with last main changes (#61)) +======= +======= + +>>>>>>> d1f7acc (Change generated client package name and version (#52)) +>>>>>>> 438d97e (fix conflicts) from openapi_generated.pinterest_client.api.ad_accounts_api import AdAccountsApi from openapi_generated.pinterest_client.model.ad_account import AdAccount as GeneratedAdAccount from openapi_generated.pinterest_client.model.ad_account_create_request import AdAccountCreateRequest diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index 5425d38..42de7e5 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType @@ -11,6 +12,10 @@ from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi +======= +from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi + +>>>>>>> d1f7acc (Change generated client package name and version (#52)) from openapi_generated.pinterest_client.model.action_type import ActionType from openapi_generated.pinterest_client.model.budget_type import BudgetType from openapi_generated.pinterest_client.model.ad_group_response import AdGroupResponse diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 54e18c5..5b564bc 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType @@ -10,6 +11,10 @@ from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType from openapi_generated.pinterest_client.api.ads_api import AdsApi +======= +from openapi_generated.pinterest_client.api.ads_api import AdsApi + +>>>>>>> d1f7acc (Change generated client package name and version (#52)) from openapi_generated.pinterest_client.model.ad_response import AdResponse from openapi_generated.pinterest_client.model.ad_create_request import AdCreateRequest from openapi_generated.pinterest_client.model.creative_type import CreativeType diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index f7c7898..f1d8fe8 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -3,6 +3,7 @@ """ from __future__ import annotations +<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType @@ -15,6 +16,14 @@ from openapi_generated.pinterest_client.model.campaign_create_request import CampaignCreateRequest from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest +======= +from openapi_generated.pinterest_client.api.campaigns_api import CampaignsApi + +from openapi_generated.pinterest_client.model.campaign_response import CampaignResponse +from openapi_generated.pinterest_client.model.campaign_create_request import CampaignCreateRequest +from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest + +>>>>>>> d1f7acc (Change generated client package name and version (#52)) from openapi_generated.pinterest_client.model.objective_type import ObjectiveType from pinterest.ads.ad_groups import AdGroup diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index abb297b..965a7c0 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -3,8 +3,11 @@ """ from __future__ import annotations +<<<<<<< HEAD from datetime import date +======= +>>>>>>> d1f7acc (Change generated client package name and version (#52)) 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.inline_object import InlineObject From 12a9f63e3706a5ab1a6bc6d5a44c192fbec17035 Mon Sep 17 00:00:00 2001 From: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Date: Tue, 24 Jan 2023 12:49:44 -0800 Subject: [PATCH 13/35] Point to doc page on devsite (#58) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f702ac1..950cc4a 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,10 @@ campaign.set_lifetime_budget( ## Documentation * Documentation is hosted on [Developer Site](https://developers.pinterest.com/docs/sdk/). +<<<<<<< HEAD +======= + +>>>>>>> 5a733b3 (Point to doc page on devsite (#58)) ## Exceptions From 48b919010d26b1fee5c8c61108e69f17af2abb1d Mon Sep 17 00:00:00 2001 From: Rushabh Varia Date: Sat, 24 Dec 2022 15:54:34 -0500 Subject: [PATCH 14/35] Ad Account `get_analytics` (#40) * AnalyticsUtils _get_ad_entity_analytics helper method * Implement get_analytics() for AdAccount model * Make the helper function get_ad_entity_analytics public --- pinterest/ads/ad_accounts.py | 8 ++++++++ pinterest/utils/analytics.py | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index 5236093..c4b5512 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -334,6 +334,7 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time +<<<<<<< HEAD ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdAccountsApi, @@ -421,6 +422,13 @@ def get_targeting_analytics( api=AdAccountsApi, analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, entity=AdAccount, +======= + ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( + params=kwargs, + api=AdAccountsApi, + analytics_fn=AdAccountsApi.ad_account_analytics, + ad_entity=AdAccount, +>>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) client=self._client ) diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index 81b6293..159c9f0 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -6,6 +6,12 @@ from pinterest.utils.validations import AdsEntityType from pinterest.utils.base_model import PinterestBaseModel +<<<<<<< HEAD +======= + +from pinterest.client import PinterestSDKClient + +>>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) from pinterest.client import PinterestSDKClient @@ -15,6 +21,7 @@ class AnalyticsUtils: Utility class with functions to make model specific analytics api calls. """ @classmethod +<<<<<<< HEAD def get_entity_analytics( cls, params: list, @@ -22,6 +29,15 @@ def get_entity_analytics( analytics_fn: Callable, entity: PinterestBaseModel, client: PinterestSDKClient = None, +======= + def get_ad_entity_analytics( + cls, + params:list, + api:type, + analytics_fn: Callable, + ad_entity: PinterestBaseModel, + client:PinterestSDKClient = None, +>>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) **kwargs ) -> AnalyticsResponse: """ @@ -31,7 +47,11 @@ def get_entity_analytics( params (list): List of params api (type): analytics_fn (Callable): +<<<<<<< HEAD entity (PinterestBaseModel): +======= + ad_entity (PinterestBaseModel): +>>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) client (PinterestSDKClient, optional): Returns: @@ -39,8 +59,13 @@ def get_entity_analytics( """ return AnalyticsResponse( +<<<<<<< HEAD entity_type=entity, fields=params.get('columns', []), +======= + entity_type=ad_entity, + fields=params.get('columns'), +>>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) ) From ca35a5dc556a6e7d2db553474455212d227fcc0b Mon Sep 17 00:00:00 2001 From: dfana Date: Wed, 8 Feb 2023 00:01:26 -0400 Subject: [PATCH 15/35] fix conflicts --- pinterest/ads/ad_accounts.py | 109 ++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 14 deletions(-) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index c4b5512..451b7ce 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -22,6 +22,7 @@ from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner from openapi_generated.pinterest_client.model.currency import Currency <<<<<<< HEAD +<<<<<<< HEAD from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType <<<<<<< HEAD @@ -30,7 +31,14 @@ ======= >>>>>>> d1f7acc (Change generated client package name and version (#52)) +<<<<<<< HEAD >>>>>>> 438d97e (fix conflicts) +======= +======= +from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType +from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType +>>>>>>> e470fd7 (fix conflicts) +>>>>>>> b845cf6 (fix conflicts) from openapi_generated.pinterest_client.api.ad_accounts_api import AdAccountsApi from openapi_generated.pinterest_client.model.ad_account import AdAccount as GeneratedAdAccount from openapi_generated.pinterest_client.model.ad_account_create_request import AdAccountCreateRequest @@ -57,7 +65,6 @@ def __init__( ) -> None: """ Initialize an object of an AdAccount. - Args: ad_account_id (str): Unique identifier of an ad account. client (PinterestSDKClient, optional): PinterestSDKClient Object. Defaults to `default_api_client`. @@ -129,16 +136,13 @@ def create( For more, see \ Create an advertiser account. - Args: name (str): Ad Account name owner_user_id (str): Ad Account's owning user ID country (str): Country ID from ISO 3166-1 alpha-2. Example: "US" or "RU". client (PinterestSDKClient): PinterestSDKClient Object - Keyword Args: Any valid keyword arguments or query parameters for endpoint. - Returns: AdAccount: AdAccount Object """ @@ -172,7 +176,6 @@ def list_campaigns( roles granted to them via\ \ Business Access: Admin, Analyst, Campaign Manager. - Args: campaign_ids (list[str], optional): List of Campaign Ids to use to filter the results. Defaults to None. entity_statuses (list[str], optional): Possible Entity Statuses "ACTIVE", "PAUSED" or "ARCHIVED". Defaults @@ -184,10 +187,8 @@ def list_campaigns( Note that higher-value IDs are associated with more-recently added items. Defaults to "ASCENDING". bookmark (str, optional): Cursor used to fetch the next page of items. Defaults to None. - Keyword Args: Any valid keyword arguments or query parameters for endpoint. - Returns: list[Campaign]: List of Campaign Objects Bookmark: Bookmark for pagination if present, else None. @@ -214,7 +215,6 @@ def list_audiences( # pylint: disable=too-many-arguments """ Get a list of the audiences in the AdAccount, filtered by the specified arguments - Args: entity_statuses (list[str], optional): Possible Entity Statuses "ACTIVE", "PAUSED" or "ARCHIVED". Defaults to None. @@ -225,7 +225,6 @@ def list_audiences( Note that higher-value IDs are associated with more-recently added items. Defaults to "ASCENDING". bookmark (str, optional): Cursor used to fetch the next page of items. Defaults to None. - Returns: list[Audience]: List of Audience Objects Bookmark: Bookmark for pagination if present, else None. @@ -250,7 +249,6 @@ def list_customer_lists( # pylint: disable=too-many-arguments """ Get a list of customer lists in the AdAccount, filtered by the specified arguments - Args: page_size (int[1..100], optional): Maximum number of items to include in a single page of the response. See documentation on Pagination for more information. Defaults to None @@ -259,7 +257,6 @@ def list_customer_lists( Note that higher-value IDs are associated with more-recently added items. Defaults to "ASCENDING". bookmark (str, optional): Cursor used to fetch the next page of items. Defaults to None. - Returns: list[CustomerList]: List of Audience Objects Bookmark: Bookmark for pagination if present, else None. @@ -287,10 +284,8 @@ def get_analytics( ) -> AnalyticsResponse: """ Get analytics for the specified ad_account_id, filtered by the specified options. - - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary roles granted to them via Business Access: Admin, Analyst, Campaign Manager. - Args: start_date (date): Metric report start date (UTC). end_date (date): Metric report end date (UTC). @@ -320,7 +315,6 @@ def get_analytics( the conversion metrics returned from this endpoint will be reported. There are two dates associated with a conversion event: the date that the user interacted with the ad, and the date that the user completed a conversion event. Defaults to "TIME_OF_AD_ACTION". - Returns: AnalyticsResponse: AnalyticsResponse object. """ @@ -334,6 +328,7 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time +<<<<<<< HEAD <<<<<<< HEAD ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, @@ -429,6 +424,92 @@ def get_targeting_analytics( analytics_fn=AdAccountsApi.ad_account_analytics, ad_entity=AdAccount, >>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) +======= + ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( + params=kwargs, + api=AdAccountsApi, + analytics_fn=AdAccountsApi.ad_account_analytics, + entity=AdAccount, + client=self._client + ) + + return ad_account_analytics_response + + def get_targeting_analytics( + self, + start_date:date, + end_date:date, + targeting_types:list[str], + columns:list[str], + granularity:str, + click_window_days:int=30, + engagement_window_days:int=30, + view_window_days:int=1, + conversion_report_time:str = "TIME_OF_AD_ACTION", + attribution_types:str = None, + **kwargs + ) -> AnalyticsResponse: + """ + Get analytics for the specified ad_account_id, filtered by the specified options. + - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary + roles granted to them via Business Access: Admin, Analyst, Campaign Manager. + Args: + start_date (date): Metric report start date (UTC). + end_date (date): Metric report end date (UTC). + targeting_types (list[str]): Items Enum: "KEYWORD" "APPTYPE" "GENDER" "LOCATION" "PLACEMENT" "COUNTRY" + "TARGETED_INTEREST" "PINNER_INTEREST" "AUDIENCE_INCLUDE" "AUDIENCE_EXCLUDE" "GEO" "AGE_BUCKET" "REGION" + Targeting type breakdowns for the report. The reporting per targeting type + is independent from each other. + columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as + MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, + or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of + the advertiser'scurrency. For example, if the advertiser's currency is GBP (British pound sterling), all + MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no value, + it may not be returned + granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" + TOTAL - metrics are aggregated over the specified date range. + DAY - metrics are broken down daily. + HOUR - metrics are broken down hourly. + WEEKLY - metrics are broken down weekly. + MONTHLY - metrics are broken down monthly + click_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a pin click action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use + their defined attribution windows.. Defaults to 30. + engagement_window_days (int, optional): Enum: 1 7 30 60 Number of days to use as the conversion attribution + window for an engagement action. Engagements include saves, closeups, link clicks, and carousel card + swipes. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their defined attribution + windows. Defaults to 30. + view_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution + window for a view action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their + defined attribution windows. Defaults to 1. + conversion_report_time (str, optional): Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION". The date by which + the conversion metrics returned from this endpoint will be reported. There are two dates associated + with a conversion event: the date that the user interacted with the ad, and the date that the user + completed a conversion event. Defaults to "TIME_OF_AD_ACTION". + attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" + List of types of attribution for the conversion report + Returns: + AnalyticsResponse: AnalyticsResponse object. + """ + kwargs['ad_account_id'] = self.id + kwargs['start_date'] = start_date + kwargs['end_date'] = end_date + kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] + kwargs['columns'] = columns + kwargs['granularity'] = granularity + kwargs['click_window_days'] = click_window_days + kwargs['engagement_window_days'] = engagement_window_days + kwargs['view_window_days'] = view_window_days + kwargs['conversion_report_time'] = conversion_report_time + if attribution_types: + kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) + + ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( + params=kwargs, + api=AdAccountsApi, + analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, + entity=AdAccount, +>>>>>>> e470fd7 (fix conflicts) client=self._client ) From 79c801cfc29a0c3d895327126ef755c1af8801cf Mon Sep 17 00:00:00 2001 From: Dante Date: Thu, 2 Feb 2023 20:13:38 -0400 Subject: [PATCH 16/35] Add get pin analytic (#73) * feat: add get pin analytic * fix campaigns param * Add missing line * fix ad_entity ad group * refactor: change param and method name --- pinterest/organic/pins.py | 5 +++++ pinterest/utils/analytics.py | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index 965a7c0..3b46e6a 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -3,11 +3,16 @@ """ from __future__ import annotations +<<<<<<< HEAD <<<<<<< HEAD from datetime import date ======= >>>>>>> d1f7acc (Change generated client package name and version (#52)) +======= +from datetime import date + +>>>>>>> aa63ab6 (Add get pin analytic (#73)) 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.inline_object import InlineObject diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index 159c9f0..13cf2b5 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -21,6 +21,7 @@ class AnalyticsUtils: Utility class with functions to make model specific analytics api calls. """ @classmethod +<<<<<<< HEAD <<<<<<< HEAD def get_entity_analytics( cls, @@ -31,13 +32,21 @@ def get_entity_analytics( client: PinterestSDKClient = None, ======= def get_ad_entity_analytics( +======= + def get_entity_analytics( +>>>>>>> aa63ab6 (Add get pin analytic (#73)) cls, - params:list, - api:type, + params: list, + api: type, analytics_fn: Callable, +<<<<<<< HEAD ad_entity: PinterestBaseModel, client:PinterestSDKClient = None, >>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) +======= + entity: PinterestBaseModel, + client: PinterestSDKClient = None, +>>>>>>> aa63ab6 (Add get pin analytic (#73)) **kwargs ) -> AnalyticsResponse: """ @@ -47,11 +56,15 @@ def get_ad_entity_analytics( params (list): List of params api (type): analytics_fn (Callable): +<<<<<<< HEAD <<<<<<< HEAD entity (PinterestBaseModel): ======= ad_entity (PinterestBaseModel): >>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) +======= + entity (PinterestBaseModel): +>>>>>>> aa63ab6 (Add get pin analytic (#73)) client (PinterestSDKClient, optional): Returns: @@ -59,6 +72,7 @@ def get_ad_entity_analytics( """ return AnalyticsResponse( +<<<<<<< HEAD <<<<<<< HEAD entity_type=entity, fields=params.get('columns', []), @@ -66,6 +80,10 @@ def get_ad_entity_analytics( entity_type=ad_entity, fields=params.get('columns'), >>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) +======= + entity_type=entity, + fields=params.get('columns', []), +>>>>>>> aa63ab6 (Add get pin analytic (#73)) raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) ) From ec3b96a8f16311fb3aee6c0d9435b42863e70418 Mon Sep 17 00:00:00 2001 From: dfana Date: Tue, 7 Feb 2023 23:49:18 -0400 Subject: [PATCH 17/35] fix conflics --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 950cc4a..22bb38f 100644 --- a/README.md +++ b/README.md @@ -183,8 +183,11 @@ campaign.set_lifetime_budget( <<<<<<< HEAD ======= +<<<<<<< HEAD >>>>>>> 5a733b3 (Point to doc page on devsite (#58)) +======= +>>>>>>> 980226d (fix conflics) ## Exceptions See `pinterest.utils.sdk_exceptions` for a list of exceptions which may be thrown by the SDK. From b9eb788a43306430aee0e5fa2525d86338353813 Mon Sep 17 00:00:00 2001 From: dfana Date: Tue, 7 Feb 2023 23:52:07 -0400 Subject: [PATCH 18/35] fix conflicts --- pinterest/ads/ad_groups.py | 5 +++-- pinterest/ads/ads.py | 2 -- pinterest/ads/campaigns.py | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index 42de7e5..0d7c2ab 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -7,15 +7,16 @@ from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType - from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType - from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi +<<<<<<< HEAD ======= from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi >>>>>>> d1f7acc (Change generated client package name and version (#52)) +======= +>>>>>>> 58c044f (fix conflicts) from openapi_generated.pinterest_client.model.action_type import ActionType from openapi_generated.pinterest_client.model.budget_type import BudgetType from openapi_generated.pinterest_client.model.ad_group_response import AdGroupResponse diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 5b564bc..a7ddd24 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -7,9 +7,7 @@ from datetime import date from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType - from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType - from openapi_generated.pinterest_client.api.ads_api import AdsApi ======= from openapi_generated.pinterest_client.api.ads_api import AdsApi diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index f1d8fe8..e445192 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -7,14 +7,12 @@ from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType - from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType - from openapi_generated.pinterest_client.api.campaigns_api import CampaignsApi - from openapi_generated.pinterest_client.model.campaign_response import CampaignResponse from openapi_generated.pinterest_client.model.campaign_create_request import CampaignCreateRequest from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest +<<<<<<< HEAD ======= from openapi_generated.pinterest_client.api.campaigns_api import CampaignsApi @@ -24,6 +22,8 @@ from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest >>>>>>> d1f7acc (Change generated client package name and version (#52)) +======= +>>>>>>> 58c044f (fix conflicts) from openapi_generated.pinterest_client.model.objective_type import ObjectiveType from pinterest.ads.ad_groups import AdGroup From e987f8e19f19591840c5a7babfd6dd4da12e4bc8 Mon Sep 17 00:00:00 2001 From: Thuc Nguyen <59547942+thucngyyen@users.noreply.github.com> Date: Tue, 31 Jan 2023 07:04:23 -0800 Subject: [PATCH 19/35] fix conflicts --- pinterest/ads/ad_accounts.py | 2 +- pinterest/utils/analytics.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index 451b7ce..afb30cd 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -513,4 +513,4 @@ def get_targeting_analytics( client=self._client ) - return ad_account_analytics_response + return ad_account_analytics_response \ No newline at end of file diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index 13cf2b5..e24281f 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -51,7 +51,6 @@ def get_entity_analytics( ) -> AnalyticsResponse: """ Helper function used to get ad entity analytics. - Args: params (list): List of params api (type): @@ -66,7 +65,6 @@ def get_entity_analytics( entity (PinterestBaseModel): >>>>>>> aa63ab6 (Add get pin analytic (#73)) client (PinterestSDKClient, optional): - Returns: AnalyticsResponse: """ @@ -100,7 +98,6 @@ def __init__( ) -> None: """ Initialize an Ads Analytics object. - Args: entity_type (str): Entity Type identifier. Enum: ad_account, campaign, ad_group, ad. fields (list[str]): _description_ @@ -126,4 +123,4 @@ def raw_response(self) -> dict: return self._raw_response def __str__(self) -> str: - return f"{self.raw_response}" + return f"{self.raw_response}" \ No newline at end of file From 7ff48448fc33787b8b23fdf51561e1dd3df1af9a Mon Sep 17 00:00:00 2001 From: dfana Date: Wed, 8 Feb 2023 00:16:12 -0400 Subject: [PATCH 20/35] fix conflicts --- README.md | 7 -- pinterest/ads/ad_accounts.py | 128 ----------------------------------- pinterest/ads/ad_groups.py | 9 --- pinterest/ads/ads.py | 5 -- pinterest/ads/campaigns.py | 13 ---- pinterest/organic/pins.py | 8 --- pinterest/utils/analytics.py | 43 ------------ 7 files changed, 213 deletions(-) diff --git a/README.md b/README.md index 22bb38f..f702ac1 100644 --- a/README.md +++ b/README.md @@ -180,14 +180,7 @@ campaign.set_lifetime_budget( ## Documentation * Documentation is hosted on [Developer Site](https://developers.pinterest.com/docs/sdk/). -<<<<<<< HEAD -======= -<<<<<<< HEAD ->>>>>>> 5a733b3 (Point to doc page on devsite (#58)) - -======= ->>>>>>> 980226d (fix conflics) ## Exceptions See `pinterest.utils.sdk_exceptions` for a list of exceptions which may be thrown by the SDK. diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index afb30cd..b407c9a 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -4,41 +4,11 @@ from __future__ import annotations from datetime import date -<<<<<<< HEAD -<<<<<<< HEAD from openapi_generated.pinterest_client.model.country import Country from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner from openapi_generated.pinterest_client.model.currency import Currency -======= -from pinterest.generated.client.model.country import Country -from pinterest.generated.client.model.ad_account_owner import AdAccountOwner -from pinterest.generated.client.model.currency import Currency -from pinterest.generated.client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType -from pinterest.generated.client.model.conversion_report_attribution_type import ConversionReportAttributionType ->>>>>>> 06554c5 (implement `get_targeting_analytics` for AdAccount model (#43)) - -======= -from openapi_generated.pinterest_client.model.country import Country -from openapi_generated.pinterest_client.model.ad_account_owner import AdAccountOwner -from openapi_generated.pinterest_client.model.currency import Currency -<<<<<<< HEAD -<<<<<<< HEAD -from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType -from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType -<<<<<<< HEAD ->>>>>>> b14fe65 (Update with last main changes (#61)) -======= -======= - ->>>>>>> d1f7acc (Change generated client package name and version (#52)) -<<<<<<< HEAD ->>>>>>> 438d97e (fix conflicts) -======= -======= from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType ->>>>>>> e470fd7 (fix conflicts) ->>>>>>> b845cf6 (fix conflicts) from openapi_generated.pinterest_client.api.ad_accounts_api import AdAccountsApi from openapi_generated.pinterest_client.model.ad_account import AdAccount as GeneratedAdAccount from openapi_generated.pinterest_client.model.ad_account_create_request import AdAccountCreateRequest @@ -328,103 +298,6 @@ def get_analytics( kwargs['view_window_days'] = view_window_days kwargs['conversion_report_time'] = conversion_report_time -<<<<<<< HEAD -<<<<<<< HEAD - ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( - params=kwargs, - api=AdAccountsApi, - analytics_fn=AdAccountsApi.ad_account_analytics, - entity=AdAccount, - client=self._client - ) - - return ad_account_analytics_response - - def get_targeting_analytics( - self, - start_date:date, - end_date:date, - targeting_types:list[str], - columns:list[str], - granularity:str, - click_window_days:int=30, - engagement_window_days:int=30, - view_window_days:int=1, - conversion_report_time:str = "TIME_OF_AD_ACTION", - attribution_types:str = None, - **kwargs - ) -> AnalyticsResponse: - """ - Get analytics for the specified ad_account_id, filtered by the specified options. - - - The token's user_account must either be the Owner of the specified ad account, or have one of the necessary - roles granted to them via Business Access: Admin, Analyst, Campaign Manager. - - Args: - start_date (date): Metric report start date (UTC). - end_date (date): Metric report end date (UTC). - targeting_types (list[str]): Items Enum: "KEYWORD" "APPTYPE" "GENDER" "LOCATION" "PLACEMENT" "COUNTRY" - "TARGETED_INTEREST" "PINNER_INTEREST" "AUDIENCE_INCLUDE" "AUDIENCE_EXCLUDE" "GEO" "AGE_BUCKET" "REGION" - Targeting type breakdowns for the report. The reporting per targeting type - is independent from each other. - columns (list[str]): Columns to retrieve, encoded as a comma-separated string. NOTE: Any metrics defined as - MICRO_DOLLARS returns a value based on the advertiser profile's currency field. For USD,($1/1,000,000, - or $0.000001 - one one-ten-thousandth of a cent). it's microdollars. Otherwise, it's in microunits of - the advertiser'scurrency. For example, if the advertiser's currency is GBP (British pound sterling), all - MICRO_DOLLARS fields will be in GBP microunits (1/1,000,000 British pound). If a column has no value, - it may not be returned - granularity (str): Enum: "TOTAL" "DAY" "HOUR" "WEEK" "MONTH" - TOTAL - metrics are aggregated over the specified date range. - DAY - metrics are broken down daily. - HOUR - metrics are broken down hourly. - WEEKLY - metrics are broken down weekly. - MONTHLY - metrics are broken down monthly - click_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution - window for a pin click action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use - their defined attribution windows.. Defaults to 30. - engagement_window_days (int, optional): Enum: 1 7 30 60 Number of days to use as the conversion attribution - window for an engagement action. Engagements include saves, closeups, link clicks, and carousel card - swipes. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their defined attribution - windows. Defaults to 30. - view_window_days (int, optional): Enum: 1 7 30 60. Number of days to use as the conversion attribution - window for a view action. Applies to Pinterest Tag conversion metrics. Prior conversion tags use their - defined attribution windows. Defaults to 1. - conversion_report_time (str, optional): Enum: "TIME_OF_AD_ACTION" "TIME_OF_CONVERSION". The date by which - the conversion metrics returned from this endpoint will be reported. There are two dates associated - with a conversion event: the date that the user interacted with the ad, and the date that the user - completed a conversion event. Defaults to "TIME_OF_AD_ACTION". - attribution_types (str): Enum: "INDIVIDUAL" "HOUSEHOLD" - List of types of attribution for the conversion report - - Returns: - AnalyticsResponse: AnalyticsResponse object. - """ - kwargs['ad_account_id'] = self.id - kwargs['start_date'] = start_date - kwargs['end_date'] = end_date - kwargs['targeting_types'] = [AdsAnalyticsTargetingType(targeting_type) for targeting_type in targeting_types] - kwargs['columns'] = columns - kwargs['granularity'] = granularity - kwargs['click_window_days'] = click_window_days - kwargs['engagement_window_days'] = engagement_window_days - kwargs['view_window_days'] = view_window_days - kwargs['conversion_report_time'] = conversion_report_time - if attribution_types: - kwargs['attribution_types'] = ConversionReportAttributionType(attribution_types) - - ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( - params=kwargs, - api=AdAccountsApi, - analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, - entity=AdAccount, -======= - ad_account_analytics_response = AnalyticsUtils.get_ad_entity_analytics( - params=kwargs, - api=AdAccountsApi, - analytics_fn=AdAccountsApi.ad_account_analytics, - ad_entity=AdAccount, ->>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) -======= ad_account_analytics_response = AnalyticsUtils.get_entity_analytics( params=kwargs, api=AdAccountsApi, @@ -509,7 +382,6 @@ def get_targeting_analytics( api=AdAccountsApi, analytics_fn=AdAccountsApi.ad_account_targeting_analytics_get, entity=AdAccount, ->>>>>>> e470fd7 (fix conflicts) client=self._client ) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index 0d7c2ab..2f2e8ae 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -3,20 +3,11 @@ """ from __future__ import annotations -<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi -<<<<<<< HEAD - -======= -from openapi_generated.pinterest_client.api.ad_groups_api import AdGroupsApi - ->>>>>>> d1f7acc (Change generated client package name and version (#52)) -======= ->>>>>>> 58c044f (fix conflicts) from openapi_generated.pinterest_client.model.action_type import ActionType from openapi_generated.pinterest_client.model.budget_type import BudgetType from openapi_generated.pinterest_client.model.ad_group_response import AdGroupResponse diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index a7ddd24..d741a5a 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -3,16 +3,11 @@ """ from __future__ import annotations -<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.ads_analytics_targeting_type import AdsAnalyticsTargetingType from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType from openapi_generated.pinterest_client.api.ads_api import AdsApi -======= -from openapi_generated.pinterest_client.api.ads_api import AdsApi - ->>>>>>> d1f7acc (Change generated client package name and version (#52)) from openapi_generated.pinterest_client.model.ad_response import AdResponse from openapi_generated.pinterest_client.model.ad_create_request import AdCreateRequest from openapi_generated.pinterest_client.model.creative_type import CreativeType diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index e445192..5889e37 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -3,7 +3,6 @@ """ from __future__ import annotations -<<<<<<< HEAD from datetime import date from openapi_generated.pinterest_client.model.conversion_report_attribution_type import ConversionReportAttributionType @@ -12,18 +11,6 @@ from openapi_generated.pinterest_client.model.campaign_response import CampaignResponse from openapi_generated.pinterest_client.model.campaign_create_request import CampaignCreateRequest from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest -<<<<<<< HEAD - -======= -from openapi_generated.pinterest_client.api.campaigns_api import CampaignsApi - -from openapi_generated.pinterest_client.model.campaign_response import CampaignResponse -from openapi_generated.pinterest_client.model.campaign_create_request import CampaignCreateRequest -from openapi_generated.pinterest_client.model.campaign_update_request import CampaignUpdateRequest - ->>>>>>> d1f7acc (Change generated client package name and version (#52)) -======= ->>>>>>> 58c044f (fix conflicts) from openapi_generated.pinterest_client.model.objective_type import ObjectiveType from pinterest.ads.ad_groups import AdGroup diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index 3b46e6a..abb297b 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -3,16 +3,8 @@ """ from __future__ import annotations -<<<<<<< HEAD -<<<<<<< HEAD from datetime import date -======= ->>>>>>> d1f7acc (Change generated client package name and version (#52)) -======= -from datetime import date - ->>>>>>> aa63ab6 (Add get pin analytic (#73)) 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.inline_object import InlineObject diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index e24281f..54adfcd 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -6,12 +6,6 @@ from pinterest.utils.validations import AdsEntityType from pinterest.utils.base_model import PinterestBaseModel -<<<<<<< HEAD -======= - -from pinterest.client import PinterestSDKClient - ->>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) from pinterest.client import PinterestSDKClient @@ -21,8 +15,6 @@ class AnalyticsUtils: Utility class with functions to make model specific analytics api calls. """ @classmethod -<<<<<<< HEAD -<<<<<<< HEAD def get_entity_analytics( cls, params: list, @@ -30,23 +22,6 @@ def get_entity_analytics( analytics_fn: Callable, entity: PinterestBaseModel, client: PinterestSDKClient = None, -======= - def get_ad_entity_analytics( -======= - def get_entity_analytics( ->>>>>>> aa63ab6 (Add get pin analytic (#73)) - cls, - params: list, - api: type, - analytics_fn: Callable, -<<<<<<< HEAD - ad_entity: PinterestBaseModel, - client:PinterestSDKClient = None, ->>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) -======= - entity: PinterestBaseModel, - client: PinterestSDKClient = None, ->>>>>>> aa63ab6 (Add get pin analytic (#73)) **kwargs ) -> AnalyticsResponse: """ @@ -55,33 +30,15 @@ def get_entity_analytics( params (list): List of params api (type): analytics_fn (Callable): -<<<<<<< HEAD -<<<<<<< HEAD entity (PinterestBaseModel): -======= - ad_entity (PinterestBaseModel): ->>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) -======= - entity (PinterestBaseModel): ->>>>>>> aa63ab6 (Add get pin analytic (#73)) client (PinterestSDKClient, optional): Returns: AnalyticsResponse: """ return AnalyticsResponse( -<<<<<<< HEAD -<<<<<<< HEAD - entity_type=entity, - fields=params.get('columns', []), -======= - entity_type=ad_entity, - fields=params.get('columns'), ->>>>>>> c09fda9 (Ad Account `get_analytics` (#40)) -======= entity_type=entity, fields=params.get('columns', []), ->>>>>>> aa63ab6 (Add get pin analytic (#73)) raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) ) From 6efe21dca36f257fd6de32808a10bd6d412bc0bf Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:13:05 +0000 Subject: [PATCH 21/35] Integration tests for ad_groups analytics --- integration_tests/ads/test_ad_groups.py | 147 +++++++++++++++++++++++- 1 file changed, 143 insertions(+), 4 deletions(-) diff --git a/integration_tests/ads/test_ad_groups.py b/integration_tests/ads/test_ad_groups.py index d147e26..1c96df2 100644 --- a/integration_tests/ads/test_ad_groups.py +++ b/integration_tests/ads/test_ad_groups.py @@ -1,9 +1,15 @@ ''' Test AdGroup Model ''' +from datetime import date +from datetime import timedelta +from parameterized import parameterized + +from openapi_generated.pinterest_client.exceptions import ApiException +from openapi_generated.pinterest_client.model.targeting_spec import TargetingSpec from integration_tests.base_test import BaseTestCase -from integration_tests.config import DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import DEFAULT_AD_ACCOUNT_ID, DEFAULT_AD_GROUP_ID from pinterest.ads.ad_groups import AdGroup @@ -63,9 +69,7 @@ def test_update_success(self): ) new_name = "SDK_AD_GROUP_NEW_NAME" - new_spec = { - "GENDER": ["male"] - } + new_spec = TargetingSpec(gender=["male"]) ad_group.update_fields( name=new_name, @@ -221,3 +225,138 @@ def test_disable_auto_targeting(self): ad_group_id=getattr(ad_group_0, "_id") ) self.assertFalse(getattr(ad_group_1, "_auto_targeting_enabled")) + + +class TestGetAnalytics(BaseTestCase): + """ + Test getting Ad Group analytics + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_hour", "HOUR"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_ad_group_analytics_success(self, name, granularity): + + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","TOTAL_ENGAGEMENT","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + ad_group = AdGroup( + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + ad_group_id=DEFAULT_AD_GROUP_ID, + client=self.test_client, + ) + + ad_group_analytics = ad_group.get_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_group_analytics) + self.assertIsNotNone(ad_group_analytics.raw_response) + analytics_response = ad_group_analytics.raw_response.get('value') + for dict_item in analytics_response: + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_analytics_fail(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + ad_group = AdGroup( + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + ad_group_id=DEFAULT_AD_GROUP_ID, + client=self.test_client, + ) + with self.assertRaises(ApiException): + ad_group.get_analytics(**analytics_info_dict) + +class TestGetTargetingAnalytics(BaseTestCase): + """ + Test getting targeting analytics for Ad Group + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_ad_group_targeting_analytics_success(self, name, granularity): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + ad_group = AdGroup( + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + ad_group_id=DEFAULT_AD_GROUP_ID, + client=self.test_client, + ) + ad_group_analytics = ad_group.get_targeting_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_group_analytics) + self.assertIsNotNone(ad_group_analytics.raw_response) + analytics_response = ad_group_analytics.raw_response.get('data') + for dict_item in analytics_response: + self.assertIsNotNone(dict_item.get('metrics')) + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item.get('metrics')) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item.get('metrics')) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_ad_group_targeting_analytics_success(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + ad_group = AdGroup( + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + ad_group_id=DEFAULT_AD_GROUP_ID, + client=self.test_client, + ) + with self.assertRaises(ApiException): + ad_group.get_targeting_analytics(**analytics_info_dict) From ffb70b434ed0935ad9920856b35cf67a18dfe40f Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:19:16 +0000 Subject: [PATCH 22/35] Integrationn tests for ads analytics --- integration_tests/ads/test_ads.py | 162 ++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 10 deletions(-) diff --git a/integration_tests/ads/test_ads.py b/integration_tests/ads/test_ads.py index 0032c29..8fe10e7 100644 --- a/integration_tests/ads/test_ads.py +++ b/integration_tests/ads/test_ads.py @@ -1,15 +1,23 @@ """ Test Ad Model """ +from datetime import date +from datetime import timedelta +from unittest.mock import DEFAULT +from parameterized import parameterized from pinterest.ads.ads import Ad +from openapi_generated.pinterest_client.exceptions import ApiException from openapi_generated.pinterest_client.exceptions import ApiValueError from openapi_generated.pinterest_client.exceptions import NotFoundException from openapi_generated.pinterest_client.model.entity_status import EntityStatus from integration_tests.base_test import BaseTestCase -from integration_tests.config import DEFAULT_PIN_ID, DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import DEFAULT_PIN_ID +from integration_tests.config import DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import DEFAULT_AD_ID +from integration_tests.config import DEFAULT_AD_GROUP_ID class TestCreateAd(BaseTestCase): @@ -23,16 +31,15 @@ def test_create_ad_success(self): """ ad = Ad.create( ad_account_id=DEFAULT_AD_ACCOUNT_ID, - ad_group_id=self.ad_group_utils.get_ad_group_id(), - creative_type="REGULAR", + ad_group_id=DEFAULT_AD_GROUP_ID, + creative_type="IDEA", pin_id=DEFAULT_PIN_ID, name="Test_create_ad", - status="ACTIVE", + status="PAUSED", is_pin_deleted=False, is_removable=False, client=self.test_client, ) - assert ad assert getattr(ad, "_id") assert getattr(ad, "_name") == "Test_create_ad" @@ -43,7 +50,7 @@ def test_create_ad_failure_without_creative_type(self): """ ad_arguments = dict( ad_account_id=DEFAULT_AD_ACCOUNT_ID, - ad_group_id=self.ad_group_utils.get_ad_group_id(), + ad_group_id=DEFAULT_AD_GROUP_ID, pin_id=DEFAULT_PIN_ID, name="Test_create_ad", status="ACTIVE", @@ -59,7 +66,7 @@ def test_create_ad_failure_with_incorrect_creative_type(self): """ ad_arguments = dict( ad_account_id=DEFAULT_AD_ACCOUNT_ID, - ad_group_id=self.ad_group_utils.get_ad_group_id(), + ad_group_id=DEFAULT_AD_GROUP_ID, creative_type="NOT", pin_id=DEFAULT_PIN_ID, name="Test_create_ad", @@ -80,12 +87,12 @@ def test_get_ad_success(self): """ ad = Ad( ad_account_id=DEFAULT_AD_ACCOUNT_ID, - ad_id=self.ad_utils.get_ad_id(), + ad_id=DEFAULT_AD_ID, client=self.test_client, ) assert ad - assert getattr(ad, "_id") == self.ad_utils.get_ad_id() + assert getattr(ad, "_id") == DEFAULT_AD_ID def test_get_ad_fail_with_invalid_id(self): """ @@ -145,7 +152,7 @@ def test_update_ad_success(self): """ ad = Ad( ad_account_id=DEFAULT_AD_ACCOUNT_ID, - ad_id=self.ad_utils.get_ad_id(), + ad_id=DEFAULT_AD_ID, client=self.test_client, ) @@ -160,3 +167,138 @@ def test_update_ad_success(self): assert ad assert getattr(ad, "_name") == new_name assert getattr(ad, "_status") == EntityStatus(new_status) + +class TestGetAnalytics(BaseTestCase): + """ + Test getting Ad analytics + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_hour", "HOUR"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_ad_analytics_success(self, name, granularity): + + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + ad = Ad( + ad_account_id=analytics_info_dict.pop('ad_account_id'), + ad_id=DEFAULT_AD_ID, + client=self.test_client, + ) + + ad_analytics = ad.get_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_analytics) + self.assertIsNotNone(ad_analytics.raw_response) + analytics_response = ad_analytics.raw_response.get('value') + for dict_item in analytics_response: + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_analytics_fail(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + ad = Ad( + ad_account_id=analytics_info_dict.pop('ad_account_id'), + ad_id=DEFAULT_AD_ID, + client=self.test_client, + ) + with self.assertRaises(ApiException): + ad.get_analytics(**analytics_info_dict) + + +class TestGetTargetingAnalytics(BaseTestCase): + """ + Test getting targeting analytics + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_ad_targeting_analytics_success(self, name, granularity): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + ad = Ad( + ad_account_id=analytics_info_dict.pop('ad_account_id'), + ad_id=DEFAULT_AD_ID, + client=self.test_client, + ) + ad_analytics = ad.get_targeting_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_analytics) + self.assertIsNotNone(ad_analytics.raw_response) + analytics_response = ad_analytics.raw_response.get('data') + for dict_item in analytics_response: + self.assertIsNotNone(dict_item.get('metrics')) + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item.get('metrics')) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item.get('metrics')) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_ad_targeting_analytics_fail(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + ad = Ad( + ad_account_id=analytics_info_dict.pop('ad_account_id'), + ad_id=DEFAULT_AD_ID, + client=self.test_client, + ) + with self.assertRaises(ApiException): + ad.get_targeting_analytics(**analytics_info_dict) From e2c051b4e53cd576dc034f657d9a6d0bed67f3b6 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:21:46 +0000 Subject: [PATCH 23/35] Integration tests for campaigns analytics --- integration_tests/ads/test_campaigns.py | 141 +++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/integration_tests/ads/test_campaigns.py b/integration_tests/ads/test_campaigns.py index f56cb01..e76d8cf 100644 --- a/integration_tests/ads/test_campaigns.py +++ b/integration_tests/ads/test_campaigns.py @@ -1,10 +1,13 @@ """ Test Campaign Model """ - +from datetime import date +from datetime import timedelta +from parameterized import parameterized from unittest.mock import patch from openapi_generated.pinterest_client.model.objective_type import ObjectiveType +from openapi_generated.pinterest_client.exceptions import ApiException from openapi_generated.pinterest_client.exceptions import ApiValueError from openapi_generated.pinterest_client.exceptions import NotFoundException @@ -15,6 +18,7 @@ from integration_tests.base_test import BaseTestCase from integration_tests.config import DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import DEFAULT_CAMPAIGN_ID class TestCreateCampaign(BaseTestCase): @@ -233,3 +237,138 @@ def test_get_next_page_of_campaigns(self): assert isinstance(campaign, Campaign) assert created_campaign_ids == get_all_campaigns_ids + + +class TestGetAnalytics(BaseTestCase): + """ + Test getting Campaign analytics + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_hour", "HOUR"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_campaign_analytics_success(self, name, granularity): + + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","TOTAL_ENGAGEMENT","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + campaign = Campaign( + client=self.test_client, + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + campaign_id=DEFAULT_CAMPAIGN_ID, + ) + + campaign_analytics = campaign.get_analytics(**analytics_info_dict) + self.assertIsNotNone(campaign_analytics) + self.assertIsNotNone(campaign_analytics.raw_response) + analytics_response = campaign_analytics.raw_response.get('value') + for dict_item in analytics_response: + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_campaign_analytics_fail(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + campaign = Campaign( + client=self.test_client, + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + campaign_id=DEFAULT_CAMPAIGN_ID, + ) + with self.assertRaises(ApiException): + campaign.get_analytics(**analytics_info_dict) + +class TestGetTargetingAnalytics(BaseTestCase): + """ + Test getting targeting analytics for Campaigns + """ + DAYS_BACK = 2 + FURTHEST_DAYS_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + FURTHEST_DAYS_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_campaign_targeting_analytics_success(self, name, granularity): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + campaign = Campaign( + client=self.test_client, + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + campaign_id=DEFAULT_CAMPAIGN_ID, + ) + campaign_analytics = campaign.get_targeting_analytics(**analytics_info_dict) + self.assertIsNotNone(campaign_analytics) + self.assertIsNotNone(campaign_analytics.raw_response) + analytics_response = campaign_analytics.raw_response.get('data') + for dict_item in analytics_response: + self.assertIsNotNone(dict_item.get('metrics')) + for column in analytics_info_dict.get('columns'): + self.assertIn(column, dict_item.get('metrics')) + if granularity != 'TOTAL': + self.assertIn('DATE', dict_item.get('metrics')) + + @parameterized.expand( + [ + ("granularity_total","TOTAL", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_day", "DAY", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_hour", "HOUR", FURTHEST_DAYS_BACK_HOUR + 1), + ("granularity_week", "WEEK", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ("granularity_month", "MONTH", FURTHEST_DAYS_BACK_NOT_HOUR + 1), + ] + ) + def test_get_campaign_targeting_analytics_fail(self, name, granularity, days_back): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(days_back), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["SPEND_IN_MICRO_DOLLAR","SPEND_IN_DOLLAR", "TOTAL_ENGAGEMENT"], + 'granularity': granularity, + } + campaign = Campaign( + client=self.test_client, + ad_account_id=DEFAULT_AD_ACCOUNT_ID, + campaign_id=DEFAULT_CAMPAIGN_ID, + ) + with self.assertRaises(ApiException): + campaign.get_targeting_analytics(**analytics_info_dict) From cb9bfdbb8821fd589e1730545e9879129ca5a226 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:23:51 +0000 Subject: [PATCH 24/35] Integration tests for pins analytics --- integration_tests/organic/test_pins.py | 52 ++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/integration_tests/organic/test_pins.py b/integration_tests/organic/test_pins.py index 7ce8379..e6364a4 100644 --- a/integration_tests/organic/test_pins.py +++ b/integration_tests/organic/test_pins.py @@ -3,9 +3,12 @@ *NOTE*: Do not forget to delete pin after the test. """ +from datetime import date +from datetime import timedelta from parameterized import parameterized from openapi_generated.pinterest_client.exceptions import NotFoundException +from openapi_generated.pinterest_client.exceptions import ApiException from pinterest.organic.pins import Pin @@ -102,6 +105,7 @@ def test_save_pin_success(self, pin_save_kwargs): """ pin = self.pin_utils.create_new_pin(title="Test Saving Pin") assert pin + assert pin.title == "Test Saving Pin" pin.save(**pin_save_kwargs) @@ -117,3 +121,51 @@ def test_save_pin_success(self, pin_save_kwargs): pin_id=pin.id, client=self.test_client ) + +class TestGetAnalytics(BaseTestCase): + """ + Test getting a Pin analytics + """ + DAYS_BACK = 4 + EXPECTED_NUM_OF_DAILY_METRICS = DAYS_BACK + 1 + DAYS_BACK_OUT_OF_RANGE = 91 + METRIC_TYPES = "IMPRESSION,OUTBOUND_CLICK,PIN_CLICK,SAVE,SAVE_RATE,TOTAL_COMMENTS,TOTAL_REACTIONS" + + def validate_raw_response(self, raw_response): + self.assertIsNotNone(raw_response) + self.assertIsNotNone(raw_response.get('all')) + self.assertIsNotNone(raw_response.get('all').get('daily_metrics')) + self.assertIsNotNone(raw_response.get('all').get('lifetime_metrics')) + self.assertIsNotNone(raw_response.get('all').get('summary_metrics')) + self.assertEqual(len(raw_response.get('all').get('daily_metrics')), self.EXPECTED_NUM_OF_DAILY_METRICS) + for metric in self.METRIC_TYPES.split(','): + self.assertIn(metric, {**raw_response.get('all').get('summary_metrics'),**raw_response.get('all').get('lifetime_metrics')}) + + + def test_get_analytics_success(self): + """ + Test request the Pin analitycs + """ + analytics_info_dict = { + 'pin_id': DEFAULT_PIN_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'metric_types': ["IMPRESSION,OUTBOUND_CLICK,PIN_CLICK,SAVE,SAVE_RATE,TOTAL_COMMENTS,TOTAL_REACTIONS"], + } + pin = Pin(pin_id=analytics_info_dict.pop('pin_id')) + analytics = pin.get_analytics(**analytics_info_dict) + self.assertIsNotNone(analytics) + self.validate_raw_response(analytics.raw_response) + + def test_get_analytics_failure(self): + """Test request the Pin analytics from before 90 days ago. + """ + analytics_info_dict = { + 'pin_id': DEFAULT_PIN_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK_OUT_OF_RANGE), + 'end_date': date.today(), + 'metric_types': ["IMPRESSION,OUTBOUND_CLICK,PIN_CLICK,SAVE,SAVE_RATE,TOTAL_COMMENTS,TOTAL_REACTIONS"], + } + pin = Pin(pin_id=analytics_info_dict.pop('pin_id')) + with self.assertRaises(ApiException): + pin.get_analytics(**analytics_info_dict) \ No newline at end of file From e2061ccfdf5ff87899dead732b24b08716ec3e5f Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:27:11 +0000 Subject: [PATCH 25/35] Update integration tests for conversion events to test failure by asserting error is raised --- .../ads/test_conversion_events.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/integration_tests/ads/test_conversion_events.py b/integration_tests/ads/test_conversion_events.py index b3b9fba..0e203b1 100644 --- a/integration_tests/ads/test_conversion_events.py +++ b/integration_tests/ads/test_conversion_events.py @@ -8,6 +8,8 @@ from pinterest.client import PinterestSDKClient from pinterest.ads.conversion_events import Conversion +from openapi_generated.pinterest_client.exceptions import ApiException + class TestSendConversionEvent(BaseTestCase): """ Test send Conversion Event @@ -79,17 +81,12 @@ def test_send_conversion_fail(self): for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] - response = Conversion.send_conversion_events( - client = client, - ad_account_id = DEFAULT_AD_ACCOUNT_ID, - conversion_events = conversion_events, - test = True, - ) + with self.assertRaises(ApiException): + Conversion.send_conversion_events( + client = client, + ad_account_id = DEFAULT_AD_ACCOUNT_ID, + conversion_events = conversion_events, + test = True, + ) - assert response - assert response.num_events_received == 2 - assert response.num_events_processed == 0 - assert len(response.events) == 2 - assert 'hashed format' in response.events[0].error_message - assert 'hashed format' in response.events[0].error_message From 9bef17e17f538f043755bde79eee58b21ccfe1df Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:32:52 +0000 Subject: [PATCH 26/35] Update board and pin models with new properties --- pinterest/organic/boards.py | 35 +++++++++++++++++++++++++++++++++++ pinterest/organic/pins.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/pinterest/organic/boards.py b/pinterest/organic/boards.py index 0b57eee..929897c 100644 --- a/pinterest/organic/boards.py +++ b/pinterest/organic/boards.py @@ -205,7 +205,14 @@ def __init__( client (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default client, if not provided. """ self._id = None + self._created_at = None + self._board_pins_modified_at = None self._name = None + self._collaborator_count = None + self._pin_count = None + self._follower_count = None + self._media = None + self._owner = None self._description = None self._owner = None self._privacy = None @@ -227,11 +234,39 @@ def id(self) -> str: # pylint: disable=missing-function-docstring return self._id + @property + def created_at(self) -> str: + return self._created_at + + @property + def board_pins_modified_at(self) -> str: + return self._board_pins_modified_at + @property def name(self) -> str: # pylint: disable=missing-function-docstring return self._name + @property + def collaborator_count(self) -> int: + return self._collaborator_count + + @property + def pin_count(self) -> int: + return self._pin_count + + @property + def follower_count(self) -> int: + return self._follower_count + + @property + def media(self): + return self._media + + @property + def owner(self) -> str: + return self._owner + @property def description(self) -> str: # pylint: disable=missing-function-docstring diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index 0bd2f3d..82f9989 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -45,8 +45,12 @@ def __init__( client (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default client, if not provided. """ self._ad_account_id = None - self._id = None + self._is_owner = None + self._is_standard = None + self._note = None + self._has_been_promoted = None + self._creative_type = None self._created_at = None self._link = None self._title = None @@ -79,6 +83,26 @@ def id(self) -> str: # pylint: disable=missing-function-docstring return self._id + @property + def is_owner(self) -> str: + return self._is_owner + + @property + def is_standard(self) -> str: + return self._is_standard + + @property + def note(self) -> str: + return self._note + + @property + def has_been_promoted(self) -> str: + return self._has_been_promoted + + @property + def creative_type(self) -> str: + return self._creative_type + @property def created_at(self) -> str: # pylint: disable=missing-function-docstring @@ -286,7 +310,7 @@ def get_analytics( start_date: date, end_date: date, app_types: str = "ALL", - metric_types: str | list[str] = "ALL", + metric_types: list[str] = [], split_field: str = None, **kwargs ) -> AnalyticsResponse: @@ -320,6 +344,7 @@ def get_analytics( Returns: AnalyticsResponse: AnalyticsResponse object. """ + kwargs['pin_id'] = self._id if self._ad_account_id: kwargs['ad_account_id'] = self._ad_account_id if app_types: From bdb4cc6227300250efa33ad9f9e9e2470def8744 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:35:13 +0000 Subject: [PATCH 27/35] Fix valition of responses --- pinterest/utils/analytics.py | 6 +++--- pinterest/utils/validations.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index 54adfcd..e62c6b6 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -39,7 +39,7 @@ def get_entity_analytics( return AnalyticsResponse( entity_type=entity, fields=params.get('columns', []), - raw_response=getattr(api(client), analytics_fn)(**params, **kwargs) + raw_response=getattr(api(client), analytics_fn.__name__)(**params, **kwargs) ) @@ -56,11 +56,11 @@ def __init__( """ Initialize an Ads Analytics object. Args: - entity_type (str): Entity Type identifier. Enum: ad_account, campaign, ad_group, ad. + entity_type (PinterestBaseModel): Entity Type identifier. Enum: ad_account, campaign, ad_group, ad. fields (list[str]): _description_ raw_response (dict): _description_ """ - self._entity_type = AdsEntityType(entity_type).name + self._entity_type = entity_type.__name__.lower() self._fields = fields self._raw_response = raw_response diff --git a/pinterest/utils/validations.py b/pinterest/utils/validations.py index a965781..d35315c 100644 --- a/pinterest/utils/validations.py +++ b/pinterest/utils/validations.py @@ -1,6 +1,6 @@ from enum import Enum -def AdsEntityType(Enum): +class AdsEntityType(Enum): AD_ACCOUNT = "ad_account" CAMPAIGN = "campaign" ADGROUP = "ad_group" From 57f9b23c83c13fb2e62d22e9f6365d01d3934246 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:37:26 +0000 Subject: [PATCH 28/35] Avoid deletion of defatult test pin --- integration_tests/utils/organic_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration_tests/utils/organic_utils.py b/integration_tests/utils/organic_utils.py index b1ef35b..d11bcdb 100644 --- a/integration_tests/utils/organic_utils.py +++ b/integration_tests/utils/organic_utils.py @@ -79,4 +79,5 @@ def create_new_pin(self, **kwargs): return Pin.create(**_merge_default_params_with_params(self.get_default_params(), kwargs)) def delete_pin(self, pin_id): - return Pin.delete(pin_id=pin_id, client=self.test_client) + if pin_id != DEFAULT_PIN_ID: # Make sure default pin is not being deleted + return Pin.delete(pin_id=pin_id, client=self.test_client) From de41ef447fbc706d000145383ce56da2d2e51c95 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:38:15 +0000 Subject: [PATCH 29/35] Fix creative type for deafulttest pin --- integration_tests/utils/ads_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_tests/utils/ads_utils.py b/integration_tests/utils/ads_utils.py index 5aaa22d..c57de14 100644 --- a/integration_tests/utils/ads_utils.py +++ b/integration_tests/utils/ads_utils.py @@ -13,7 +13,7 @@ from pinterest.ads.conversion_tags import ConversionTag -from integration_tests.config import DEFAULT_PIN_ID, OWNER_USER_ID, DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import DEFAULT_AD_GROUP_ID, DEFAULT_PIN_ID, OWNER_USER_ID, DEFAULT_AD_ACCOUNT_ID def _merge_default_params_with_params(default_params, params): @@ -212,7 +212,7 @@ def __init__(self, client=None): self.ad = Ad.create( ad_account_id=DEFAULT_AD_ACCOUNT_ID, ad_group_id=getattr(self.ad_group, "_id"), - creative_type="REGULAR", + creative_type="IDEA", pin_id=DEFAULT_PIN_ID, name="Test_create_ad", status="ACTIVE", @@ -232,7 +232,7 @@ def get_default_params(self): return dict( ad_account_id=DEFAULT_AD_ACCOUNT_ID, ad_group_id=getattr(self.ad_group, "_id"), - creative_type="REGULAR", + creative_type="IDEA", pin_id=DEFAULT_PIN_ID, name="Test_create_ad", status="ACTIVE", From 089503635569f207a3a6cf041df7c018bf82dfaf Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:39:43 +0000 Subject: [PATCH 30/35] Update requirements.txt and setup.py to match last veersion of Pinterest-Generated-Client --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 101fee2..b998c63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Pinterest-Generated-Client==0.1.7 +Pinterest-Generated-Client==0.1.8 python-dateutil==2.8.2 six==1.16.0 urllib3==1.26.12 diff --git a/setup.py b/setup.py index edadc0c..93ef353 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.7" + "Pinterest-Generated-Client==0.1.8" ] long_description = (Path(__file__).parent / "README.md").read_text() From aa682d4123deb1e9f4b057d41286406e414cf615 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:41:10 +0000 Subject: [PATCH 31/35] Update config with new env variables --- integration_tests/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration_tests/config.py b/integration_tests/config.py index 2aceb4c..51bc751 100644 --- a/integration_tests/config.py +++ b/integration_tests/config.py @@ -9,3 +9,6 @@ DEFAULT_BOARD_SECTION_ID = os.environ.get('DEFAULT_BOARD_SECTION_ID', "") OWNER_USER_ID = os.environ.get('OWNER_USER_ID', "") DEFAULT_AD_ACCOUNT_ID = os.environ.get('DEFAULT_AD_ACCOUNT_ID', "") +DEFAULT_AD_ID = os.environ.get('DEFAULT_AD_ID', "") +DEFAULT_AD_GROUP_ID = os.environ.get('DEFAULT_AD_GROUP_ID', "") +DEFAULT_CAMPAIGN_ID = os.environ.get('DEFAULT_CAMPAIGN_ID', "") From adccaa0ba0f8f44a5c35188e6a5968fc8a5475b9 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:41:58 +0000 Subject: [PATCH 32/35] Update clean organic data --- integration_tests/clean_organic_data.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/integration_tests/clean_organic_data.py b/integration_tests/clean_organic_data.py index 92339c0..a73dfcc 100644 --- a/integration_tests/clean_organic_data.py +++ b/integration_tests/clean_organic_data.py @@ -9,10 +9,5 @@ def test_delete_organic_data(): """ Delete organic boards from default client """ - all_boards, _ = Board.get_all() - for board in all_boards: - if board.id == DEFAULT_BOARD_ID: - continue - Board.delete(board_id=board.id) + pass - assert len(Board.get_all()[0]) == 1 From cdc37ae1b14988f5ad8d9917b77275662e05755f Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Mon, 2 Oct 2023 15:42:54 +0000 Subject: [PATCH 33/35] Update integration tests for ad accounts analytics --- integration_tests/ads/test_ad_accounts.py | 63 ++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/integration_tests/ads/test_ad_accounts.py b/integration_tests/ads/test_ad_accounts.py index c83f04c..6d1aeff 100644 --- a/integration_tests/ads/test_ad_accounts.py +++ b/integration_tests/ads/test_ad_accounts.py @@ -1,7 +1,9 @@ """ Test Ad Account Model """ - +from datetime import date +from datetime import timedelta +from parameterized import parameterized from unittest.mock import patch from pinterest.ads.ad_accounts import AdAccount @@ -9,7 +11,8 @@ from pinterest.ads.audiences import Audience from integration_tests.base_test import BaseTestCase -from integration_tests.config import OWNER_USER_ID, DEFAULT_AD_ACCOUNT_ID +from integration_tests.config import OWNER_USER_ID +from integration_tests.config import DEFAULT_AD_ACCOUNT_ID class TestAdAccount(BaseTestCase): @@ -136,3 +139,59 @@ def test_list_customer_list_success(self): get_all_customer_list_ids.add(getattr(customer_list, "_id")) assert new_customer_list_ids == get_all_customer_list_ids + + +class TestGetAnalytics(BaseTestCase): + """ + Test getting Ad accounts analytics + """ + DAYS_BACK = 2 + FURTHEST_BACK_HOUR = 7 # Futhest allowed days back for granularity HOUR + MAX_RANGE_DAYS = 3 # Max time range in days for granularity HOUR + FURTHEST_BACK_NOT_HOUR = 90 # Futhest allowed days back for any granularity but HOUR + + @parameterized.expand( + [ + ("granularity_total","TOTAL"), + ("granularity_day", "DAY"), + ("granularity_hour", "HOUR"), + ("granularity_week", "WEEK"), + ("granularity_month", "MONTH"), + ] + ) + def test_get_ad_analytics_success(self, name, granularity): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(self.DAYS_BACK), + 'end_date': date.today(), + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': granularity, + } + ad_account = AdAccount( + ad_account_id=self.ad_account_utils.get_default_ad_account_id(), + client=self.test_client + ) + ad_account_analytics = ad_account.get_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_account_analytics) + self.assertIsNotNone(ad_account_analytics.raw_response) + + +class TestGetTargetingAnalytics(BaseTestCase): + + def test_get_ad_targeting_analytics_success(self): + analytics_info_dict = { + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, + 'start_date': date.today() - timedelta(2), + 'end_date': date.today(), + 'targeting_types':["GENDER"], + 'columns': ["ADVERTISER_ID","PIN_PROMOTION_ID","SPEND_IN_DOLLAR"], + 'granularity': 'DAY', + } + ad_account = AdAccount( + ad_account_id=self.ad_account_utils.get_default_ad_account_id(), + client=self.test_client + ) + ad_analytics = ad_account.get_targeting_analytics(**analytics_info_dict) + self.assertIsNotNone(ad_analytics) + self.assertIsNotNone(ad_analytics.raw_response) + self.assertIsNotNone(ad_analytics.raw_response.get('data')) From e64cdf4e1cabbc01840821b5d9588d52da627e37 Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Tue, 3 Oct 2023 17:52:44 +0000 Subject: [PATCH 34/35] Fix lint problems --- integration_tests/organic/__init__.py | 0 pinterest/ads/ad_accounts.py | 2 +- pinterest/organic/boards.py | 12 +++++++----- pinterest/organic/pins.py | 11 ++++++++--- pinterest/utils/analytics.py | 3 +-- setup.py | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 integration_tests/organic/__init__.py diff --git a/integration_tests/organic/__init__.py b/integration_tests/organic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index ed4f6cf..d367e16 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -398,4 +398,4 @@ def get_targeting_analytics( client=self._client ) - return ad_account_analytics_response \ No newline at end of file + return ad_account_analytics_response diff --git a/pinterest/organic/boards.py b/pinterest/organic/boards.py index 929897c..7821eb0 100644 --- a/pinterest/organic/boards.py +++ b/pinterest/organic/boards.py @@ -236,10 +236,12 @@ def id(self) -> str: @property def created_at(self) -> str: + # pylint: disable=missing-function-docstring return self._created_at @property def board_pins_modified_at(self) -> str: + # pylint: disable=missing-function-docstring return self._board_pins_modified_at @property @@ -249,24 +251,24 @@ def name(self) -> str: @property def collaborator_count(self) -> int: + # pylint: disable=missing-function-docstring return self._collaborator_count @property def pin_count(self) -> int: + # pylint: disable=missing-function-docstring return self._pin_count @property def follower_count(self) -> int: + # pylint: disable=missing-function-docstring return self._follower_count - + @property def media(self): + # pylint: disable=missing-function-docstring return self._media - @property - def owner(self) -> str: - return self._owner - @property def description(self) -> str: # pylint: disable=missing-function-docstring diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index 82f9989..53c11c1 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -85,22 +85,27 @@ def id(self) -> str: @property def is_owner(self) -> str: + # pylint: disable=missing-function-docstring return self._is_owner - + @property def is_standard(self) -> str: + # pylint: disable=missing-function-docstring return self._is_standard - + @property def note(self) -> str: + # pylint: disable=missing-function-docstring return self._note @property def has_been_promoted(self) -> str: + # pylint: disable=missing-function-docstring return self._has_been_promoted @property def creative_type(self) -> str: + # pylint: disable=missing-function-docstring return self._creative_type @property @@ -310,7 +315,7 @@ def get_analytics( start_date: date, end_date: date, app_types: str = "ALL", - metric_types: list[str] = [], + metric_types: list[str] = None, split_field: str = None, **kwargs ) -> AnalyticsResponse: diff --git a/pinterest/utils/analytics.py b/pinterest/utils/analytics.py index e62c6b6..eb79342 100644 --- a/pinterest/utils/analytics.py +++ b/pinterest/utils/analytics.py @@ -4,7 +4,6 @@ from __future__ import annotations from typing import Callable -from pinterest.utils.validations import AdsEntityType from pinterest.utils.base_model import PinterestBaseModel from pinterest.client import PinterestSDKClient @@ -80,4 +79,4 @@ def raw_response(self) -> dict: return self._raw_response def __str__(self) -> str: - return f"{self.raw_response}" \ No newline at end of file + return f"{self.raw_response}" diff --git a/setup.py b/setup.py index 93ef353..1fc0703 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def _get_prod_version(): "Pinterest-Generated-Client==0.1.8" ] -long_description = (Path(__file__).parent / "README.md").read_text() +long_description = (Path(__file__).parent / "README.md").read_text(encoding='UTF-8') package_root = os.path.abspath(os.path.dirname(__file__)) __version__ = None From 8028bc8d6e0c656ef2d49cf1eff59000ac5edd4b Mon Sep 17 00:00:00 2001 From: Gilderlane Ribeiro Alexandre Date: Tue, 3 Oct 2023 18:05:09 +0000 Subject: [PATCH 35/35] Update unit test --- tests/src/pinterest/ads/test_ad_groups.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/src/pinterest/ads/test_ad_groups.py b/tests/src/pinterest/ads/test_ad_groups.py index af61403..8e9935f 100644 --- a/tests/src/pinterest/ads/test_ad_groups.py +++ b/tests/src/pinterest/ads/test_ad_groups.py @@ -8,6 +8,7 @@ from openapi_generated.pinterest_client.model.ad_group_response import AdGroupResponse from openapi_generated.pinterest_client.model.ad_group_array_response import AdGroupArrayResponse from openapi_generated.pinterest_client.model.ad_group_array_response_element import AdGroupArrayResponseElement +from openapi_generated.pinterest_client.model.targeting_spec import TargetingSpec from pinterest.ads.ad_groups import AdGroup from pinterest.ads.ads import Ad @@ -101,9 +102,7 @@ def test_update_ad_group(self, get_mock, update_mock): """ update_mock.__name__ = "ad_groups_update" new_name = "SDK_AD_GROUP_NEW_NAME" - new_spec = { - "GENDER": ["male"] - } + new_spec = TargetingSpec(gender=["male"]) get_mock.return_value = AdGroupResponse( id=self.test_ad_group_id,