diff --git a/.github/workflows/cron_integrationtests.yml b/.github/workflows/cron_integrationtests.yml new file mode 100644 index 0000000..923c4d3 --- /dev/null +++ b/.github/workflows/cron_integrationtests.yml @@ -0,0 +1,37 @@ +name: Cron Integration Tests + +on: + schedule: + - cron: "0 17 * * *" + +jobs: + build: + environment: integ + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Run all integration tests + env: + PINTEREST_REFRESH_ACCESS_TOKEN: ${{ secrets.CI_REFRESH_ACCESS_TOKEN }} + PINTEREST_APP_SECRET: ${{ secrets.CI_APP_SECRET }} + PINTEREST_APP_ID: ${{ secrets.CI_APP_ID }} + PINTEREST_API_URI: ${{ secrets.CI_HOST_URI }} + CONVERSION_ACCESS_TOKEN: ${{ secrets.CI_CONVERSION_ACCESS_TOKEN }} + DEFAULT_BOARD_ID: ${{ secrets.CI_DEFAULT_BOARD_ID }} + DEFAULT_BOARD_NAME: ${{ secrets.CI_DEFAULT_BOARD_NAME }} + DEFAULT_PIN_ID: ${{ secrets.CI_DEFAULT_PIN_ID }} + DEFAULT_BOARD_SECTION_ID: ${{ secrets.CI_DEFAULT_BOARD_SECTION_ID }} + OWNER_USER_ID: ${{ secrets.CI_OWNER_USER_ID }} + DEFAULT_AD_ACCOUNT_ID: ${{ secrets.CI_DEFAULT_AD_ACCOUNT_ID }} + run: | + python -m pip install --upgrade pip + make install_dev + make integration_tests diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml index c45b135..8eb62f5 100644 --- a/.github/workflows/integrationtests.yml +++ b/.github/workflows/integrationtests.yml @@ -18,17 +18,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Run all integration tests env: - PINTEREST_REFRESH_ACCESS_TOKEN: ${{ secrets.CI_REFRESH_ACCESS_TOKEN }} - PINTEREST_APP_SECRET: ${{ secrets.CI_APP_SECRET }} - PINTEREST_APP_ID: ${{ secrets.CI_APP_ID }} - PINTEREST_API_URI: ${{ secrets.CI_HOST_URI }} - CONVERSION_ACCESS_TOKEN: ${{ secrets.CI_CONVERSION_ACCESS_TOKEN }} - DEFAULT_BOARD_ID: ${{ secrets.CI_DEFAULT_BOARD_ID }} - DEFAULT_BOARD_NAME: ${{ secrets.CI_DEFAULT_BOARD_NAME }} - DEFAULT_PIN_ID: ${{ secrets.CI_DEFAULT_PIN_ID }} - DEFAULT_BOARD_SECTION_ID: ${{ secrets.CI_DEFAULT_BOARD_SECTION_ID }} - OWNER_USER_ID: ${{ secrets.CI_OWNER_USER_ID }} - DEFAULT_AD_ACCOUNT_ID: ${{ secrets.CI_DEFAULT_AD_ACCOUNT_ID }} + PINTEREST_JSON_ENV_VARIABLES: ${{ secrets.CI_INTEG_TEST_JSON }} run: | python -m pip install --upgrade pip make install_dev diff --git a/.gitignore b/.gitignore index e53ae66..dd08ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ target/ .idea # Config files -.env \ No newline at end of file +.env +http_logs.txt \ No newline at end of file diff --git a/Makefile b/Makefile index 894858a..9883f26 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +.PHONY: integration_tests lint unit_tests clean_organic_data + install: @echo pip install pip install -r requirements.txt @@ -13,9 +15,13 @@ unit_tests: package_test: ./package_test/run.sh -integration_tests: +clean_organic_data: + @echo cleaning organic data... + python -m pytest ./integration_tests/clean_organic_data.py + +integration_tests: clean_organic_data @echo integration tests... - python -m pytest --cov ./pinterest/ --cov-branch ./integration_tests/ --cov-report term-missing + python -m pytest --ignore=clean_organic_data.py --cov ./pinterest/ --cov-branch ./integration_tests/ --cov-report term-missing clean: clean-build clean-pyc ## Clean @@ -55,4 +61,4 @@ flake: flake8 . --count --show-source --statistics flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics -lint: pylint flake \ No newline at end of file +lint: pylint flake diff --git a/docs/pinterest/README.md b/docs/pinterest/README.md index f0e550a..b436077 100644 --- a/docs/pinterest/README.md +++ b/docs/pinterest/README.md @@ -10,6 +10,8 @@ - [`ads.ads`](./ads.ads.md#module-adsads): Ads high level model - [`ads.audiences`](./ads.audiences.md#module-adsaudiences): High level module class for Audience object - [`ads.campaigns`](./ads.campaigns.md#module-adscampaigns): Campaign Class for Pinterest Python SDK +- [`ads.conversion_events`](./ads.conversion_events.md#module-adsconversion_events): Conversion Event Class for Pinterest Python SDK +- [`ads.conversion_tags`](./ads.conversion_tags.md#module-adsconversion_tags): Conversion Class for Pinterest Python SDK - [`ads.customer_lists`](./ads.customer_lists.md#module-adscustomer_lists): High level module class for Customer List object - [`ads.keywords`](./ads.keywords.md#module-adskeywords): High level module class for Keyword object - [`client`](./client.md#module-client): Pinterest Client @@ -31,6 +33,8 @@ - [`ads.Ad`](./ads.ads.md#class-ad): Ad model used to view, create, update its attributes - [`audiences.Audience`](./ads.audiences.md#class-audience): High level model class to manage audiences for an AdAccount - [`campaigns.Campaign`](./ads.campaigns.md#class-campaign): Campaign model used to view, create, update its attributes and list its different entities. +- [`conversion_events.Conversion`](./ads.conversion_events.md#class-conversion): Conversion Event Model used to send conversion events to Pinterest API +- [`conversion_tags.ConversionTag`](./ads.conversion_tags.md#class-conversiontag): Conversion Tag model used to view, create, update its attributes and list its different entities - [`customer_lists.CustomerList`](./ads.customer_lists.md#class-customerlist): High level model class to manage customer_lists for an CustomerList - [`keywords.Keyword`](./ads.keywords.md#class-keyword): High level model class to manage keywords - [`client.PinterestSDKClient`](./client.md#class-pinterestsdkclient): Wrapper API client for SDK high level models @@ -45,4 +49,5 @@ - [`error_handling.verify_api_response`](./utils.error_handling.md#function-verify_api_response): Verify that there are no errors in `response` received from api - [`load_json_config.load_json_config`](./utils.load_json_config.md#function-load_json_config): Parse a config.json file and then load all the variables found as environment variables. +- [`load_json_config.load_json_config_from_single_env_var`](./utils.load_json_config.md#function-load_json_config_from_single_env_var): Parse PINTEREST_JSON_ENV_VARIABLES environment variable to split long JSON string into - [`refresh_access_token.get_new_access_token`](./utils.refresh_access_token.md#function-get_new_access_token): Function used to retrieve a new access token for a user using the refresh token. diff --git a/docs/pinterest/ads.ad_accounts.md b/docs/pinterest/ads.ad_accounts.md index 4e3c6f6..8e57a69 100644 --- a/docs/pinterest/ads.ad_accounts.md +++ b/docs/pinterest/ads.ad_accounts.md @@ -40,6 +40,14 @@ Initialize an object of an AdAccount. +--- + +#### property created_time + + + + + --- #### property currency @@ -80,11 +88,19 @@ Initialize an object of an AdAccount. +--- + +#### property updated_time + + + + + --- - + ### classmethod `create` @@ -119,7 +135,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. --- - + ### method `list_audiences` @@ -153,7 +169,7 @@ Get a list of the audiences in the AdAccount, filtered by the specified argument --- - + ### method `list_campaigns` @@ -192,7 +208,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. --- - + ### method `list_customer_lists` diff --git a/docs/pinterest/ads.ad_groups.md b/docs/pinterest/ads.ad_groups.md index e2b94d3..e5f3e35 100644 --- a/docs/pinterest/ads.ad_groups.md +++ b/docs/pinterest/ads.ad_groups.md @@ -161,6 +161,14 @@ Initialize an AdGroup object. +--- + +#### property optimization_goal_metadata + + + + + --- #### property pacing_delivery_type @@ -237,7 +245,7 @@ Initialize an AdGroup object. --- - + ### classmethod `create` @@ -329,7 +337,7 @@ to ad campaigns. Some types of campaigns (e.g. budget optimization) have limits --- - + ### method `disable_auto_targeting` @@ -347,7 +355,7 @@ Disable auto-targeting for ad group. Also known as + ### classmethod `get_all` @@ -405,7 +413,7 @@ List ad groups based on provided campaign IDs or ad group IDs.(campaign_ids or a --- - + ### method `list_ads` @@ -441,7 +449,7 @@ Get list of ads under current AdGroup with specified arguments --- - + ### method `update_fields` diff --git a/docs/pinterest/ads.ads.md b/docs/pinterest/ads.ads.md index 97c9ecf..fbed9ba 100644 --- a/docs/pinterest/ads.ads.md +++ b/docs/pinterest/ads.ads.md @@ -258,7 +258,7 @@ Initialize an Ad object. --- - + ### classmethod `create` @@ -323,7 +323,7 @@ Create a new ad. Request must contain ad_group_id, creative_type, and the source --- - + ### classmethod `get_all` @@ -374,7 +374,7 @@ List ads that meet the filters provided: --- - + ### method `update_fields` diff --git a/docs/pinterest/ads.campaigns.md b/docs/pinterest/ads.campaigns.md index 4e2faf5..9a92d4c 100644 --- a/docs/pinterest/ads.campaigns.md +++ b/docs/pinterest/ads.campaigns.md @@ -142,6 +142,14 @@ Initialize a Campaign object. +--- + +#### property summary_status + + + + + --- #### property tracking_urls @@ -170,7 +178,7 @@ Initialize a Campaign object. --- - + ### method `activate` @@ -188,7 +196,7 @@ Activate a paused or archived campaign --- - + ### method `archive` @@ -206,7 +214,7 @@ Archive an active or paused campaign --- - + ### classmethod `create` @@ -298,7 +306,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. --- - + ### classmethod `get_all` @@ -340,7 +348,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. --- - + ### method `get_daily_budget` @@ -358,7 +366,7 @@ Get the current daily spend cap budget of the campaign. --- - + ### method `get_lifetime_budget` @@ -376,7 +384,7 @@ Get the current life time spend cap budget of the campaign. --- - + ### method `list_ad_groups` @@ -407,7 +415,7 @@ List ad groups directed related to campaign. This method makes a synchronous HTT --- - + ### method `pause` @@ -425,7 +433,7 @@ Pause an active or archived campaign --- - + ### method `set_daily_budget` @@ -449,7 +457,7 @@ Set new daily spend cap budget for the campaign. --- - + ### method `set_lifetime_budget` @@ -473,7 +481,7 @@ Set new life time spend cap budget for the campaign. --- - + ### method `update_fields` diff --git a/docs/pinterest/ads.conversion_events.md b/docs/pinterest/ads.conversion_events.md new file mode 100644 index 0000000..167bfd4 --- /dev/null +++ b/docs/pinterest/ads.conversion_events.md @@ -0,0 +1,99 @@ + + + + +# module `ads.conversion_events` +Conversion Event Class for Pinterest Python SDK + + + +--- + + + +## class `Conversion` +Conversion Event Model used to send conversion events to Pinterest API + + + + +--- + + + +### classmethod `create_conversion_event` + +```python +create_conversion_event( + event_name: 'str', + action_source: 'str', + event_time: 'int', + event_id: 'str', + user_data: 'dict', + custom_data: 'dict', + event_source_url: 'str' = None, + partner_name: 'str' = None, + app_id: 'str' = None, + app_name: 'str' = None, + app_version: 'str' = None, + device_brand: 'str' = None, + device_carrier: 'str' = None, + device_model: 'str' = None, + device_type: 'str' = None, + os_version: 'str' = None, + language: 'str' = None, + **kwargs +) → ConversionEventsData +``` + +Create Conversion Event Data to be sent. + + + +**Args:** + + - `event_name` (str): The type of the user event, Enum: "add_to_cart", "checkout", "custom", "lead", "page_visit", "search", "signup", "view_category", "watch_video" + - `action_source` (str): The source indicating where the conversion event occurred, Enum: "app_adroid", "app_ios", "web", "offline" + - `event_time` (int): The time when the event happened. Unix timestamp in seconds + - `event_id` (str): The unique id string that identifies this event and can be used for deduping between events ingested via both the conversion API and Pinterest tracking + - `user_data` (dict): Object containing customer information data. Note, it is required at least one of 1) em, 2) hashed_maids or 3) pair client_ip_address + client_user_agent. + - `custom_data` (dict): Object containing other custom data. + - `event_source_url` (str, optional): URL of the web conversion event + - `partner_name` (str, optional): The third party partner name responsible to send the event to Conversion API on behalf of the adverstiser. Only send this field if Pinterest has worked directly with you to define a value for partner_name. + - `app_id` (str, optional): The app store app ID. + - `app_name` (str, optional): Name of the app. + - `app_version` (str, optional): Version of the app. + - `device_brand` (str, optional): Brand of the user device. + - `device_carrier` (str, optional): User device's model carrier. + - `device_model` (str, optional): Model of the user device. + - `device_type` (str, optional): Type of the user device. + - `os_version` (str, optional): Version of the device operating system. + - `language` (str, optional): Two-character ISO-639-1 language code indicating the user's language. + + + +**Returns:** + + - `ConversionEventsData`: ConversionEventData to be sent + +--- + + + +### classmethod `send_conversion_events` + +```python +send_conversion_events( + ad_account_id: 'str', + conversion_events: 'list[ConversionEventsData]', + test: 'bool' = False, + client: 'PinterestSDKClient' = None, + **kwargs +) → tuple(int, int, list[ConversionApiResponseEvents]) +``` + +Send conversion events to Pinterest API for Conversions. + +Note: Highly recommend to use create_client_with_token (with Conversion Access Token) to create different client for this functionality. + + diff --git a/docs/pinterest/ads.conversion_tags.md b/docs/pinterest/ads.conversion_tags.md new file mode 100644 index 0000000..6666fd9 --- /dev/null +++ b/docs/pinterest/ads.conversion_tags.md @@ -0,0 +1,271 @@ + + + + +# module `ads.conversion_tags` +Conversion Class for Pinterest Python SDK + + + +--- + + + +## class `ConversionTag` +Conversion Tag model used to view, create, update its attributes and list its different entities + + + +### method `__init__` + +```python +__init__( + ad_account_id: 'str', + conversion_tag_id: 'str', + client: 'PinterestSDKClient' = None, + **kwargs +) → None +``` + +Initialize Conversion Tag Object. + + + +**Args:** + + - `ad_account_id` (str): ConversionTag's Ad Account ID + - `conversion_tag_id` (str): ConversionTag ID, must be associated with Ad Account ID provided + - `client` (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default client, if not provided. + + +--- + +#### property ad_account_id + + + + + +--- + +#### property code_snippet + + + + + +--- + +#### property configs + + + + + +--- + +#### property enhanced_match_status + + + + + +--- + +#### property id + + + + + +--- + +#### property last_fired_time_ms + + + + + +--- + +#### property name + + + + + +--- + +#### property status + + + + + +--- + +#### property version + + + + + + + +--- + + + +### classmethod `create` + +```python +create( + ad_account_id: 'str', + name: 'str', + aem_enabled: 'bool' = None, + md_frequency: 'float' = None, + aem_fnln_enabled: 'bool' = None, + aem_ph_enabled: 'bool' = None, + aem_ge_enabled: 'bool' = None, + aem_db_enabled: 'bool' = None, + aem_loc_enabled: 'bool' = None, + client: 'PinterestSDKClient' = None, + **kwargs +) → ConversionTag +``` + +Create a conversion tag, also known as Pinterest tag with the option to enable enhance match.

+ +The Pinterest Tag tracks actions people take on the ad account’ s website after they view the ad account's ad on Pinterest. The advertiser needs to customize this tag to track conversions.

+ +For more information, see:

+ +Set up the Pinterest tag

+ +Pinterest Tag

+ +Enhanced match" + + + +**Args:** + + - `ad_account_id` (str): ConversionTag's Ad Account ID + - `name` (str): ConversionTag name + - `aem_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match email is enabled. See Enhanced match for more information. + + + - `md_frequency` (float=1.0, Nullable): Metadata ingestion frequency. + - `aem_fnln_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match name is enabled. See Enhanced match for more information. + + + - `aem_ph_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match phone is enabled. See Enhanced match for more information. + + + - `aem_ge_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match gender is enabled. See Enhanced match for more information. + + + - `aem_db_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match birthdate is enabled. See Enhanced match for more information. + + + - `aem_loc_enabled` (bool=False, Nullable): Whether Automatic Enhanced Match location is enabled. See Enhanced match for more information. + + + +**Returns:** + + - `ConversionTag`: ConversionTag Object + +--- + + + +### classmethod `get_all` + +```python +get_all( + ad_account_id: 'str', + filter_deleted: 'bool' = False, + client: 'PinterestSDKClient' = None, + **kwargs +) → list[ConversionTag] +``` + +Get a list of ConversionTag, filter by specified arguments + + + +**Args:** + + - `ad_account_id` (str): Unique identifier of an ad account. + - `filter_deleted` (bool=False, optional): Filter out deleted tags. + - `client` (_type_, optional): PinterestSDKClient Object. Uses the default client, if not provided. + + + +**Returns:** + + - `list[ConversionTag]`: List of ConversionTags + +--- + + + +### classmethod `get_ocpm_eligible_conversion_tag_events` + +```python +get_ocpm_eligible_conversion_tag_events( + ad_account_id: 'str', + client: 'PinterestSDKClient' = None, + **kwargs +) → tuple[str, list[ConversionEventResponse]] +``` + +Get OCPM eligible conversion tag events for an Ad Account. + + + +**Args:** + + - `ad_account_id` (str): Ad Account ID + - `client` (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default client, if not provided. + + + +**Returns:** + + - `list[ConversionEventResponse]`: List of ConversionTagEvent + +--- + + + +### classmethod `get_page_visit_conversion_tag_events` + +```python +get_page_visit_conversion_tag_events( + ad_account_id: 'str', + page_size: 'int' = None, + order: 'str' = 'ASCENDING', + bookmark: 'str' = None, + client: 'PinterestSDKClient' = None, + **kwargs +) → tuple[list[ConversionEventResponse], Bookmark] +``` + +Get page visit conversion tag events for an ad account. + + + +**Args:** + + - `ad_account` (str): Ad Account ID + - `client` (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default client, if not provided. + + + +**Returns:** + + - `list[ConversionEventResponse]`: List of ConversionTagEvent + + diff --git a/docs/pinterest/organic.boards.md b/docs/pinterest/organic.boards.md index 296e088..4b1ba75 100644 --- a/docs/pinterest/organic.boards.md +++ b/docs/pinterest/organic.boards.md @@ -419,8 +419,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. list_pins( section_id: 'str' = None, page_size: 'int' = None, - bookmark: 'str' = None, - **kwargs + bookmark: 'str' = None ) → tuple[list[Pin], Bookmark] ``` diff --git a/docs/pinterest/organic.pins.md b/docs/pinterest/organic.pins.md index addad14..c8b5df2 100644 --- a/docs/pinterest/organic.pins.md +++ b/docs/pinterest/organic.pins.md @@ -9,12 +9,12 @@ Pin Class for Pinterest Python SDK --- - + ## class `Pin` Pin model used to view, create, update its attributes and list its different entities. - + ### method `__init__` @@ -152,7 +152,7 @@ By default, the "operation user_account" is the token user_account. Optional: Bu --- - + ### classmethod `create` @@ -212,7 +212,7 @@ Keyword Args: Any valid keyword arguments or query parameters for endpoint. --- - + ### classmethod `delete` @@ -240,7 +240,7 @@ Delete a Pin owned by the "operation user_account". --- - + ### method `save` diff --git a/docs/pinterest/utils.base_model.md b/docs/pinterest/utils.base_model.md index 061594d..ba5fe1c 100644 --- a/docs/pinterest/utils.base_model.md +++ b/docs/pinterest/utils.base_model.md @@ -9,12 +9,12 @@ Pinterest Base Model --- - + ## class `PinterestBaseModel` Base Model for all other Higher Level Models in the Python Client - + ### method `__init__` diff --git a/docs/pinterest/utils.bookmark.md b/docs/pinterest/utils.bookmark.md index 67039cb..677ca0d 100644 --- a/docs/pinterest/utils.bookmark.md +++ b/docs/pinterest/utils.bookmark.md @@ -45,7 +45,7 @@ Initialize a Bookmark object. --- - + ### method `get_bookmark_token` diff --git a/docs/pinterest/utils.load_json_config.md b/docs/pinterest/utils.load_json_config.md index 7800ae2..6a6cb6a 100644 --- a/docs/pinterest/utils.load_json_config.md +++ b/docs/pinterest/utils.load_json_config.md @@ -19,3 +19,16 @@ load_json_config() Parse a config.json file and then load all the variables found as environment variables. +--- + + + +## function `load_json_config_from_single_env_var` + +```python +load_json_config_from_single_env_var() +``` + +Parse PINTEREST_JSON_ENV_VARIABLES environment variable to split long JSON string into individual environment variables. + + diff --git a/docs/pinterest/utils.sdk_exceptions.md b/docs/pinterest/utils.sdk_exceptions.md index f563d5c..1cea340 100644 --- a/docs/pinterest/utils.sdk_exceptions.md +++ b/docs/pinterest/utils.sdk_exceptions.md @@ -19,7 +19,7 @@ Raises an exception for Model's Errors ### method `__init__` ```python -__init__(status=None, reason=None, http_resp=None) +__init__(status=None, reason=None, http_resp=None, body=None) ``` diff --git a/docs/utils/README.md b/docs/utils/README.md index 4c57256..4a08596 100644 --- a/docs/utils/README.md +++ b/docs/utils/README.md @@ -11,4 +11,4 @@ python3 docs/utils/script.py ## Documentation guide -If you make change to this package, you have to regenerate doc, especially changes that add or remove module, you have to update `MODULES` and `IGNORED_FILES` under `script.py` \ No newline at end of file +When making change that add or remove module, you have to update `MODULES` and `IGNORED_FILES_INDEXING` accordingly and `IGNORE_MODULES` if needed. \ No newline at end of file diff --git a/docs/utils/python-sdk-doc.yaml b/docs/utils/python-sdk-doc.yaml index 485bb35..531d11c 100644 --- a/docs/utils/python-sdk-doc.yaml +++ b/docs/utils/python-sdk-doc.yaml @@ -9,7 +9,7 @@ info: url: https://spdx.org/licenses/MIT termsOfService: https://developers.pinterest.com/terms/ title: Pinterest Python SDK - version: 0.1.4 + version: 0.2.0 openapi: 3.0.3 paths: /test: @@ -45,9 +45,10 @@ tags: \ account. \n

  • client (PinterestSDKClient, optional):\ \ PinterestSDKClient Object. Defaults to default_api_client.
  • \n\ \n
    \n

    property country

    \n
    \n

    property\ - \ currency

    \n
    \n

    property id

    \n
    \n

    property\ - \ name

    \n
    \n

    property owner

    \n
    \n

    property\ - \ permissions

    \n
    \n

    \n


    \n

    property currency

    \n
    \n

    property\ + \ id

    \n
    \n

    property name

    \n
    \n

    property\ + \ owner

    \n
    \n

    property permissions

    \n
    \n

    property\ + \ updated_time

    \n
    \n

    \n

    classmethod create

    \n

    python\n\ create(\n name: 'str',\n owner_user_id: 'str',\n country: 'str',\n \ @@ -66,7 +67,7 @@ tags: \ (PinterestSDKClient): PinterestSDKClient Object \n\n

    Keyword Args:\ \ Any valid keyword arguments or query parameters for endpoint.

    \n

    Returns:

    \n\ \n
    \n\ -

    \n

    method list_audiences

    \n

    python\n\ list_audiences(\n entity_statuses: 'list[str]' = None,\n page_size: 'int'\ @@ -85,7 +86,7 @@ tags: \ Cursor used to fetch the next page of items. Defaults to None. \n\n\

    Returns:

    \n\n
    \n

    \n\n


    \n

    \n

    method list_campaigns

    \n

    python\n\ list_campaigns(\n campaign_ids: 'list[str]' = None,\n entity_statuses: 'list[str]'\ @@ -111,7 +112,7 @@ tags:

    Keyword Args: Any valid keyword arguments or query parameters for endpoint.\ \

    \n

    Returns:

    \n
      \n
    • list[Campaign]:\ \ List of Campaign Objects
    • \n
    • Bookmark: Bookmark\ - \ for pagination if present, else None.
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method list_customer_lists

    \n

    python\n\ list_customer_lists(\n page_size: 'int' = None,\n order: 'str' = None,\n\ @@ -156,12 +157,13 @@ tags: \ dca_assets\n


    \n

    property end_time

    \n
    \n

    property\ \ feed_profile_id

    \n
    \n

    property id

    \n
    \n

    property\ \ lifetime_frequency_cap

    \n
    \n

    property name

    \n\n

    property pacing_delivery_type

    \n
    \n

    property\ - \ placement_group

    \n
    \n

    property start_time

    \n
    \n\ -

    property status

    \n
    \n

    property summary_status

    \n\ -
    \n

    property targeting_spec

    \n
    \n

    property\ - \ tracking_urls

    \n
    \n

    property type

    \n
    \n

    property\ - \ updated_time

    \n
    \n

    \n

    property optimization_goal_metadata

    \n
    \n

    property\ + \ pacing_delivery_type

    \n
    \n

    property placement_group

    \n\ +
    \n

    property start_time

    \n
    \n

    property\ + \ status

    \n
    \n

    property summary_status

    \n
    \n

    property\ + \ targeting_spec

    \n
    \n

    property tracking_urls

    \n\n

    property type

    \n
    \n

    property updated_time

    \n\ +
    \n

    \n

    classmethod create

    \n

    python\n\ create(\n ad_account_id: 'str',\n name: 'str',\n campaign_id: 'str',\n\ @@ -223,21 +225,21 @@ tags: \ target=\"_blank\">\"expanded targeting\". Defaults to None. \n

  • client\ \ (PinterestSDKClient, optional): PinterestSDKClient Object. Defaults to default_api_client.\ \
  • \n\n

    Returns:

    \n
      \n
    • AdGroup:\ - \ AdGroup Object
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method disable_auto_targeting

    \n

    python\n\ disable_auto_targeting()

    \n

    Disable auto-targeting for ad group. Also\ \ known as \"\ expanded targeting\".

    \n

    Returns:

    \n
      \n
    • bool:\ \ true if ad group disable auto_targeting_enabled
    • \n
    \n
    \n

    \n

    method enable_auto_targeting

    \n

    python\n\ enable_auto_targeting()

    \n

    Enable auto-targeting for ad group. Also\ \ known as \"\ expanded targeting\".

    \n

    Returns:

    \n
      \n
    • bool:\ \ true if ad group enable auto_targeting_enabled
    • \n
    \n
    \n

    \n

    classmethod get_all

    \n

    python\n\ get_all(\n ad_account_id: 'str',\n campaign_ids: 'list[str]' = None,\n \ @@ -259,7 +261,7 @@ tags: \ (PinterestSDKClient, optional): description. Defaults to default_api_client.\ \ \n\n

    Returns:

    \n
      \n
    • tuple[list[AdGroup],\ \ Bookmark]: description
    • \n
    \n
    \n

    \n

    method list_ads

    \n

    python\n\ list_ads(\n ad_ids: 'list[str]' = None,\n entity_statuses: 'list[str]' =\ @@ -279,7 +281,7 @@ tags: \ Cursor used to fetch the next page of items. Defaults to None. \n\n\

    Returns:

    \n
      \n
    • list[Ad]: List\ \ of Ads
    • \n
    • Bookmark: Bookmark object
    • \n
    \n\ -
    \n

    \n

    \n

    method update_fields

    \n

    python\n\ update_fields(**kwargs) \u2192 bool

    \n

    Update adgroup fields using\ @@ -320,7 +322,7 @@ tags: \ summary_status\n


    \n

    property tracking_urls

    \n\n

    property type

    \n
    \n

    property updated_time

    \n\
    \n

    property view_tracking_url

    \n
    \n

    \n

    classmethod create

    \n

    python\n\ create(\n ad_account_id: 'str',\n ad_group_id: 'str',\n creative_type:\ @@ -372,7 +374,7 @@ tags: \ URL for ad impressions. Defaults to None. \n

  • client\ \ (PinterestSDKClient, optional): PinterestSDKClient Object. Defaults to default_api_client.\ \
  • \n\n

    Returns:

    \n
      \n
    • Ad:\ - \ The newly created Ad.
    • \n
    \n
    \n

    \n\n


    \n

    \n

    classmethod get_all

    \n

    python\n\ get_all(\n ad_account_id: 'str',\n campaign_ids: 'list[str]' = None,\n \ @@ -404,7 +406,7 @@ tags: \ \n

  • client (ApiClient): ApiClient Object
  • \n\ \n

    Returns:

    \n
      \n
    • list[Ad]:\ \ List of Ad Objects
    • \n
    • Bookmark: Bookmark for pagination\ - \ if present, else None.
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method update_fields

    \n

    python\n\ update_fields(**kwargs) \u2192 bool

    \n

    Update Ad fields suing any\ @@ -543,21 +545,21 @@ tags: \ lifetime_spend_cap\n


    \n

    property name

    \n
    \n\

    property objective_type

    \n
    \n

    property order_line_id

    \n\
    \n

    property start_time

    \n
    \n

    property\ - \ status

    \n
    \n

    property tracking_urls

    \n
    \n

    property\ - \ type

    \n
    \n

    property updated_time

    \n
    \n

    \n


    \n

    property summary_status

    \n
    \n

    property\ + \ tracking_urls

    \n
    \n

    property type

    \n
    \n

    property\ + \ updated_time

    \n
    \n

    \n

    method activate

    \n

    python\n\ activate() \u2192 bool

    \n

    Activate a paused or archived campaign

    \n\

    Returns:

    \n
      \n
    • bool: If campaign\ \ status was successfully changed to 'ACTIVE'
    • \n
    \n
    \n

    \n

    method archive

    \n

    python\n\ archive() \u2192 bool

    \n

    Archive an active or paused campaign

    \n\

    Returns:

    \n
      \n
    • bool: If campaign\ \ status was successfully changed to 'ARCHIVED'
    • \n
    \n
    \n

    \n

    classmethod create

    \n

    python\n\ create(\n ad_account_id: 'str',\n name: 'str',\n objective_type: 'str',\n\ @@ -623,7 +625,7 @@ tags: \ optional): PinterestSDKClient Object, uses the default client, if not provided.\ \ \n\n

    Keyword Args: Any valid keyword arguments or query parameters\ \ for endpoint.

    \n

    Returns:

    \n
      \n
    • Campaign:\ - \ Campaign Object
    • \n
    \n
    \n

    \n\n


    \n

    \n

    classmethod get_all

    \n

    python\n\ get_all(\n ad_account_id: 'str',\n campaign_ids: 'list[str]' = None,\n \ @@ -648,17 +650,17 @@ tags: \ Object \n\n

    Keyword Args: Any valid keyword arguments or query parameters\ \ for endpoint.

    \n

    Returns:

    \n
      \n
    • list[Campaign]:\ \ List of Campaign Objects
    • \n
    • Bookmark: Bookmark\ - \ for pagination if present, else None.
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method get_daily_budget

    \n

    python\n\ get_daily_budget() \u2192 float

    \n

    Get the current daily spend cap\ \ budget of the campaign.

    \n

    Returns:

    \n
      \n
    • float:\ - \ Current daily spend cap budget
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method get_lifetime_budget

    \n

    python\n\ get_lifetime_budget() \u2192 float

    \n

    Get the current life time spend\ \ cap budget of the campaign.

    \n

    Returns:

    \n
      \n
    • float:\ - \ Current life time spend cap budget
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method list_ad_groups

    \n

    python\n\ list_ad_groups(\n page_size: 'int' = 25,\n order: 'str' = 'ASCENDING',\n\ @@ -672,12 +674,12 @@ tags: \ description. Defaults to None. \n

  • entity_statuses\ \ (str, optional): description. Defaults to None.
  • \n\n

    Returns:

    \n\
      \n
    • tuple[list[AdGroup], Bookmark]: description\ - \
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method pause

    \n

    python\npause()\ \ \u2192 bool

    \n

    Pause an active or archived campaign

    \n

    Returns:

    \n\
      \n
    • bool: If campaign status was successfully changed\ - \ to 'PAUSED'
    • \n
    \n
    \n

    \n\n


    \n

    \n

    method set_daily_budget

    \n

    python\n\ set_daily_budget(new_spend_cap: 'int') \u2192 bool

    \n

    Set new daily\ @@ -685,7 +687,7 @@ tags:

  • new_spend_cap (int): The new campaign daily spending\ \ cap.
  • \n\n

    Returns:

    \n
      \n
    • bool:\ \ If the daily budget was changed successfully
    • \n
    \n
    \n

    \n

    method set_lifetime_budget

    \n

    python\n\ set_lifetime_budget(new_spend_cap: 'int') \u2192 bool

    \n

    Set new life\ @@ -693,7 +695,7 @@ tags:

      \n
    • new_spend_cap (int): The new campaign total spending\ \ cap.
    • \n
    \n

    Returns:

    \n
      \n
    • bool:\ \ If the lifetime budget was changed successfully
    • \n
    \n
    \n

    \n

    method update_fields

    \n

    python\n\ update_fields(**kwargs) \u2192 bool

    \n

    Update the campaign fields\ @@ -702,6 +704,155 @@ tags: \ If campaign fields were successfully updated \n" name: campaigns x-traitTag: false +- description: "\n\n

    \n

    module ads.conversion_events

    \n

    Conversion\ + \ Event Class for Pinterest Python SDK

    \n
    \n

    \n

    class Conversion

    \n

    Conversion Event\ + \ Model used to send conversion events to Pinterest API

    \n
    \n

    \n

    classmethod create_conversion_event

    \n\ +

    python\ncreate_conversion_event(\n event_name: 'str',\n action_source:\ + \ 'str',\n event_time: 'int',\n event_id: 'str',\n user_data: 'dict',\n\ + \ custom_data: 'dict',\n event_source_url: 'str' = None,\n partner_name:\ + \ 'str' = None,\n app_id: 'str' = None,\n app_name: 'str' = None,\n app_version:\ + \ 'str' = None,\n device_brand: 'str' = None,\n device_carrier: 'str' =\ + \ None,\n device_model: 'str' = None,\n device_type: 'str' = None,\n \ + \ os_version: 'str' = None,\n language: 'str' = None,\n **kwargs\n) \u2192\ + \ ConversionEventsData

    \n

    Create Conversion Event Data to be sent.\ + \

    \n

    Args:

    \n
      \n
    • event_name\ + \ (str): The type of the user event, Enum: \"add_to_cart\", \"checkout\", \"\ + custom\", \"lead\", \"page_visit\", \"search\", \"signup\", \"view_category\"\ + , \"watch_video\"
    • \n
    • action_source (str): The source\ + \ indicating where the conversion event occurred, Enum: \"app_adroid\", \"app_ios\"\ + , \"web\", \"offline\"
    • \n
    • event_time (int): The time\ + \ when the event happened. Unix timestamp in seconds
    • \n
    • event_id\ + \ (str): The unique id string that identifies this event and can be used for\ + \ deduping between events ingested via both the conversion API and Pinterest\ + \ tracking
    • \n
    • user_data (dict): Object containing\ + \ customer information data. Note, it is required at least one of 1) em, 2) hashed_maids\ + \ or 3) pair client_ip_address + client_user_agent.
    • \n
    • custom_data\ + \ (dict): Object containing other custom data.
    • \n
    • event_source_url\ + \ (str, optional): URL of the web conversion event
    • \n
    • partner_name\ + \ (str, optional): The third party partner name responsible to send the event\ + \ to Conversion API on behalf of the adverstiser. Only send this field if Pinterest\ + \ has worked directly with you to define a value for partner_name.
    • \n
    • app_id\ + \ (str, optional): The app store app ID.
    • \n
    • app_name\ + \ (str, optional): Name of the app.
    • \n
    • app_version\ + \ (str, optional): Version of the app.
    • \n
    • device_brand\ + \ (str, optional): Brand of the user device.
    • \n
    • device_carrier\ + \ (str, optional): User device's model carrier.
    • \n
    • device_model\ + \ (str, optional): Model of the user device.
    • \n
    • device_type\ + \ (str, optional): Type of the user device.
    • \n
    • os_version\ + \ (str, optional): Version of the device operating system.
    • \n
    • language\ + \ (str, optional): Two-character ISO-639-1 language code indicating the user's\ + \ language.
    • \n
    \n

    Returns:

    \n
      \n
    • ConversionEventsData:\ + \ ConversionEventData to be sent
    • \n
    \n
    \n

    \n

    classmethod send_conversion_events

    \n\ +

    python\nsend_conversion_events(\n ad_account_id: 'str',\n conversion_events:\ + \ 'list[ConversionEventsData]',\n test: 'bool' = False,\n client: 'PinterestSDKClient'\ + \ = None,\n **kwargs\n) \u2192 tuple(int, int, list[ConversionApiResponseEvents])

    \n\ +

    Send conversion events to Pinterest API for Conversions.

    \n

    Note: Highly\ + \ recommend to use create_client_with_token (with Conversion Access Token) to\ + \ create different client for this functionality.

    " + name: conversion_events + x-traitTag: false +- description: "\n\n

    \n

    module ads.conversion_tags

    \n

    Conversion\ + \ Class for Pinterest Python SDK

    \n
    \n

    \n

    class ConversionTag

    \n

    Conversion\ + \ Tag model used to view, create, update its attributes and list its different\ + \ entities

    \n

    \n

    method __init__

    \n

    python\n\ + __init__(\n ad_account_id: 'str',\n conversion_tag_id: 'str',\n client:\ + \ 'PinterestSDKClient' = None,\n **kwargs\n) \u2192 None

    \n

    Initialize\ + \ Conversion Tag Object.

    \n

    Args:

    \n
      \n
    • ad_account_id\ + \ (str): ConversionTag's Ad Account ID
    • \n
    • conversion_tag_id\ + \ (str): ConversionTag ID, must be associated with Ad Account ID provided
    • \n\ +
    • client (PinterestSDKClient, optional): PinterestSDKClient\ + \ Object. Uses the default client, if not provided.
    • \n
    \n
    \n

    property\ + \ ad_account_id

    \n
    \n

    property code_snippet

    \n
    \n\ +

    property configs

    \n
    \n

    property enhanced_match_status

    \n\ +
    \n

    property id

    \n
    \n

    property last_fired_time_ms

    \n\ +
    \n

    property name

    \n
    \n

    property status

    \n\ +
    \n

    property version

    \n
    \n

    \n

    classmethod create

    \n

    python\n\ + create(\n ad_account_id: 'str',\n name: 'str',\n aem_enabled: 'bool'\ + \ = None,\n md_frequency: 'float' = None,\n aem_fnln_enabled: 'bool' = None,\n\ + \ aem_ph_enabled: 'bool' = None,\n aem_ge_enabled: 'bool' = None,\n aem_db_enabled:\ + \ 'bool' = None,\n aem_loc_enabled: 'bool' = None,\n client: 'PinterestSDKClient'\ + \ = None,\n **kwargs\n) \u2192 ConversionTag

    \n

    Create a conversion\ + \ tag, also known as Pinterest tag with the option to enable enhance match.

    \ + \

    \n

    The Pinterest Tag tracks actions people take on the ad account\u2019\ + \ s website after they view the ad account's ad on Pinterest. The advertiser\ + \ needs to customize this tag to track conversions.

    \n

    For more\ + \ information, see:

    \n

    Set up the Pinterest tag

    \n

    Pinterest Tag

    \n

    Enhanced\ + \ match\"

    \n

    Args:

    \n
      \n
    • ad_account_id\ + \ (str): ConversionTag's Ad Account ID
    • \n
    • name (str):\ + \ ConversionTag name
    • \n
    • \n

      aem_enabled (bool=False,\ + \ Nullable): Whether Automatic Enhanced Match email is enabled. See \ + \ Enhanced match for more information.

      \n
    • \n
    • \n

      md_frequency\ + \ (float=1.0, Nullable): Metadata ingestion frequency.

      \n
    • \n
    • \n

      aem_fnln_enabled\ + \ (bool=False, Nullable): Whether Automatic Enhanced Match name is enabled. See\ + \ Enhanced match for more information.

      \n
    • \n
    • \n

      aem_ph_enabled\ + \ (bool=False, Nullable): Whether Automatic Enhanced Match phone is enabled.\ + \ See Enhanced match for more information.

      \n
    • \n
    • \n

      aem_ge_enabled\ + \ (bool=False, Nullable): Whether Automatic Enhanced Match gender is enabled.\ + \ See Enhanced match for more information.

      \n
    • \n
    • \n

      aem_db_enabled\ + \ (bool=False, Nullable): Whether Automatic Enhanced Match birthdate is enabled.\ + \ See Enhanced match for more information.

      \n
    • \n
    • \n

      aem_loc_enabled\ + \ (bool=False, Nullable): Whether Automatic Enhanced Match location is enabled.\ + \ See Enhanced match for more information.

      \n
    • \n
    \n

    Returns:

    \n\ +
      \n
    • ConversionTag: ConversionTag Object
    • \n
    \n\ +
    \n

    \n

    classmethod get_all

    \n

    python\n\ + get_all(\n ad_account_id: 'str',\n filter_deleted: 'bool' = False,\n \ + \ client: 'PinterestSDKClient' = None,\n **kwargs\n) \u2192 list[ConversionTag]

    \n\ +

    Get a list of ConversionTag, filter by specified arguments

    \n

    Args:

    \n\ +
      \n
    • ad_account_id (str): Unique identifier of an ad\ + \ account.
    • \n
    • filter_deleted (bool=False, optional):\ + \ Filter out deleted tags.
    • \n
    • client (type,\ + \ optional): PinterestSDKClient Object. Uses the default client, if not provided.\ + \
    • \n
    \n

    Returns:

    \n
      \n
    • list[ConversionTag]:\ + \ List of ConversionTags
    • \n
    \n
    \n

    \n

    classmethod get_ocpm_eligible_conversion_tag_events

    \n\ +

    python\nget_ocpm_eligible_conversion_tag_events(\n ad_account_id:\ + \ 'str',\n client: 'PinterestSDKClient' = None,\n **kwargs\n) \u2192 tuple[str,\ + \ list[ConversionEventResponse]]

    \n

    Get OCPM eligible conversion tag\ + \ events for an Ad Account.

    \n

    Args:

    \n
      \n
    • ad_account_id\ + \ (str): Ad Account ID
    • \n
    • client (PinterestSDKClient,\ + \ optional): PinterestSDKClient Object. Uses the default client, if not provided.\ + \
    • \n
    \n

    Returns:

    \n
      \n
    • list[ConversionEventResponse]:\ + \ List of ConversionTagEvent
    • \n
    \n
    \n

    \n

    classmethod get_page_visit_conversion_tag_events

    \n\ +

    python\nget_page_visit_conversion_tag_events(\n ad_account_id: 'str',\n\ + \ page_size: 'int' = None,\n order: 'str' = 'ASCENDING',\n bookmark:\ + \ 'str' = None,\n client: 'PinterestSDKClient' = None,\n **kwargs\n) \u2192\ + \ tuple[list[ConversionEventResponse], Bookmark]

    \n

    Get page visit\ + \ conversion tag events for an ad account.

    \n

    Args:

    \n\ +
      \n
    • ad_account (str): Ad Account ID
    • \n
    • client\ + \ (PinterestSDKClient, optional): PinterestSDKClient Object. Uses the default\ + \ client, if not provided.
    • \n
    \n

    Returns:

    \n
      \n\ +
    • list[ConversionEventResponse]: List of ConversionTagEvent\ + \
    • \n
    " + name: conversion_tags + x-traitTag: false - description: "\n\n

    \n

    module ads.customer_lists

    \n

    High\ @@ -1050,19 +1201,19 @@ tags: >

    \n

    method list_pins

    \n

    python\n\ list_pins(\n section_id: 'str' = None,\n page_size: 'int' = None,\n bookmark:\ - \ 'str' = None,\n **kwargs\n) \u2192 tuple[list[Pin], Bookmark]

    \n\ -

    Get a list of the Pins on a board owned by the \"operation user_account\" -\ - \ or on a group board that has been shared with this account.

    \n
      \n
    • By\ - \ default, the \"operation user_account\" is the token user_account.
    • \n
    \n\ -

    Args:

    \n
      \n
    • section_id (str,\ - \ optional): Unique identifier of a board section. If not passed in, all pins\ - \ under the board will be listed.
    • \n
    • 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 which\ - \ will return default page size campaigns.
    • \n
    • bookmark\ - \ (str, optional): Cursor used to fetch the next page of items. Defaults to None.\ - \
    • \n
    \n

    Keyword Args: Any valid keyword arguments or query parameters\ - \ for endpoint.

    \n

    Returns:

    \n
      \n
    • list[Pin]:\ + \ 'str' = None\n) \u2192 tuple[list[Pin], Bookmark]

      \n

      Get a list\ + \ of the Pins on a board owned by the \"operation user_account\" - or on a group\ + \ board that has been shared with this account.

      \n
        \n
      • By default, the\ + \ \"operation user_account\" is the token user_account.
      • \n
      \n

      Args:

      \n\ +
        \n
      • section_id (str, optional): Unique identifier of\ + \ a board section. If not passed in, all pins under the board will be listed.\ + \
      • \n
      • 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 which will return default\ + \ page size campaigns.
      • \n
      • bookmark (str, optional):\ + \ Cursor used to fetch the next page of items. Defaults to None.
      • \n
      \n\ +

      Keyword Args: Any valid keyword arguments or query parameters for endpoint.\ + \

      \n

      Returns:

      \n
        \n
      • list[Pin]:\ \ List of Pin Objects
      • \n
      • Bookmark: Bookmark for\ \ pagination if present, else None.
      • \n
      \n
      \n

      \n\n

      \n

      module organic.pins

      \n

      Pin Class\ - \ for Pinterest Python SDK

      \n
      \n

      \n


      \n

      \n

      class Pin

      \n

      Pin model used to view,\ \ create, update its attributes and list its different entities.

      \n

      \n

      method __init__

      \n

      python\n\ __init__(\n pin_id: 'str',\n ad_account_id: 'str' = None,\n client: 'PinterestSDKClient'\ @@ -1144,7 +1295,7 @@ tags: \ dominant_color\n


      \n

      property id

      \n
      \n

      property\ \ link

      \n
      \n

      property media

      \n
      \n

      property\ \ media_source

      \n
      \n

      property parent_pin_id

      \n
      \n\ -

      property title

      \n
      \n

      property title\n


      \n

      \n

      classmethod create

      \n

      python\n\ create(\n board_id: 'str',\n media_source: 'dict',\n link: 'str' = None,\n\ @@ -1185,7 +1336,7 @@ tags: \ Object, uses the default client, if not provided.

    • \n
    \n

    Keyword Args:\ \ Any valid keyword arguments or query parameters for endpoint.

    \n

    Returns:

    \n\
      \n
    • Pin: Pin object
    • \n
    \n
    \n

    \n

    classmethod delete

    \n

    python\n\ delete(pin_id: 'str', client: 'PinterestSDKClient' = None) \u2192 bool

    \n\ @@ -1195,7 +1346,7 @@ tags:
  • client (PinterestSDKClient, optional): PinterestSDKClient\ \ Object. Uses the default client, if not provided.
  • \n\n

    Returns:

    \n\
      \n
    • bool: If the pin was deleted successfully.
    • \n\ -
    \n
    \n

    \n


    \n

    \n

    method save

    \n

    python\nsave(board_id:\ \ 'str', board_section_id: 'str' = None) \u2192 None

    \n

    Save a pin\ @@ -1211,11 +1362,11 @@ tags: - description: "\n\n

    \n

    module utils.base_model

    \n

    Pinterest\ - \ Base Model

    \n
    \n

    \n


    \n

    \n

    class PinterestBaseModel

    \n

    Base\ \ Model for all other Higher Level Models in the Python Client

    \n

    \n

    method __init__

    \n

    python\n\ __init__(\n _id: str,\n generated_api: object,\n generated_api_get_fn:\ @@ -1241,7 +1392,7 @@ tags: \ (str): The model's function which returns a bookmark. \n

  • model_fn_args\ \ (dict): Arguments passed to the function.
  • \n
  • client\ \ (PinterestSDKClient): Client used to make the SDK call.
  • \n\n
    \n\ -

    \n

    method get_bookmark_token

    \n

    python\n\ get_bookmark_token() \u2192 str

    \n

    Returns the bookmark pagination\ @@ -1292,7 +1443,21 @@ tags: load_json_config()

    Parse a config.json file and then load all the variables found as environment - variables.

    ' + variables.

    + +
    + +

    + +

    function load_json_config_from_single_env_var

    + +

    python + + load_json_config_from_single_env_var()

    + +

    Parse PINTEREST_JSON_ENV_VARIABLES environment variable to split long JSON + string into individual environment variables.

    ' name: load_json_config x-traitTag: false - description: "\n\n

    python - __init__(status=None, reason=None, http_resp=None)

    ' + __init__(status=None, reason=None, http_resp=None, body=None)

    ' name: sdk_exceptions x-traitTag: false x-tagGroups: @@ -1349,6 +1514,8 @@ x-tagGroups: - ads - audiences - campaigns + - conversion_events + - conversion_tags - customer_lists - keywords - name: pinterest.client diff --git a/docs/utils/script.py b/docs/utils/script.py index 04f1537..a34a63c 100644 --- a/docs/utils/script.py +++ b/docs/utils/script.py @@ -44,6 +44,7 @@ def remove_old_doc(): files = next(os.walk("docs/pinterest"), (None, None, []))[2] for file in files: os.remove(f"docs/pinterest/{file}") + def generate_new_doc(): """ Use lazydoc to generate new doc at: docs/pinterest @@ -113,6 +114,7 @@ def check_index(num_file: int, index: dict): if num_file != num_file_indexed: raise Exception("Cound't index all file, please double check") + def create_file_index() -> dict: """ Create file index: index[module_name] = list[file_name] @@ -160,6 +162,7 @@ def create_file_index() -> dict: return return_index + def truncate_md_extension(file: str) -> str: """Truncate .md file extension @@ -171,6 +174,7 @@ def truncate_md_extension(file: str) -> str: """ return file[:-3] + def append_doc_to_spec_file(index: dict): """ Accord to index file, append docs to spec skeleton spec and overwrite it to python-sdk-doc.yaml @@ -184,6 +188,10 @@ def append_doc_to_spec_file(index: dict): spec_path = PROJECT_PATH + '/docs/utils/skeleton-spec.yaml' spec = yaml.load(open(spec_path, 'r'), Loader=yaml.FullLoader) + # Update version + from pinterest.version import __version__ + spec['info']['version'] = __version__ + # Appending md doc into skeleton spec spec['tags'] = [] spec['x-tagGroups'] = [] diff --git a/integration_tests/ads/test_ad_groups.py b/integration_tests/ads/test_ad_groups.py index ebbcd24..d147e26 100644 --- a/integration_tests/ads/test_ad_groups.py +++ b/integration_tests/ads/test_ad_groups.py @@ -23,6 +23,8 @@ def test_create_ad_group_success(self): campaign_id=getattr(self.ad_group_utils.campaign, "_id"), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) assert ad_group @@ -62,7 +64,7 @@ def test_update_success(self): new_name = "SDK_AD_GROUP_NEW_NAME" new_spec = { - "GENDER": ["MALE"] + "GENDER": ["male"] } ad_group.update_fields( @@ -136,6 +138,8 @@ def test_get_list_with_campaign_ids_success(self): campaign_id=getattr(new_campaign, "_id"), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) new_ad_groups = AdGroup.get_all( diff --git a/integration_tests/ads/test_campaigns.py b/integration_tests/ads/test_campaigns.py index 9d5c287..f56cb01 100644 --- a/integration_tests/ads/test_campaigns.py +++ b/integration_tests/ads/test_campaigns.py @@ -30,7 +30,7 @@ def test_create_campaign_success(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) assert campaign @@ -52,7 +52,7 @@ def test_create_campaign_failure_without_budget(self): self.assertRaisesRegex( SdkException, - r"^[\S\s]*campaigns should have a campaign level budget[\S\s]*$", + r"^[\S\s]*code 2384[\S\s]*$", Campaign.create, **campaign_arguments) @@ -65,7 +65,7 @@ def test_create_campaign_failure_incorrect_objective_type(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="INCORRECT_OBJECTIVE_TYPE", - daily_spend_cap=10, + daily_spend_cap=10000000, ) with self.assertRaisesRegex(ApiValueError, "Invalid"): Campaign.create(**campaign_arguments) @@ -184,6 +184,8 @@ def test_list_ad_groups(self): campaign_id=getattr(campaign, '_id'), billable_event="IMPRESSION", name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) ) diff --git a/integration_tests/ads/test_conversion_events.py b/integration_tests/ads/test_conversion_events.py index 8179592..b3b9fba 100644 --- a/integration_tests/ads/test_conversion_events.py +++ b/integration_tests/ads/test_conversion_events.py @@ -23,6 +23,8 @@ def test_send_conversion_success(self): raw_user_data = dict( em = ["964bbaf162703657e787eb4455197c8b35c18940c75980b0285619fe9b8acec8"] #random hash256 ) + raw_custom_data = dict() + conversion_events = [ Conversion.create_conversion_event( event_name = "add_to_cart", @@ -30,6 +32,7 @@ def test_send_conversion_success(self): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data= raw_custom_data, ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] @@ -48,11 +51,9 @@ def test_send_conversion_success(self): assert response.events[0].status == "processed" assert response.events[0].error_message == "" - assert response.events[0].warning_message == "" assert response.events[1].status == "processed" assert response.events[1].error_message == "" - assert response.events[1].warning_message == "" def test_send_conversion_fail(self): """ @@ -64,6 +65,8 @@ def test_send_conversion_fail(self): raw_user_data = dict( em = ["test_non_hashed_email@pinterest.com"] ) + raw_custom_data = dict() + conversion_events = [ Conversion.create_conversion_event( event_name = "add_to_cart", @@ -71,6 +74,7 @@ def test_send_conversion_fail(self): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data = raw_custom_data, ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] @@ -84,8 +88,8 @@ def test_send_conversion_fail(self): assert response assert response.num_events_received == 2 - assert response.num_events_processed == 2 + assert response.num_events_processed == 0 assert len(response.events) == 2 - assert response.events[0].warning_message == "'em' is not in sha256 hashed format." - assert response.events[1].warning_message == "'em' is not in sha256 hashed format." + assert 'hashed format' in response.events[0].error_message + assert 'hashed format' in response.events[0].error_message diff --git a/integration_tests/ads/test_conversion_tags.py b/integration_tests/ads/test_conversion_tags.py index 6fb6f35..b21f540 100644 --- a/integration_tests/ads/test_conversion_tags.py +++ b/integration_tests/ads/test_conversion_tags.py @@ -18,12 +18,42 @@ def test_create_conversion_tag_success(self): """ conversion_tag = ConversionTag.create( ad_account_id = DEFAULT_AD_ACCOUNT_ID, - name = "Test Conversion Tag" + name = "Test Conversion Tag", + aem_enabled = None, + md_frequency = None, + aem_fnln_enabled = None, + aem_ph_enabled = None, + aem_ge_enabled = None, + aem_db_enabled = None, + aem_loc_enabled = None, ) assert conversion_tag assert getattr(conversion_tag, "_id") assert getattr(conversion_tag, "_name") == "Test Conversion Tag" + assert getattr(conversion_tag.configs, "aem_enabled") == False + + def test_create_conversion_tag_with_configs_success(self): + """ + Test creating a new Conversion Tag successfully + """ + conversion_tag = ConversionTag.create( + ad_account_id = DEFAULT_AD_ACCOUNT_ID, + name = "Test Conversion Tag", + aem_enabled = True, + md_frequency = 1.2, + aem_fnln_enabled = None, + aem_ph_enabled = None, + aem_ge_enabled = None, + aem_db_enabled = None, + aem_loc_enabled = None, + ) + + assert conversion_tag + assert getattr(conversion_tag, "_id") + assert getattr(conversion_tag, "_name") == "Test Conversion Tag" + assert getattr(conversion_tag.configs, "aem_enabled") == True + assert getattr(conversion_tag.configs, "md_frequency") == 1.2 class TestGetConversionTag(BaseTestCase): """ @@ -67,6 +97,10 @@ def test_get_list_success(self): conversion_tags = ConversionTag.get_all(ad_account_id = ad_account_id) assert len(conversion_tags) == NUMBER_OF_NEW_CONVERSION_TAG + for conversion_tag in conversion_tags: + assert conversion_tag.name == "SDK_TEST_CONVERSION_TAG" + assert conversion_tag.ad_account_id == ad_account_id + class TestGetPageVsitConversionTag(BaseTestCase): """ diff --git a/integration_tests/clean_organic_data.py b/integration_tests/clean_organic_data.py new file mode 100644 index 0000000..92339c0 --- /dev/null +++ b/integration_tests/clean_organic_data.py @@ -0,0 +1,18 @@ +""" +Delete all non-essential organic data from test user +""" + +from pinterest.organic.boards import Board +from integration_tests.config import DEFAULT_BOARD_ID + +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) + + assert len(Board.get_all()[0]) == 1 diff --git a/integration_tests/client/test_client.py b/integration_tests/client/test_client.py index 4a791ad..51c9582 100644 --- a/integration_tests/client/test_client.py +++ b/integration_tests/client/test_client.py @@ -12,6 +12,7 @@ from pinterest.organic.boards import Board from pinterest.client import PinterestSDKClient from integration_tests.base_test import BaseTestCase +from pinterest.utils.error_handling import SdkException class ClientTest(BaseTestCase): @@ -56,6 +57,18 @@ def test_good_setup_default_access_token(self): PinterestSDKClient.set_default_access_token(access_token=good_access_token) self.assertIsNotNone(Board.get_all()) + def test_bad_refresh_token(self): + refresh_token = 'refresh_token' + app_id = '12345' + app_secret = '123456asdfg' + with self.assertRaises(SdkException): + PinterestSDKClient._get_access_token( + refresh_token=refresh_token, + app_id=app_id, + app_secret=app_secret + ) + + diff --git a/integration_tests/organic/test_boards.py b/integration_tests/organic/test_boards.py index 6eda64c..98dcc6b 100644 --- a/integration_tests/organic/test_boards.py +++ b/integration_tests/organic/test_boards.py @@ -13,10 +13,12 @@ from pinterest.organic.boards import Board from pinterest.organic.boards import BoardSection from pinterest.organic.pins import Pin +from pinterest.utils.bookmark import Bookmark from integration_tests.base_test import BaseTestCase from integration_tests.config import DEFAULT_BOARD_ID, DEFAULT_BOARD_NAME + class TestGetBoard(BaseTestCase): """ Test intializing Board model @@ -32,8 +34,8 @@ def test_get_board_success(self): assert board assert board.id == DEFAULT_BOARD_ID - assert board.name == DEFAULT_BOARD_NAME - + self.assertIsNotNone(board.name) + self.assertIsNotNone(board.description) def test_get_board_failure_invalid_board_id(self): """ Test getting a Board failure due to invalid board id @@ -56,15 +58,16 @@ def test_create_and_delete_board_success(self): """ Test creating a new Board and deleting the Board successfully """ + random_board_name = self.board_utils.get_random_board_name() board = Board.create( - name="SDK Test Create Board", + name=random_board_name, description="SDK Test Board Description", privacy="PUBLIC", client=self.test_client ) assert board - assert board.name == "SDK Test Create Board" + assert board.name == random_board_name self.board_utils.delete_board(board_id=board.id) @@ -88,7 +91,8 @@ def test_update_board_fields_success(self): board = self.board_utils.create_new_board( description=old_description, - privacy=old_privacy + privacy=old_privacy, + name=self.board_utils.get_random_board_name() ) assert board.description == old_description @@ -107,7 +111,7 @@ def test_update_board_fields_failure_invalid_field_value(self): """ Test updating the name of the board with invalid/existing board name value. """ - board = self.board_utils.create_new_board() + board = self.board_utils.create_new_board(name=self.board_utils.get_random_board_name()) assert board @@ -135,7 +139,7 @@ def test_change_board_privacy_success(self, old_privacy, new_privacy, make_priva and Test making a public board secret successfully """ - board = self.board_utils.create_new_board(privacy=old_privacy) + board = self.board_utils.create_new_board(privacy=old_privacy, name=self.board_utils.get_random_board_name()) assert board assert board.privacy == old_privacy @@ -154,7 +158,8 @@ def test_create_new_board_section_success(self): """ Test creating a new board section under a board model successfully. """ - board = self.board_utils.create_new_board(name="Create Board Section Test") + random_board_name = self.board_utils.get_random_board_name() + board = self.board_utils.create_new_board(name=random_board_name) assert board @@ -282,49 +287,49 @@ def test_list_pins_on_board(self): """ Test if all pins on a board are returned """ - new_board = self.board_utils.create_new_board(name="GET ALL BOARD PINS TEST BOARD") + new_board = self.board_utils.create_new_board(name=self.board_utils.get_random_board_name()) NUMBER_OF_PINS_TO_CREATE = 3 PIN_CREATION_MEDIA_SOURCES = [ { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.pinimg.com/564x/28/75/e9/2875e94f8055227e72d514b837adb271.jpg' + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABNUlEQVR4nOzRQQkAIADAQBH7fwXrWcQY93CXYLB17h5xpg74XQOwBmANwBqANQBrANYArAFYA7AGYA3AGoA1AGsA1gCsAVgDsAZgDcAagDUAawDWAKwBWAOwBmANwBqANQBrANYArAFYA7AGYA3AGoA1AGsA1gCsAVgDsAZgDcAagDUAawDWAKwBWAOwBmANwBqANQBrANYArAFYA7AGYA3AGoA1AGsA1gCsAVgDsAZgDcAagDUAawDWAKwBWAOwBmANwBqANQBrANYArAFYA7AGYA3AGoA1AGsA1gCsAVgDsAZgDcAagDUAawDWAKwBWAOwBmANwBqANQBrANYArAFYA7AGYA3AGoA1AGsA1gCsAVgDsAZgDcAagDUAawDWAKwBWAOwBmANwBqANQBrAPYCAAD//2UYAxd4/Gr3AAAAAElFTkSuQmCC", }, { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.picsum.photos/id/13/2500/1667.jpg?hmac=SoX9UoHhN8HyklRA4A3vcCWJMVtiBXUg0W4ljWTor7s' + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABVElEQVR4nOzTQQ3CQABEUdgQziQoQgYeuGCiYiqnOiqiIvbw0vQ/BZP8zGN5/m9ntv7eesKUoQdcXQGwAmAFwAqAFQArAFYArABYAbACYAXACoAVACsAVgCsAFgBsAJgBcAKgBUAKwBWAKwAWAGwAmAFwAqAFQArAFYArABYAbACYAXACoAVACsAVgCsAFgBsAJgBcAKgBUAKwBWAKwAWAGwAmAFwAqAFQArAFYArABYAbACYAXACoAVACsAdt/HR2+Y8n1tesKUHoAVACsAVgCsAFgBsAJgBcAKgBUAKwBWAKwAWAGwAmAFwAqAFQArAFYArABYAbACYAXACoAVACsAVgCsAFgBsAJgBcAKgBUAKwBWAKwAWAGwAmAFwAqAFQArAFYArABYAbACYAXACoAVACsAVgCsAFgBsAJgBcAKgBUAKwBWAKwAWAGwAmBHAAAA//8nhwWSfghzXAAAAABJRU5ErkJggg==", }, { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.picsum.photos/id/21/3008/2008.jpg?hmac=T8DSVNvP-QldCew7WD4jj_S3mWwxZPqdF0CNPksSko4' - } + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABpklEQVR4nOzbQSrtcRiH8XtvZwm3W3esFFZgZAfGRgbMbYAlUIyUPSg5SilCtiA7MFcytgSjt2fy+Szg+zv1nHf4X6x+PvyatHa7N7r/trUc3f+7fTi6/2d0nR8JEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFii7vLp9EHPk6+RveXV7O///3gfHTfBcQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmACx30fr/2Zf2H0enf9//TK6/7oz+/2BC4gJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFii5XNjdEHjk/vR/cvzvZH928eZ/+jLiAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASIfQcAAP//pBwaLoqp/uUAAAAASUVORK5CYII=", + }, ] - - created_pin_ids = set( - getattr(self.pin_utils.create_new_pin( + created_pin_ids = set() + for num in range(NUMBER_OF_PINS_TO_CREATE): + response = self.pin_utils.create_new_pin( board_id=new_board.id, title=f"GET ALL PINS TEST #{num}", media_source=PIN_CREATION_MEDIA_SOURCES[num] - ), - 'id' ) - for num in range(NUMBER_OF_PINS_TO_CREATE) - ) + created_pin_ids.add(getattr(response, 'id')) assert len(created_pin_ids) == NUMBER_OF_PINS_TO_CREATE - pins_list, _ = new_board.list_pins() + pins_list_1, bookmark_1 = new_board.list_pins(page_size=NUMBER_OF_PINS_TO_CREATE//2+1) + assert isinstance(bookmark_1, Bookmark) + + pins_list_2, _ = bookmark_1.get_next() + + pins_list = pins_list_1 + pins_list_2 # delete organic data from prod for pin in pins_list: self.pin_utils.delete_pin(pin.id) + self.board_utils.delete_board(new_board.id) assert len(created_pin_ids) == len(pins_list) @@ -340,29 +345,27 @@ def test_list_pins_on_board_section(self): """ Test if all pins on a board section are returned """ - new_board = self.board_utils.create_new_board(name="GET ALL BOARD SECTION PINS TEST BOARD") + random_board_name = self.board_utils.get_random_board_name() + new_board = self.board_utils.create_new_board(name=random_board_name) new_section = new_board.create_section(name="GET ALL PINS FROM BOARD SECTION Test") NUMBER_OF_PINS_TO_CREATE = 3 PIN_CREATION_MEDIA_SOURCES = [ { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.pinimg.com/564x/28/75/e9/2875e94f8055227e72d514b837adb271.jpg' + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABqUlEQVR4nOzToUodABSH8d3tro6FPcTWlsfC2oXVpTUxit1kk1vEIJh8CDGaDbeaxGASxCAWswg+gunwld/vAf4nfJzl3tebD5Ounv6P7n/+sxrdP99sRvc/jq7zLgFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQGxxe/lz9MDx2cHo/sP37dH9w/WX0X0fEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQWPz5djx74tXs0un+3+ja6f3r/OLrvA2ICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATIDYcv33dfTAv4vn0f393y+j+ztbJ6P7PiAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASIvQUAAP//k50aDw7OHnwAAAAASUVORK5CYII=", }, { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.picsum.photos/id/13/2500/1667.jpg?hmac=SoX9UoHhN8HyklRA4A3vcCWJMVtiBXUg0W4ljWTor7s' + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABq0lEQVR4nOzTL0oeABjH8W28abywlfUxxmAs7ASewWAQxQPYrJb3CCYxyJsEkwfwT7BZPYHRJmYFk0cwPXzL53OA3xO+PIuHn38+Tbp/+ze6f748Hd1/XP0d3f8yus6HBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAscW3/5ujB7ZOtkf3fxzvjO5ffF2P7vuAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATIPb58HZj9MD+3cHo/mr9Orp/9nQ0uu8DYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgNhib3k5euDl183o/u7vq9H95+vvo/s+ICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIi9BwAA//9BBBgTPEF1UQAAAABJRU5ErkJggg==", }, { - "source_type": "image_url", + "source_type": "image_base64", "content_type": "image/jpeg", - "data": "string", - 'url':'https://i.picsum.photos/id/21/3008/2008.jpg?hmac=T8DSVNvP-QldCew7WD4jj_S3mWwxZPqdF0CNPksSko4' - } + "data": "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAIAAABMXPacAAABq0lEQVR4nOzbvUndYRiH4RD+AySEhHwMcFJlghQiWAq2ioU4weGAvQO4goUgVlaOYGtlq9ZWLnA6R7B6uJvrGuD3FjdP+S5/Lp8/TTpfbUf3D672Rvdvfu2M7n8eXedDAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyA2HL35XT0gfX+yej+78P16P7j6t/ovguICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYsvZ3/+jDzy9bUb3jzcXo/s/vx6N7ruAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATILbs3t6PPvBwvR3d//5t9n/Dy+uP0X0XEBMgJkBMgJgAMQFiAsQEiAkQEyAmQEyAmAAxAWICxASICRATICZATICYADEBYgLEBIgJEBMgJkBMgJgAMQFiAsTeAwAA//8vexX9K+3GIgAAAABJRU5ErkJggg==", + }, ] created_pin_ids = set( diff --git a/integration_tests/organic/test_pins.py b/integration_tests/organic/test_pins.py index 52719c1..7ce8379 100644 --- a/integration_tests/organic/test_pins.py +++ b/integration_tests/organic/test_pins.py @@ -12,6 +12,7 @@ from integration_tests.base_test import BaseTestCase from integration_tests.config import DEFAULT_PIN_ID from integration_tests.config import DEFAULT_BOARD_ID +from integration_tests.config import DEFAULT_AD_ACCOUNT_ID from integration_tests.config import DEFAULT_BOARD_SECTION_ID class TestGetPin(BaseTestCase): @@ -23,12 +24,12 @@ class TestGetPin(BaseTestCase): ({ 'description': 'PIN is PUBLIC', 'pin_id': DEFAULT_PIN_ID, - 'ad_account_id': None, + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, },), ({ 'description': 'PIN in PROTECTED BOARD', 'pin_id': DEFAULT_PIN_ID, - 'ad_account_id': None, + 'ad_account_id': DEFAULT_AD_ACCOUNT_ID, },) ] ) @@ -62,6 +63,7 @@ def test_create_and_delete_pin_success(self): "data": "string", 'url':'https://i.pinimg.com/564x/28/75/e9/2875e94f8055227e72d514b837adb271.jpg' }, + ad_account_id = DEFAULT_AD_ACCOUNT_ID, client=self.test_client ) diff --git a/integration_tests/utils/ads_utils.py b/integration_tests/utils/ads_utils.py index bc766e2..5aaa22d 100644 --- a/integration_tests/utils/ads_utils.py +++ b/integration_tests/utils/ads_utils.py @@ -108,7 +108,7 @@ def __init__(self, client=None): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) self.campaign_id = self.campaign._id @@ -124,7 +124,7 @@ def get_default_params(self): ad_account_id=DEFAULT_AD_ACCOUNT_ID, name="SDK Test Campaign", objective_type="AWARENESS", - daily_spend_cap=10, + daily_spend_cap=10000000, ) def create_new_campaign(self, **kwargs): @@ -142,6 +142,8 @@ def __init__(self, client=None): billable_event="IMPRESSION", client=self.test_client, name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) self.ad_group_id = self.ad_group._id @@ -158,6 +160,8 @@ def get_default_params(self): billable_event="IMPRESSION", client=self.test_client, name="SDK_INTEGRATION_TEST_ADGROUP", + auto_targeting_enabled=False, + bid_in_micro_currency=10000000, ) def create_new_ad_group(self, **kwargs): diff --git a/integration_tests/utils/organic_utils.py b/integration_tests/utils/organic_utils.py index 0f8531d..b1ef35b 100644 --- a/integration_tests/utils/organic_utils.py +++ b/integration_tests/utils/organic_utils.py @@ -1,8 +1,7 @@ """ Provide helper and utility functions for Organic Endpoints Integration Testing """ -from openapi_generated.pinterest_client.api.boards_api import BoardsApi -from openapi_generated.pinterest_client.model.board import Board as GeneratedBoard +import random from pinterest.client import PinterestSDKClient @@ -27,6 +26,9 @@ def __init__(self, client=None): self.test_client = client or PinterestSDKClient.create_default_client() self.board = Board(board_id=DEFAULT_BOARD_ID, client=client) + def get_random_board_name(self): + return "SDK Test Create Board {}".format(random.randint(0, 1000)) + def get_board(self): return self.board diff --git a/pinterest/ads/ad_accounts.py b/pinterest/ads/ad_accounts.py index cd8b8ad..4c18707 100644 --- a/pinterest/ads/ad_accounts.py +++ b/pinterest/ads/ad_accounts.py @@ -44,6 +44,8 @@ def __init__( self._country = None self._currency = None self._permissions = None + self._created_time = None + self._updated_time = None PinterestBaseModel.__init__( self, @@ -86,6 +88,16 @@ def permissions(self) -> list[str]: # pylint: disable=missing-function-docstring return self._permissions + @property + def created_time(self) -> int: + # pylint: disable=missing-function-docstring + return self._created_time + + @property + def updated_time(self) -> int: + # pylint: disable=missing-function-docstring + return self._updated_time + @classmethod def create( cls, @@ -128,6 +140,7 @@ def create( }, api=AdAccountsApi, create_fn=AdAccountsApi.ad_accounts_create, + client=cls._get_client(client), ) return cls(ad_account_id=response.id, client=cls._get_client(client)) diff --git a/pinterest/ads/ad_groups.py b/pinterest/ads/ad_groups.py index a6150e5..1df2599 100644 --- a/pinterest/ads/ad_groups.py +++ b/pinterest/ads/ad_groups.py @@ -63,6 +63,7 @@ def __init__( self._summary_status = None self._feed_profile_id = None self._dca_assets = None + self._optimization_goal_metadata = None PinterestBaseModel.__init__( self, @@ -201,6 +202,11 @@ def dca_assets(self): #pylint: disable=missing-function-docstring return self._dca_assets + @property + def optimization_goal_metadata(self): + #pylint: disable=missing-function-docstring + return self._optimization_goal_metadata + @classmethod def create( @@ -325,7 +331,8 @@ def create( }, api=AdGroupsApi, create_fn=AdGroupsApi.ad_groups_create, - map_fn=lambda obj: obj.items[0].data + map_fn=lambda obj: obj.items[0].data, + client=cls._get_client(client), ) return cls( ad_account_id=response.ad_account_id, diff --git a/pinterest/ads/ads.py b/pinterest/ads/ads.py index 1a9ef7a..1d98774 100644 --- a/pinterest/ads/ads.py +++ b/pinterest/ads/ads.py @@ -70,6 +70,7 @@ def __init__( generated_api_get_fn="ads_get", generated_api_get_fn_args={"ad_account_id": ad_account_id, "ad_id": ad_id}, model_attribute_types = AdResponse.openapi_types, + client=client, ) self._ad_account_id = str(ad_account_id) self._populate_fields(**kwargs) @@ -304,7 +305,8 @@ def create(cls, }, api=AdsApi, create_fn=AdsApi.ads_create, - map_fn=lambda obj: obj.items[0].data + map_fn=lambda obj: obj.items[0].data, + client=cls._get_client(client), ) return cls( ad_account_id=response.ad_account_id, diff --git a/pinterest/ads/audiences.py b/pinterest/ads/audiences.py index 17e4184..acde78b 100644 --- a/pinterest/ads/audiences.py +++ b/pinterest/ads/audiences.py @@ -178,7 +178,8 @@ def create( ), }, api=AudiencesApi, - create_fn=AudiencesApi.audiences_create + create_fn=AudiencesApi.audiences_create, + client=cls._get_client(client), ) return cls( diff --git a/pinterest/ads/campaigns.py b/pinterest/ads/campaigns.py index d18bb6e..a67c25f 100644 --- a/pinterest/ads/campaigns.py +++ b/pinterest/ads/campaigns.py @@ -55,6 +55,7 @@ def __init__( self._type = None self._is_flexible_daily_budgets = None self._is_campaign_budget_optimization = None + self._summary_status = None PinterestBaseModel.__init__( self, @@ -148,6 +149,11 @@ def is_campaign_budget_optimization(self) -> bool: # pylint: disable=missing-function-docstring return self._is_campaign_budget_optimization + @property + def summary_status(self) -> str: + # pylint: disable=missing-function-docstring + return self._summary_status + @classmethod def create( @@ -283,7 +289,8 @@ def create( }, api=CampaignsApi, create_fn=CampaignsApi.campaigns_create, - map_fn=lambda obj: obj.items[0].data + map_fn=lambda obj: obj.items[0].data, + client=cls._get_client(client), ) return cls( diff --git a/pinterest/ads/conversion_events.py b/pinterest/ads/conversion_events.py index 90b0a39..f4904d2 100644 --- a/pinterest/ads/conversion_events.py +++ b/pinterest/ads/conversion_events.py @@ -3,11 +3,12 @@ """ from __future__ import annotations -from openapi_generated.pinterest_client.api.conversion_events_api import ConversionEventsApi from openapi_generated.pinterest_client.model.conversion_events import ConversionEvents +from openapi_generated.pinterest_client.api.conversion_events_api import ConversionEventsApi from openapi_generated.pinterest_client.model.conversion_events_data import ConversionEventsData -from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents from openapi_generated.pinterest_client.model.conversion_events_user_data import ConversionEventsUserData +from openapi_generated.pinterest_client.model.conversion_events_custom_data import ConversionEventsCustomData +from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents from pinterest.client import PinterestSDKClient from pinterest.utils.base_model import PinterestBaseModel @@ -26,6 +27,7 @@ def create_conversion_event( event_time : int, event_id : str, user_data : dict, + custom_data : dict, event_source_url : str = None, partner_name : str = None, app_id : str = None, @@ -39,7 +41,7 @@ def create_conversion_event( language : str = None, **kwargs ) -> ConversionEventsData: - """ Create Conversion Event Data to be sent + """ Create Conversion Event Data to be sent. Args: event_name (str): The type of the user event, Enum: "add_to_cart", "checkout", "custom", @@ -51,6 +53,7 @@ def create_conversion_event( between events ingested via both the conversion API and Pinterest tracking user_data (dict): Object containing customer information data. Note, it is required at least one of 1) em, 2) hashed_maids or 3) pair client_ip_address + client_user_agent. + custom_data (dict): Object containing other custom data. event_source_url (str, optional): URL of the web conversion event partner_name (str, optional): The third party partner name responsible to send the event to Conversion API on behalf of the adverstiser. Only send this field if Pinterest has worked @@ -74,6 +77,7 @@ def create_conversion_event( event_time = event_time, event_id = event_id, user_data = ConversionEventsUserData(**user_data), + custom_data = ConversionEventsCustomData(**custom_data), event_source_url = event_source_url, partner_name = partner_name, app_id = app_id, @@ -98,12 +102,11 @@ def send_conversion_events( **kwargs, )-> tuple(int, int, list[ConversionApiResponseEvents]): """ - Send conversion events to Pinterest API for Conversions + Send conversion events to Pinterest API for Conversions. - Note: Highly recommend to use create_client_with_token (with Conversion Access Token) to create new client - for this functionality. + Note: Highly recommend to use create_client_with_token (with Conversion Access Token) to create different + client for this functionality. """ - response = ConversionEventsApi(api_client=cls._get_client(client)).events_create( ad_account_id = str(ad_account_id), conversion_events = ConversionEvents( diff --git a/pinterest/ads/conversion_tags.py b/pinterest/ads/conversion_tags.py index f30b17e..0ed4151 100644 --- a/pinterest/ads/conversion_tags.py +++ b/pinterest/ads/conversion_tags.py @@ -30,7 +30,7 @@ def __init__( **kwargs ) -> None: """ - Initialize Conversion Tag Object + Initialize Conversion Tag Object. Args: ad_account_id (str): ConversionTag's Ad Account ID @@ -109,13 +109,13 @@ def create( cls, ad_account_id : str, name : str, - aem_enabled : bool = False, - md_frequency : float = 0.0, - aem_fnln_enabled : bool = False, - aem_ph_enabled : bool = False, - aem_ge_enabled : bool = False, - aem_db_enabled : bool = False, - aem_loc_enabled : bool = False, + aem_enabled : bool = None, + md_frequency : float = None, + aem_fnln_enabled : bool = None, + aem_ph_enabled : bool = None, + aem_ge_enabled : bool = None, + aem_db_enabled : bool = None, + aem_loc_enabled : bool = None, client:PinterestSDKClient = None, **kwargs ) -> ConversionTag: @@ -147,6 +147,24 @@ def create( Args: ad_account_id (str): ConversionTag's Ad Account ID name (str): ConversionTag name + aem_enabled (bool=False, Nullable): Whether Automatic Enhanced Match email is enabled. See\ + Enhanced match for more information. + + md_frequency (float=1.0, Nullable): Metadata ingestion frequency. + aem_fnln_enabled (bool=False, Nullable): Whether Automatic Enhanced Match name is enabled. See\ + Enhanced match for more information. + + aem_ph_enabled (bool=False, Nullable): Whether Automatic Enhanced Match phone is enabled. See\ + Enhanced match for more information. + + aem_ge_enabled (bool=False, Nullable): Whether Automatic Enhanced Match gender is enabled. See\ + Enhanced match for more information. + + aem_db_enabled (bool=False, Nullable): Whether Automatic Enhanced Match birthdate is enabled. See\ + Enhanced match for more information. + + aem_loc_enabled (bool=False, Nullable): Whether Automatic Enhanced Match location is enabled. See\ + Enhanced match for more information. Returns: ConversionTag: ConversionTag Object @@ -169,8 +187,10 @@ def create( api = ConversionTagsApi, create_fn = ConversionTagsApi.conversion_tags_create, map_fn = lambda obj : obj, + client=cls._get_client(client), ) + return cls( ad_account_id = response.ad_account_id, conversion_tag_id = response.id, @@ -189,18 +209,18 @@ def get_all( Get a list of ConversionTag, filter by specified arguments Args: - ad_account_id (str): _description_ - filter_deleted (bool, optional): _description_. Defaults to False. - client (_type_, optional): _description_. Defaults to PinterestSDKClient=None. + ad_account_id (str): Unique identifier of an ad account. + filter_deleted (bool=False, optional): Filter out deleted tags. + client (_type_, optional): PinterestSDKClient Object. Uses the default client, if not provided. Returns: list[ConversionTag]: List of ConversionTags """ - params = {"ad_account_id" : ad_account_id, "filter_deleted": filter_deleted} + params = {"ad_account_id" : str(ad_account_id), "filter_deleted": filter_deleted} def _map_function(obj): return ConversionTag( - ad_account_id = ad_account_id, + ad_account_id = str(ad_account_id), conversion_tag_id = obj.get('id'), client = client, _model_data = obj.to_dict() @@ -226,7 +246,7 @@ def get_page_visit_conversion_tag_events( **kwargs ) -> tuple[list[ConversionEventResponse], Bookmark]: """ - Get page visit conversion tag events for an ad account + Get page visit conversion tag events for an ad account. Args: ad_account (str): Ad Account ID @@ -235,7 +255,7 @@ def get_page_visit_conversion_tag_events( Returns: list[ConversionEventResponse]: List of ConversionTagEvent """ - params = {"ad_account_id" : ad_account_id} + params = {"ad_account_id" : str(ad_account_id)} def _map_function(obj): return ConversionEventResponse( @@ -265,7 +285,7 @@ def get_ocpm_eligible_conversion_tag_events( **kwargs ) -> tuple[str, list[ConversionEventResponse]]: """ - Get OCPM eligible conversion tag events for an Ad Account + Get OCPM eligible conversion tag events for an Ad Account. Args: ad_account_id (str): Ad Account ID @@ -275,7 +295,7 @@ def get_ocpm_eligible_conversion_tag_events( list[ConversionEventResponse]: List of ConversionTagEvent """ api_response = ConversionTagsApi(api_client=cls._get_client(client)).ocpm_eligible_conversion_tags_get( - ad_account_id = ad_account_id, + ad_account_id = str(ad_account_id), **kwargs, ) diff --git a/pinterest/ads/customer_lists.py b/pinterest/ads/customer_lists.py index 31258a8..4e88d90 100644 --- a/pinterest/ads/customer_lists.py +++ b/pinterest/ads/customer_lists.py @@ -160,6 +160,7 @@ def create( }, api=CustomerListsApi, create_fn=CustomerListsApi.customer_lists_create, + client=cls._get_client(client), ) return cls( diff --git a/pinterest/ads/keywords.py b/pinterest/ads/keywords.py index 53ab365..a81f2e5 100644 --- a/pinterest/ads/keywords.py +++ b/pinterest/ads/keywords.py @@ -142,6 +142,7 @@ def map_fn(obj): api=KeywordsApi, create_fn=KeywordsApi.keywords_create, map_fn=map_fn, + client=cls._get_client(client), ) return cls( diff --git a/pinterest/config.py b/pinterest/config.py index 03c7d5b..04f1aec 100644 --- a/pinterest/config.py +++ b/pinterest/config.py @@ -4,11 +4,12 @@ import os as _os from dotenv import load_dotenv as _load_env_vars from pinterest.version import __version__ -from pinterest.utils.load_json_config import load_json_config as _load_json +from pinterest.utils.load_json_config import load_json_config as _load_json,\ + load_json_config_from_single_env_var as _load_json_single_variable _load_env_vars() _load_json() - +_load_json_single_variable() PINTEREST_DEBUG = _os.environ.get('PINTEREST_DEBUG', "False").lower() == "true" PINTEREST_PORT = _os.environ.get('PINTEREST_PORT', 0) diff --git a/pinterest/organic/boards.py b/pinterest/organic/boards.py index 083aed5..0b57eee 100644 --- a/pinterest/organic/boards.py +++ b/pinterest/organic/boards.py @@ -520,7 +520,6 @@ def list_pins( section_id:str = None, page_size:int = None, bookmark:str = None, - **kwargs ) -> tuple[list[Pin], Bookmark]: """ Get a list of the Pins on a board owned by the "operation user_account" - or on a group board that has been @@ -562,6 +561,7 @@ def _map_function(obj): api=BoardsApi, list_fn=BoardsApi.boards_list_pins if not section_id else BoardsApi.board_sections_list_pins, map_fn=_map_function, + bookmark_model_cls=self, + bookmark_model_fn=self.list_pins, client=self._client, - **kwargs ) diff --git a/pinterest/organic/pins.py b/pinterest/organic/pins.py index e1693c4..5e7829c 100644 --- a/pinterest/organic/pins.py +++ b/pinterest/organic/pins.py @@ -5,6 +5,7 @@ from openapi_generated.pinterest_client.api.pins_api import PinsApi from openapi_generated.pinterest_client.model.pin import Pin as GeneratedPin +from openapi_generated.pinterest_client.model.pin_create import PinCreate as GeneratedPinCreate from openapi_generated.pinterest_client.model.inline_object import InlineObject from pinterest.client import PinterestSDKClient @@ -140,19 +141,18 @@ def __repr__(self): @classmethod def create( cls, - board_id:str, - media_source:dict, - link:str = None, - title:str = None, - description:str = None, - dominant_color:str = None, - alt_text:str = None, - board_section_id:str = None, - parent_pin_id:str = None, - client:PinterestSDKClient = None, + board_id : str, + media_source : dict, + link : str = None, + title : str = None, + description : str = None, + dominant_color : str = None, + alt_text : str = None, + board_section_id : str = None, + parent_pin_id : str = None, + client : PinterestSDKClient = None, **kwargs ) -> Pin: - # pylint: disable=too-many-arguments """ Create a Pin on a board or board section owned by the "operation user_account". @@ -197,12 +197,13 @@ def create( Returns: Pin: Pin object """ + # pylint: disable=too-many-arguments, no-value-for-parameter if not client: client = cls._get_client() api_response = PinsApi(client).pins_create( - pin=GeneratedPin( + pin_create=GeneratedPinCreate( link=link, title=title, description=description, @@ -212,9 +213,9 @@ def create( board_section_id=board_section_id, media_source=media_source, parent_pin_id=parent_pin_id, - **kwargs - ) - ) + ), + **kwargs, + ) # pylint: disable=no-value-for-parameter verify_api_response(api_response) return Pin(pin_id=getattr(api_response, "id"), client=client) diff --git a/pinterest/utils/base_model.py b/pinterest/utils/base_model.py index 0f85ef0..31a6889 100644 --- a/pinterest/utils/base_model.py +++ b/pinterest/utils/base_model.py @@ -117,6 +117,8 @@ def _list( api: type = None, list_fn: Callable = None, map_fn: Callable = None, + bookmark_model_cls: object = None, + bookmark_model_fn: Callable = None, client: PinterestSDKClient = None, **kwargs ): @@ -132,36 +134,30 @@ def _list( items = [] bookmark = None - # Python do while, always execute at least 1 - while True: - http_response = cls._call_method( - cls._get_api_instance(api, client), - list_fn.__name__, - params, - **kwargs - ) + http_response = cls._call_method( + cls._get_api_instance(api, client), + list_fn.__name__, + params, + **kwargs + ) - verify_api_response(http_response) - - items = http_response.get('items', []) - bookmark = http_response.get('bookmark', None) - # Only execute 1 if page size is set - if page_size is not None: - break - # Set the new bookmark - if bookmark is not None: - kwargs["bookmark"] = bookmark - # if bookmark is none this mean all items is extracted. - else: - break + verify_api_response(http_response) + + items = http_response.get('items', []) + bookmark = http_response.get('bookmark', None) + + # Set the new bookmark + if bookmark is not None: + kwargs["bookmark"] = bookmark - kwargs.update(params) + if not bookmark_model_cls: + kwargs.update(params) bookmark_model = Bookmark( bookmark_token=bookmark, - model=cls, - model_fn='get_all', + model=cls if not bookmark_model_cls else bookmark_model_cls, + model_fn='get_all' if not bookmark_model_fn else bookmark_model_fn.__name__, model_fn_args=kwargs, - client=client, + client=client if not bookmark_model_cls else None, ) if bookmark else None return [map_fn(item) for item in items], bookmark_model diff --git a/pinterest/utils/bookmark.py b/pinterest/utils/bookmark.py index d05a868..d62f5b1 100644 --- a/pinterest/utils/bookmark.py +++ b/pinterest/utils/bookmark.py @@ -43,6 +43,8 @@ def get_next(self) -> tuple[list[object], Bookmark]: Bookmark: Bookmark Object for pagination if present, else None. """ self.model_fn_args['bookmark'] = self.bookmark_token + if self.client: + self.model_fn_args['client'] = self.client if 'kwargs' in self.model_fn_args: kwargs = self.model_fn_args.get('kwargs') del self.model_fn_args['kwargs'] diff --git a/pinterest/utils/load_json_config.py b/pinterest/utils/load_json_config.py index fc1af69..cbfecbb 100644 --- a/pinterest/utils/load_json_config.py +++ b/pinterest/utils/load_json_config.py @@ -5,7 +5,7 @@ import json import os -__all__ = ['load_json_config'] +__all__ = ['load_json_config', 'load_json_config_from_single_env_var'] _PREFIX = 'PINTEREST_' @@ -23,6 +23,20 @@ def load_json_config(): for attribute, value in config_json.items(): _set_as_environment_variables(f'{_PREFIX}{attribute.upper()}', str(value)) +def load_json_config_from_single_env_var(): + """ + Parse PINTEREST_JSON_ENV_VARIABLES environment variable to split long JSON string into + individual environment variables. + """ + config_json = os.environ.get('PINTEREST_JSON_ENV_VARIABLES') + if not config_json: + return + + config_json = json.loads(config_json) + + for attribute, value in config_json.items(): + os.environ[attribute] = str(value) + def _get_current_dir(): return os.path.abspath(os.path.join(os.getcwd(), os.path.pardir)) diff --git a/pinterest/utils/refresh_access_token.py b/pinterest/utils/refresh_access_token.py index 2a64311..252a1a9 100644 --- a/pinterest/utils/refresh_access_token.py +++ b/pinterest/utils/refresh_access_token.py @@ -43,10 +43,18 @@ def get_new_access_token( body=data, timeout=5 ) - if not response.status == 200: - raise SdkException(reason="Authentication error. \ - Kindly check if the following variables are correct: [PINTEREST_ACCESS_TOKEN] or \ - [PINTEREST_APP_ID, PINTEREST_APP_SECRET, PINTEREST_REFRESH_ACCESS_TOKEN]") + if response.status == 401: + raise SdkException( + status=response.status, + reason=response.reason, + body="Authentication error. " + + "Kindly check if the following variables are correct: [PINTEREST_ACCESS_TOKEN] or " + + "[PINTEREST_APP_ID, PINTEREST_APP_SECRET, PINTEREST_REFRESH_ACCESS_TOKEN]. " + + f"Response from server: {response.data}", + http_resp=response + ) + if response.status != 200: + raise SdkException(http_resp=response) data = json.loads(response.data) diff --git a/pinterest/utils/sdk_exceptions.py b/pinterest/utils/sdk_exceptions.py index f992f25..764de17 100644 --- a/pinterest/utils/sdk_exceptions.py +++ b/pinterest/utils/sdk_exceptions.py @@ -4,7 +4,7 @@ class SdkException(Exception): """Raises an exception for Model's Errors""" - def __init__(self, status=None, reason=None, http_resp=None): + def __init__(self, status=None, reason=None, http_resp=None, body=None): if http_resp: self.status = http_resp.status self.reason = http_resp.reason @@ -13,7 +13,7 @@ def __init__(self, status=None, reason=None, http_resp=None): else: self.status = status self.reason = reason - self.body = None + self.body = body self.headers = None def __str__(self): diff --git a/pinterest/version.py b/pinterest/version.py index 94753e9..39f3e05 100644 --- a/pinterest/version.py +++ b/pinterest/version.py @@ -1,4 +1,4 @@ """ Pinterest SDK Packages Version """ -__version__ = '0.2.0' +__version__ = '0.2.1' diff --git a/requirements.txt b/requirements.txt index 00a7736..101fee2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Pinterest-Generated-Client==0.1.5 +Pinterest-Generated-Client==0.1.7 python-dateutil==2.8.2 six==1.16.0 urllib3==1.26.12 diff --git a/setup.py b/setup.py index 2fc5213..edadc0c 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def _get_prod_version(): "python-dateutil", "python-dotenv==0.20.0", "six==1.16.0", - "Pinterest-Generated-Client==0.1.5" + "Pinterest-Generated-Client==0.1.7" ] long_description = (Path(__file__).parent / "README.md").read_text() diff --git a/tests/src/pinterest/ads/test_ad_groups.py b/tests/src/pinterest/ads/test_ad_groups.py index 396c1b3..af61403 100644 --- a/tests/src/pinterest/ads/test_ad_groups.py +++ b/tests/src/pinterest/ads/test_ad_groups.py @@ -84,6 +84,7 @@ def test_create_ad_group_model_new_ad_group(self, ad_group_get_mock, ad_group_cr campaign_id=self.test_campaign_id, billable_event='CLICKTHROUGH', name='SDK_TEST_CLIENT_ADGROUP', + auto_targeting_enabled=False ) assert created_ad_group @@ -101,7 +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"] + "GENDER": ["male"] } get_mock.return_value = AdGroupResponse( diff --git a/tests/src/pinterest/ads/test_conversion_events.py b/tests/src/pinterest/ads/test_conversion_events.py index 5034b9e..dbda9c6 100644 --- a/tests/src/pinterest/ads/test_conversion_events.py +++ b/tests/src/pinterest/ads/test_conversion_events.py @@ -1,11 +1,9 @@ from unittest import TestCase from unittest.mock import patch - from openapi_generated.pinterest_client.model.conversion_api_response import ConversionApiResponse from openapi_generated.pinterest_client.model.conversion_api_response_events import ConversionApiResponseEvents - from pinterest.ads.conversion_events import Conversion class TestConversionEvent(TestCase): @@ -46,6 +44,7 @@ def test_send_conversion_event_success(self, create_mock): event_time = 1670026573, event_id = "eventId0001", user_data = raw_user_data, + custom_data = dict(), ) for _ in range(NUMBER_OF_CONVERSION_EVENTS) ] diff --git a/tests/src/pinterest/ads/test_conversion_tags.py b/tests/src/pinterest/ads/test_conversion_tags.py index 6a01a20..30c6f1f 100644 --- a/tests/src/pinterest/ads/test_conversion_tags.py +++ b/tests/src/pinterest/ads/test_conversion_tags.py @@ -159,33 +159,57 @@ def test_get_list_conversion_tag(self, list_mock, get_mock): enhanced_match_status = EnhancedMatchStatusType("VALIDATION_COMPLETE"), id = self.test_conversion_tag_id, last_fired_time_ms = float(1599030000000), - name = "ACME Checkout Test Tag", + name = "ACME Checkout Test Tag 1", status = EntityStatus("ACTIVE"), version = "3", configs = test_configs - ) + ), + ConversionTagResponse( + ad_account_id = self.test_ad_account_id, + code_snippet = "