diff --git a/notebooks/audience_e2e_advertiser_flow.ipynb b/notebooks/audience_e2e_advertiser_flow.ipynb new file mode 100644 index 0000000..a751e5b --- /dev/null +++ b/notebooks/audience_e2e_advertiser_flow.ipynb @@ -0,0 +1,532 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cb97ec0d", + "metadata": { + "id": "h2sCjFoFUEYZ" + }, + "source": [ + "Project: /data-manager/api/_project.yaml\n", + "Book: /data-manager/api/_book.yaml\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5aa2bcd2", + "metadata": { + "cellView": "form", + "id": "ZL8ttX6NRUE0" + }, + "outputs": [], + "source": [ + "# @markdown #### Copyright 2026 Google LLC\n", + "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "399b973a", + "metadata": {}, + "source": [ + "# Advertiser Flow" + ] + }, + { + "cell_type": "markdown", + "id": "a7eda861", + "metadata": {}, + "source": [ + "## Objective\n", + "This notebook provides the workflow for an Advertiser operating on their own Google Ads account.\n", + "It can authenticate using either OAuth Client Credentials or a Service Account.\n", + "\n", + "## Prerequisites\n", + "\n", + "One of the following:\n", + "\n", + "- A client ID, client secret, and refresh token for a Google Account with access to your Google Ads account.\n", + "\n", + " Use this option if you've already gone through the process of generating user credentials.\n", + "\n", + "- The JSON for a **Desktop app** OAuth client.\n", + "\n", + " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#user-account) if you don't have this JSON file. You'll use the JSON file you download for the prompt in **Step 1**.\n", + "\n", + "- The email address of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n", + "\n", + " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n", + "\n", + "\n", + "## Instructions\n", + "1. Make a copy of this Colab.\n", + "2. Fill in the prompts in **Step 1**.\n", + "3. Run your copy of the Colab.\n" + ] + }, + { + "cell_type": "markdown", + "id": "0cd51598", + "metadata": { + "id": "-wtv73t1egvl" + }, + "source": [ + "### Step 0. Install and Import Packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0c3f5c5", + "metadata": { + "cellView": "form", + "collapsed": true, + "id": "j9kMcKunX3dd" + }, + "outputs": [], + "source": [ + "!pip install --upgrade google-ads-datamanager google-auth-oauthlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89bba485", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "import getpass\n", + "from google.ads import datamanager_v1\n", + "import google.auth\n", + "from google.colab import files\n", + "from google.oauth2.credentials import Credentials\n", + "from google.protobuf.json_format import MessageToJson" + ] + }, + { + "cell_type": "markdown", + "id": "b5345bff", + "metadata": { + "id": "81MXNTkJezi9" + }, + "source": [ + "### Step 1. Setup and Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6147f75d", + "metadata": {}, + "outputs": [], + "source": [ + "authentication_method = \"OAuth Client Credentials\" # @param [\"OAuth Client Credentials\", \"Service Account\"]\n", + "\n", + "# @markdown *Check this if you already have a Client ID, Client Secret, and Refresh Token to bypass the gcloud setup:*\n", + "use_existing_credentials = False # @param {type:\"boolean\"}\n", + "\n", + "# @markdown ### Account IDs\n", + "# @markdown **ADVERTISERS:**\n", + "# @markdown - **[required]** `operating_account_id`: Your advertiser account\n", + "# @markdown - (*optional*) `login_account_id`: Don't set this if the Google Account of your credentials is a user in the advertiser account. If instead the Google Account has access to a Google Ads manager account with the advertiser account as a child, set this to the customer ID of the Google Ads manager account.\n", + "operating_account_id = \"\" # @param {type:\"string\"}\n", + "login_account_id = \"\" # @param {type:\"string\"}\n", + "\n", + "# @markdown ### Service Account Details (If applicable)\n", + "service_account_email = \"\" # @param {type:\"string\"}\n", + "\n", + "# Clean up account IDs by removing hyphens.\n", + "operating_account_id = operating_account_id.replace(\"-\", \"\")\n", + "login_account_id = login_account_id.replace(\"-\", \"\")\n", + "\n", + "GOOGLE_ADS = \"GOOGLE_ADS\"\n", + "CONSENT_GRANTED = \"CONSENT_GRANTED\"" + ] + }, + { + "cell_type": "markdown", + "id": "b20add50", + "metadata": { + "id": "Kv3wnXRDfBgY" + }, + "source": [ + "### Step 2. Initialize Client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7c4a1ab", + "metadata": {}, + "outputs": [], + "source": [ + "class DataManagerSDK:\n", + " def __init__(self, creds):\n", + " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n", + " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n", + "\n", + "def prompt_for_desktop_client_json():\n", + "\n", + " # Prompts the user to upload the JSON file\n", + " print(\"Upload the client JSON file for your Google Cloud Desktop OAuth client:\")\n", + " uploaded = files.upload()\n", + " desktop_app_json_file = \"/tmp/oauth_client.json\"\n", + "\n", + " if len(uploaded) != 1:\n", + " raise ValueError(\"Please upload exactly one file.\")\n", + "\n", + " filename = list(uploaded.keys())[0]\n", + " content = uploaded[filename]\n", + "\n", + " # Saves the uploaded file.\n", + " with open(desktop_app_json_file, \"wb\") as f:\n", + " f.write(content)\n", + " # Returns the file path and name.\n", + " return desktop_app_json_file\n", + "\n", + "def initialize_client():\n", + " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\";\n", + " creds = None\n", + "\n", + " if authentication_method == \"OAuth Client Credentials\":\n", + " if use_existing_credentials:\n", + " print(\"Please enter your OAuth credentials:\")\n", + " client_id = input(\"Client ID: \").strip()\n", + " client_secret = getpass.getpass(\"Client Secret (input will be hidden): \").strip()\n", + " refresh_token = getpass.getpass(\"Refresh Token (input will be hidden): \").strip()\n", + "\n", + " creds = Credentials(\n", + " token=None,\n", + " client_id=client_id,\n", + " client_secret=client_secret,\n", + " refresh_token=refresh_token,\n", + " token_uri=\"https://oauth2.googleapis.com/token\",\n", + " scopes=[data_manager_scope]\n", + " )\n", + " else:\n", + " print(\"Authenticating with OAuth via gcloud...\")\n", + " client_json_file = prompt_for_desktop_client_json()\n", + " auth_command = (\n", + " f\"gcloud auth application-default login \"\n", + " f\"--client-id-file='{client_json_file}' \"\n", + " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n", + " f\"--no-browser\"\n", + " )\n", + " print(\"Running gcloud auth command...\")\n", + " get_ipython().system(auth_command)\n", + " print(\"gcloud auth process finished\")\n", + " creds, project = google.auth.default(scopes=[data_manager_scope])\n", + " else:\n", + " print(\"Authenticating with Service Account...\")\n", + " auth_command = (\n", + " f\"gcloud auth application-default login \"\n", + " f\"--impersonate-service-account={service_account_email} \"\n", + " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n", + " f\"--no-browser\"\n", + " )\n", + " print(\"Running gcloud auth command...\")\n", + " get_ipython().system(auth_command)\n", + " print(\"gcloud auth process finished\")\n", + " creds, project = google.auth.default(scopes=[data_manager_scope])\n", + "\n", + " return DataManagerSDK(creds)\n", + "\n", + "sdk = initialize_client()" + ] + }, + { + "cell_type": "markdown", + "id": "d822024f", + "metadata": { + "id": "89jsXS8jfQSd" + }, + "source": [ + "### Step 3. Create User List" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4db10e1c", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Creating User List in {operating_account_id}...\\n\")\n", + "\n", + "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n", + "\n", + "user_list_data = datamanager_v1.UserList(\n", + " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n", + " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n", + " upload_key_types=[\"CONTACT_ID\"]\n", + " ),\n", + ")\n", + "\n", + "headers = []\n", + "if login_account_id:\n", + " headers.append(\n", + " (\n", + " \"login-account\",\n", + " f\"accountTypes/GOOGLE_ADS/accounts/{login_account_id}\",\n", + " )\n", + " )\n", + "\n", + "destination_id = None\n", + "try:\n", + " ulist_res = sdk.user_list_service.create_user_list(\n", + " parent=parent_userlist, user_list=user_list_data, metadata=headers\n", + " )\n", + "\n", + " destination_id = ulist_res.id\n", + "\n", + " print(f\"User List Created!\")\n", + " print(f\"Destination ID: {destination_id}\")\n", + " print(f\"Resource Name: {ulist_res.name}\")\n", + " print(f\"Display Name: {ulist_res.display_name}\")\n", + "\n", + "except Exception as e:\n", + " print(f\"Failed to create User List: {e}\")\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n", + ")\n", + "print(f\"\\nparent: {parent_userlist}\")\n", + "\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(user_list_data._pb))\n", + "print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d6e78c7", + "metadata": { + "id": "CrmA_yg0fUad" + }, + "source": [ + "### Step 4. Ingest Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dc8934f", + "metadata": {}, + "outputs": [], + "source": [ + "ingestion_request_id = None\n", + "\n", + "if not destination_id:\n", + " print(\n", + " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n", + " )\n", + "else:\n", + " print(f\"Ingesting sample data to User List {destination_id}...\")\n", + "\n", + " destination_obj = datamanager_v1.Destination(\n", + " operating_account=datamanager_v1.ProductAccount(\n", + " account_type=GOOGLE_ADS, account_id=operating_account_id\n", + " ),\n", + " product_destination_id=str(destination_id),\n", + " )\n", + "\n", + " if login_account_id:\n", + " destination_obj.login_account = datamanager_v1.ProductAccount(\n", + " account_type=GOOGLE_ADS, account_id=login_account_id\n", + " )\n", + "\n", + " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n", + " consent=datamanager_v1.Consent(\n", + " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n", + " ),\n", + " encoding=datamanager_v1.Encoding.HEX,\n", + " terms_of_service=datamanager_v1.TermsOfService(\n", + " customer_match_terms_of_service_status=\"ACCEPTED\"\n", + " ),\n", + " validate_only=False,\n", + " audience_members=[\n", + " datamanager_v1.AudienceMember(\n", + " user_data=datamanager_v1.UserData(\n", + " user_identifiers=[\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n", + " ),\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n", + " ),\n", + " ]\n", + " )\n", + " )\n", + " ],\n", + " destinations=[destination_obj],\n", + " )\n", + "\n", + " try:\n", + " ingest_res = sdk.ingestion_service.ingest_audience_members(\n", + " request=ingest_payload\n", + " )\n", + "\n", + " ingestion_request_id = ingest_res.request_id\n", + " print(f\"Ingestion Submitted!\")\n", + " print(f\"Request ID: {ingestion_request_id}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to ingest data: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(ingest_payload._pb))\n", + " print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "68eab82f", + "metadata": { + "id": "DcXOh5xJfYdh" + }, + "source": [ + "### Step 5. Check Status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a05f95c3", + "metadata": { + "id": "DiEenmMYiIN3" + }, + "outputs": [], + "source": [ + "if not ingestion_request_id:\n", + " print(\"Status check skipped because ingestion request ID is not set.\")\n", + "else:\n", + " print(f\"5. Checking status for Request ID: {ingestion_request_id}...\")\n", + "\n", + " # Note: Headers aren't needed for this request.\n", + " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n", + " request_id=ingestion_request_id\n", + " )\n", + "\n", + " try:\n", + " status_res = sdk.ingestion_service.retrieve_request_status(\n", + " request=status_req\n", + " )\n", + "\n", + " print(f\"Successfully retrieved status!\")\n", + "\n", + " for dest_status in status_res.request_status_per_destination:\n", + " dest_id = dest_status.destination.product_destination_id\n", + " current_status = dest_status.request_status.name\n", + "\n", + " print(f\"Status for destination {dest_id}: {current_status}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to retrieve status: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(status_req._pb))\n", + " print(\"----------------------------\")" + ] + } + ], + "metadata": { + "colab": { + "private_outputs": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/notebooks/audience_e2e_data_partner_dual_persona.ipynb b/notebooks/audience_e2e_data_partner_dual_persona.ipynb new file mode 100644 index 0000000..655e603 --- /dev/null +++ b/notebooks/audience_e2e_data_partner_dual_persona.ipynb @@ -0,0 +1,693 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8c6a9fa0", + "metadata": { + "id": "h2sCjFoFUEYZ" + }, + "source": [ + "Project: /data-manager/api/_project.yaml\n", + "Book: /data-manager/api/_book.yaml\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "056ea28b", + "metadata": { + "cellView": "form", + "id": "ZL8ttX6NRUE0" + }, + "outputs": [], + "source": [ + "# @markdown #### Copyright 2026 Google LLC\n", + "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "ef431906", + "metadata": {}, + "source": [ + "# Data Partner: Dual Persona Workflow (In-Platform Experience)" + ] + }, + { + "cell_type": "markdown", + "id": "84041125", + "metadata": {}, + "source": [ + "## Objective\n", + "\n", + "This notebook simulates the exact flow required for App Verification, where advertisers link to a partner within your UI.\n", + "\n", + "## Prerequisites\n", + "\n", + "1. The JSON for a **Desktop app** OAuth client.\n", + "\n", + " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#user-account) if you don't have this JSON file. You'll use the JSON file you download for the prompt in **Phase 0**.\n", + "\n", + "2. The email address and project ID of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n", + "\n", + " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n", + "\n", + "## Phases\n", + "\n", + "* **Phase 0:** Provide OAuth Information and Account IDs\n", + "\n", + " Fill in the prompts with information about your service account, data partner, and advertiser account.\n", + "\n", + " Upload the JSON file for your OAuth Desktop app.\n", + "\n", + "* **Phase 1:** Act as the Advertiser\n", + "\n", + " Create a partner link from the advertiser to the data partner. This step uses user credentials for a user with access to the advertiser account.\n", + "\n", + "* **Phase 2:** Act as the Data Partner\n", + "\n", + " Retrieve the partner link, create an audience, push data, and check the processing status. This step uses service account credentials for a service account with access to the data partner account.\n", + "\n", + "## Instructions\n", + "\n", + "1. Make a copy of this Colab.\n", + "2. Fill in the prompts in **Phase 0**.\n", + "3. Run your copy of the Colab." + ] + }, + { + "cell_type": "markdown", + "id": "b901130d", + "metadata": { + "id": "-wtv73t1egvl" + }, + "source": [ + "### Step 0. Install and Import Packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9bb983c", + "metadata": { + "cellView": "form", + "collapsed": true, + "id": "j9kMcKunX3dd" + }, + "outputs": [], + "source": [ + "!pip install --upgrade google-ads-datamanager google-auth-oauthlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72a39bf1", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "import getpass\n", + "from google.ads import datamanager_v1\n", + "import google.auth\n", + "from google.colab import files\n", + "from google.oauth2.credentials import Credentials\n", + "from google.protobuf.json_format import MessageToJson" + ] + }, + { + "cell_type": "markdown", + "id": "59971378", + "metadata": {}, + "source": [ + "## Phase 0: Provide OAuth Information and Account IDs" + ] + }, + { + "cell_type": "markdown", + "id": "706c6a04", + "metadata": {}, + "source": [ + "### Step 1: Provide credentials information and account IDs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "917707e7", + "metadata": {}, + "outputs": [], + "source": [ + "# @markdown ### Account IDs\n", + "# @markdown Provide the advertiser and data partner account IDs below.\n", + "# @markdown - **[required]** `login_account_id`: Your Data Partner account.\n", + "# @markdown - **[required]** `operating_account_id`: The Google Ads advertiser account that will own the audience you create as part of this workflow.\n", + "# @markdown - (*optional*}) `linked_account_id`: If you want to link the `operating_account` directly to your data partner account, leave this blank. If instead you want to link a Google Ads manager account (where the `operating_account` is a child) to your data partner account, set this to the customer ID of the Google Ads manager account.\n", + "login_account_id = \"\" # @param {type:\"string\"}\n", + "operating_account_id = \"\" # @param {type:\"string\"}\n", + "linked_account_id = \"\" # @param {type:\"string\"}\n", + "\n", + "# @markdown *Check this if you already have a Client ID, Client Secret, and Refresh Token for the Advertiser to bypass the gcloud setup:*\n", + "use_existing_advertiser_credentials = False # @param {type:\"boolean\"}\n", + "\n", + "# @markdown ### Service Account Details (If applicable)\n", + "service_account_email = \"\" # @param {type:\"string\"}\n", + "\n", + "# Clean up account IDs by removing hyphens.\n", + "login_account_id = login_account_id.replace(\"-\", \"\")\n", + "operating_account_id = operating_account_id.replace(\"-\", \"\")\n", + "linked_account_id = linked_account_id.replace(\"-\", \"\")\n", + "\n", + "# Sets the linked_account_id to the operating account if linking directly to the operating account.\n", + "linked_account_id = linked_account_id or operating_account_id\n", + "\n", + "# Constants.\n", + "GOOGLE_ADS = \"GOOGLE_ADS\"\n", + "DATA_PARTNER = \"DATA_PARTNER\"\n", + "CONSENT_GRANTED = \"CONSENT_GRANTED\"\n", + "PARTNER_LINK_SCOPE = \"https://www.googleapis.com/auth/datamanager.partnerlink\"\n", + "DESKTOP_APP_JSON_FILE = \"/tmp/oauth_client.json\"" + ] + }, + { + "cell_type": "markdown", + "id": "d59c5021", + "metadata": {}, + "source": [ + "### Step 2: Upload OAuth Desktop client JSON for the Advertiser credentials\n", + "\n", + "**IMPORTANT:** Follow the prompts in the command output to complete this step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b586c553", + "metadata": {}, + "outputs": [], + "source": [ + "if use_existing_advertiser_credentials:\n", + " print(\n", + " \"Skipping OAuth client JSON upload since we're using preexisting Advertiser OAuth credentials.\"\n", + " )\n", + "else:\n", + " # Prompts the user to upload the JSON file\n", + " print(\n", + " \"Upload the client JSON file for your Google Cloud Desktop OAuth client:\"\n", + " )\n", + " uploaded = files.upload()\n", + "\n", + " if len(uploaded) != 1:\n", + " raise ValueError(\"Please upload exactly one file\")\n", + "\n", + " filename = list(uploaded.keys())[0]\n", + " content = uploaded[filename]\n", + "\n", + " # Saves the uploaded file.\n", + " with open(DESKTOP_APP_JSON_FILE, \"wb\") as f:\n", + " f.write(content)" + ] + }, + { + "cell_type": "markdown", + "id": "cc65d910", + "metadata": {}, + "source": [ + "## Phase 1: Advertiser Creates Partner Link" + ] + }, + { + "cell_type": "markdown", + "id": "8bc0b07c", + "metadata": {}, + "source": [ + "### Step 1. Initialize Advertiser Client\n", + "\n", + "**IMPORTANT:** Follow the prompts in the command output to complete this step." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b82d2d2", + "metadata": {}, + "outputs": [], + "source": [ + "class DataManagerAdvertiserSDK:\n", + " def __init__(self, creds):\n", + " self.link_service = datamanager_v1.PartnerLinkServiceClient(credentials=creds)\n", + "\n", + "def initialize_advertiser_client():\n", + " print(\"Authenticating Advertiser with OAuth...\")\n", + " creds = None\n", + "\n", + " try:\n", + " if use_existing_advertiser_credentials:\n", + " print(\"Using existing Advertiser OAuth credentials\")\n", + " print()\n", + " # Prompts the user for the client ID, client secret, and refresh token.\n", + " print(\"Please enter your preexisting Advertiser OAuth credentials:\")\n", + " advertiser_oauth_client_id = input(\"Client ID: \").strip()\n", + " advertiser_oauth_client_secret = getpass.getpass(\n", + " \"Client Secret (input will be hidden): \"\n", + " ).strip()\n", + " advertiser_oauth_client_refresh_token = getpass.getpass(\n", + " \"Refresh Token (input will be hidden): \"\n", + " ).strip()\n", + "\n", + " # Creates user credentials using the provided values.\n", + " creds = Credentials(\n", + " token=None,\n", + " client_id=advertiser_oauth_client_id,\n", + " client_secret=advertiser_oauth_client_secret,\n", + " refresh_token=advertiser_oauth_client_refresh_token,\n", + " token_uri=\"https://oauth2.googleapis.com/token\",\n", + " scopes=[PARTNER_LINK_SCOPE]\n", + " )\n", + " else:\n", + " # Run the gcloud command\n", + " # Uses the less permissive datamanager.partnerlink scope since the\n", + " # only action the advertiser needs to perform is creating the\n", + " # partner link.\n", + " print(\"Running gcloud auth to get Advertiser credentials...\")\n", + " auth_command = (\n", + " f\"gcloud auth application-default login \"\n", + " f\"--client-id-file='{DESKTOP_APP_JSON_FILE}' \"\n", + " f\"--scopes={PARTNER_LINK_SCOPE},https://www.googleapis.com/auth/cloud-platform \"\n", + " f\"--no-browser\"\n", + " )\n", + " get_ipython().system(auth_command)\n", + "\n", + " print()\n", + " print(\"gcloud auth process finished.\")\n", + "\n", + " creds, project = google.auth.default(scopes=[PARTNER_LINK_SCOPE])\n", + "\n", + " except Exception as e:\n", + " print(f\"An error occurred: {e}\")\n", + "\n", + " return DataManagerAdvertiserSDK(creds)\n", + "\n", + "advertiser_sdk = initialize_advertiser_client()\n", + "print(\"Advertiser SDK Initialized\")" + ] + }, + { + "cell_type": "markdown", + "id": "7ccb2114", + "metadata": { + "id": "--2LPM7_fKZM" + }, + "source": [ + "### Step 2. Create Partner Link" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa1cf9d3", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Creating link for Ads {linked_account_id}..\\n\")\n", + "\n", + "parent_ads = f\"accountTypes/GOOGLE_ADS/accounts/{linked_account_id}\"\n", + "\n", + "partner_link_data = datamanager_v1.PartnerLink(\n", + " owning_account=datamanager_v1.ProductAccount(\n", + " account_id=linked_account_id, account_type=GOOGLE_ADS\n", + " ),\n", + " partner_account=datamanager_v1.ProductAccount(\n", + " account_id=login_account_id, account_type=DATA_PARTNER\n", + " ),\n", + ")\n", + "\n", + "try:\n", + " link_res = advertiser_sdk.link_service.create_partner_link(\n", + " parent=parent_ads, partner_link=partner_link_data\n", + " )\n", + " print(f\"Link Created: {link_res.partner_link_id}\")\n", + " product_link_id = link_res.partner_link_id\n", + "except Exception as e:\n", + " print(f\"Link creation failed or exists: {e}\")\n", + " print(\"\\nSearching for existing link...\")\n", + " search_res = advertiser_sdk.link_service.search_partner_links(\n", + " parent=f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n", + " )\n", + " product_link_id = None\n", + " for link in search_res:\n", + " if link.owning_account.account_id == linked_account_id:\n", + " product_link_id = link.partner_link_id\n", + " print(f\"Found existing Link ID: {product_link_id}\")\n", + " break\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/create?apix=true\"\n", + ")\n", + "print(f\"\\nparent: {parent_ads}\")\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(partner_link_data._pb))\n", + "print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "cfc3a10b", + "metadata": {}, + "source": [ + "## Phase 2: Data Partner Ingests Data" + ] + }, + { + "cell_type": "markdown", + "id": "5c94c532", + "metadata": {}, + "source": [ + "### Step 1. Initialize Data Partner Client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a54be3c6", + "metadata": {}, + "outputs": [], + "source": [ + "class DataManagerPartnerSDK:\n", + " def __init__(self, creds):\n", + " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n", + " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n", + "\n", + "def initialize_partner_client():\n", + " print(\"Authenticating Data Partner with Service Account...\")\n", + " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\"\n", + " auth_command = (\n", + " f\"gcloud auth application-default login \"\n", + " f\"--impersonate-service-account={service_account_email} \"\n", + " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n", + " f\"--no-browser\"\n", + " )\n", + " get_ipython().system(auth_command)\n", + " creds, project = google.auth.default(scopes=[data_manager_scope])\n", + " return DataManagerPartnerSDK(creds)\n", + "\n", + "partner_sdk = initialize_partner_client()\n", + "print(\"Data Partner SDK Initialized\")" + ] + }, + { + "cell_type": "markdown", + "id": "494a47fa", + "metadata": { + "id": "89jsXS8jfQSd" + }, + "source": [ + "### Step 2. Create User List" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b41b5e0", + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"Creating User List in {operating_account_id}...\\n\")\n", + "\n", + "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n", + "\n", + "user_list_data = datamanager_v1.UserList(\n", + " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n", + " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n", + " upload_key_types=[\"CONTACT_ID\"]\n", + " ),\n", + ")\n", + "\n", + "login_account = f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n", + "linked_account = f\"accountTypes/GOOGLE_ADS/accounts/{linked_account_id}\"\n", + "\n", + "# Creates a list of header tuples.\n", + "headers = [\n", + " (\"login-account\", login_account),\n", + " (\"linked-account\", linked_account),\n", + "]\n", + "\n", + "destination_id = None\n", + "try:\n", + " ulist_res = partner_sdk.user_list_service.create_user_list(\n", + " parent=parent_userlist, user_list=user_list_data, metadata=headers\n", + " )\n", + "\n", + " destination_id = ulist_res.id\n", + "\n", + " print(f\"User List Created!\")\n", + " print(f\"Destination ID: {destination_id}\")\n", + " print(f\"Resource Name: {ulist_res.name}\")\n", + " print(f\"Display Name: {ulist_res.display_name}\")\n", + "\n", + "except Exception as e:\n", + " print(f\"Failed to create User List: {e}\")\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n", + ")\n", + "print(f\"\\nparent: {parent_userlist}\")\n", + "print(f\"login-account: {login_account}\")\n", + "print(f\"linked-account: {linked_account}\")\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(user_list_data._pb))\n", + "print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "3ab62226", + "metadata": { + "id": "CrmA_yg0fUad" + }, + "source": [ + "### Step 3. Ingest Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ec8601e", + "metadata": {}, + "outputs": [], + "source": [ + "ingestion_request_id = None\n", + "\n", + "if not destination_id:\n", + " print(\n", + " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n", + " )\n", + "else:\n", + " print(f\"Ingesting sample data to User List {destination_id}...\")\n", + "\n", + " destination_obj = datamanager_v1.Destination(\n", + " login_account=datamanager_v1.ProductAccount(\n", + " account_type=DATA_PARTNER, account_id=login_account_id\n", + " ),\n", + " operating_account=datamanager_v1.ProductAccount(\n", + " account_type=GOOGLE_ADS, account_id=operating_account_id\n", + " ),\n", + " linked_account=datamanager_v1.ProductAccount(\n", + " account_type=GOOGLE_ADS, account_id=linked_account_id\n", + " ),\n", + " product_destination_id=str(destination_id),\n", + " )\n", + "\n", + " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n", + " consent=datamanager_v1.Consent(\n", + " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n", + " ),\n", + " encoding=datamanager_v1.Encoding.HEX,\n", + " terms_of_service=datamanager_v1.TermsOfService(\n", + " customer_match_terms_of_service_status=\"ACCEPTED\"\n", + " ),\n", + " validate_only=False,\n", + " audience_members=[\n", + " datamanager_v1.AudienceMember(\n", + " user_data=datamanager_v1.UserData(\n", + " user_identifiers=[\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n", + " ),\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n", + " ),\n", + " ]\n", + " )\n", + " )\n", + " ],\n", + " destinations=[destination_obj],\n", + " )\n", + "\n", + " try:\n", + " ingest_res = partner_sdk.ingestion_service.ingest_audience_members(\n", + " request=ingest_payload\n", + " )\n", + "\n", + " ingestion_request_id = ingest_res.request_id\n", + " print(f\"Ingestion Submitted!\")\n", + " print(f\"Request ID: {ingestion_request_id}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to ingest data: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(ingest_payload._pb))\n", + " print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "c4eab526", + "metadata": { + "id": "DcXOh5xJfYdh" + }, + "source": [ + "### Step 4. Check Status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b69395e4", + "metadata": {}, + "outputs": [], + "source": [ + "if not ingestion_request_id:\n", + " print(\"Status check skipped because ingestion request ID is not set.\")\n", + "else:\n", + " print(f\"Checking status for Request ID: {ingestion_request_id}...\")\n", + "\n", + " # Note: Headers aren't needed for this request.\n", + " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n", + " request_id=ingestion_request_id\n", + " )\n", + "\n", + " try:\n", + " status_res = partner_sdk.ingestion_service.retrieve_request_status(\n", + " request=status_req\n", + " )\n", + "\n", + " print(f\"Successfully retrieved status!\")\n", + "\n", + " for dest_status in status_res.request_status_per_destination:\n", + " dest_id = dest_status.destination.product_destination_id\n", + " current_status = dest_status.request_status.name\n", + "\n", + " print(f\"Status for destination {dest_id}: {current_status}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to retrieve status: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(status_req._pb))\n", + " print(\"----------------------------\")" + ] + } + ], + "metadata": { + "colab": { + "private_outputs": true, + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/notebooks/audience_e2e_data_partner_simplified.ipynb b/notebooks/audience_e2e_data_partner_simplified.ipynb new file mode 100644 index 0000000..ac6485c --- /dev/null +++ b/notebooks/audience_e2e_data_partner_simplified.ipynb @@ -0,0 +1,582 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7c0fc11f", + "metadata": { + "id": "h2sCjFoFUEYZ" + }, + "source": [ + "Project: /data-manager/api/_project.yaml\n", + "Book: /data-manager/api/_book.yaml\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "549fb865", + "metadata": { + "cellView": "form", + "id": "ZL8ttX6NRUE0" + }, + "outputs": [], + "source": [ + "# @markdown #### Copyright 2026 Google LLC\n", + "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# https://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "b1b6c27f", + "metadata": {}, + "source": [ + "# Data Partner: Simplified Testing Workflow" + ] + }, + { + "cell_type": "markdown", + "id": "e54c6728", + "metadata": {}, + "source": [ + "## Objective\n", + "This notebook provides a simplified testing workflow for the Data Manager API as a Data Partner.\n", + "It uses a Service Account (or OAuth) to authenticate, assuming the authenticated user has ADMIN access to the Google Ads account.\n", + "\n", + "## Prerequisites\n", + "\n", + "The email address of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n", + "\n", + "[Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n", + "\n", + "## Instructions\n", + "1. Make a copy of this Colab.\n", + "2. Fill in the required account IDs and Service Account details in the Setup cell.\n", + "3. Run your copy of the Colab." + ] + }, + { + "cell_type": "markdown", + "id": "d8eb38f6", + "metadata": { + "id": "-wtv73t1egvl" + }, + "source": [ + "### Step 0. Install and Import Packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1895d4d6", + "metadata": { + "cellView": "form", + "collapsed": true, + "id": "j9kMcKunX3dd" + }, + "outputs": [], + "source": [ + "!pip install --upgrade google-ads-datamanager google-auth-oauthlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95962eb6", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "from google.colab import userdata\n", + "from google.ads import datamanager_v1\n", + "import google.auth\n", + "from google.protobuf.json_format import MessageToJson" + ] + }, + { + "cell_type": "markdown", + "id": "82b72a62", + "metadata": { + "id": "81MXNTkJezi9" + }, + "source": [ + "### Step 1. Setup and Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8017d907", + "metadata": {}, + "outputs": [], + "source": [ + "# @markdown ### Account IDs\n", + "# @markdown Provide the advertiser and data partner account IDs below.\n", + "# @markdown - **[required]** `login_account_id`: Your Data Partner account.\n", + "# @markdown - **[required]** `operating_account_id`: The Google Ads advertiser account that you want to link to your data partner account. This account will own the audience you create as part of this workflow.\n", + "login_account_id = \"\" # @param {type:\"string\"}\n", + "operating_account_id = \"\" # @param {type:\"string\"}\n", + "\n", + "# @markdown ### Service Account Details\n", + "service_account_email = \"\" # @param {type:\"string\"}\n", + "\n", + "# Clean up account IDs by removing hyphens.\n", + "login_account_id = login_account_id.replace(\"-\", \"\")\n", + "operating_account_id = operating_account_id.replace(\"-\", \"\")\n", + "\n", + "GOOGLE_ADS = \"GOOGLE_ADS\"\n", + "DATA_PARTNER = \"DATA_PARTNER\"\n", + "CONSENT_GRANTED = \"CONSENT_GRANTED\"" + ] + }, + { + "cell_type": "markdown", + "id": "a2a54e52", + "metadata": { + "id": "Kv3wnXRDfBgY" + }, + "source": [ + "### Step 2. Initialize Client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a3e3115", + "metadata": {}, + "outputs": [], + "source": [ + "class DataManagerSDK:\n", + " def __init__(self, creds):\n", + " self.link_service = datamanager_v1.PartnerLinkServiceClient(credentials=creds)\n", + " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n", + " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n", + "\n", + "def initialize_client():\n", + " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\";\n", + " print(\"Authenticating with Service Account...\")\n", + " auth_command = (\n", + " f\"gcloud auth application-default login \"\n", + " f\"--impersonate-service-account={service_account_email} \"\n", + " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n", + " f\"--no-browser\"\n", + " )\n", + " get_ipython().system(auth_command)\n", + " creds, project = google.auth.default(scopes=[data_manager_scope])\n", + " return DataManagerSDK(creds)\n", + "\n", + "sdk = initialize_client()\n", + "print(\"SDK Services Initialized Successfully\")" + ] + }, + { + "cell_type": "markdown", + "id": "c88699c5", + "metadata": { + "id": "--2LPM7_fKZM" + }, + "source": [ + "### Step 3. Create Partner Link" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1086aeed", + "metadata": { + "id": "V8Fk5jI4aCjO" + }, + "outputs": [], + "source": [ + "print(f\"3. Creating link for Ads {operating_account_id}..\\n\")\n", + "\n", + "parent_ads = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n", + "\n", + "partner_link_data = datamanager_v1.PartnerLink(\n", + " owning_account=datamanager_v1.ProductAccount(\n", + " account_id=operating_account_id, account_type=GOOGLE_ADS\n", + " ),\n", + " partner_account=datamanager_v1.ProductAccount(\n", + " account_id=login_account_id, account_type=DATA_PARTNER\n", + " ),\n", + ")\n", + "\n", + "try:\n", + " link_res = sdk.link_service.create_partner_link(\n", + " parent=parent_ads, partner_link=partner_link_data\n", + " )\n", + " print(f\"Link Created: {link_res.partner_link_id}\")\n", + " product_link_id = link_res.partner_link_id\n", + "except Exception as e:\n", + " print(f\"Link creation failed or exists: {e}\")\n", + " print(\"\\nSearching for existing link...\")\n", + " search_res = sdk.link_service.search_partner_links(\n", + " parent=f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n", + " )\n", + " # The search result is an iterator\n", + " for link in search_res:\n", + " if link.owning_account.account_id == operating_account_id:\n", + " product_link_id = link.partner_link_id\n", + " print(f\"Found existing Link ID: {product_link_id}\")\n", + " break\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/create?apix=true\"\n", + ")\n", + "print(f\"\\nparent: {parent_ads}\")\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(partner_link_data._pb))\n", + "print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "50115d94", + "metadata": { + "id": "89jsXS8jfQSd" + }, + "source": [ + "### Step 4. Create User List" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be6f5aaf", + "metadata": { + "id": "UBpVcR_yzHoQ" + }, + "outputs": [], + "source": [ + "print(f\"4. Creating User List in {operating_account_id}...\\n\")\n", + "\n", + "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n", + "\n", + "user_list_data = datamanager_v1.UserList(\n", + " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n", + " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n", + " upload_key_types=[\"CONTACT_ID\"]\n", + " ),\n", + ")\n", + "\n", + "# Creates a dictionary to set headers.\n", + "login_account = f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n", + "linked_account = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n", + "\n", + "headers = [\n", + " (\"login-account\", login_account),\n", + " (\"linked-account\", linked_account),\n", + "]\n", + "\n", + "destination_id = None\n", + "try:\n", + " ulist_res = sdk.user_list_service.create_user_list(\n", + " parent=parent_userlist, user_list=user_list_data, metadata=headers\n", + " )\n", + "\n", + " destination_id = ulist_res.id\n", + "\n", + " print(f\"User List Created!\")\n", + " print(f\"Destination ID: {destination_id}\")\n", + " print(f\"Resource Name: {ulist_res.name}\")\n", + " print(f\"Display Name: {ulist_res.display_name}\")\n", + "\n", + "except Exception as e:\n", + " print(f\"Failed to create User List: {e}\")\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n", + ")\n", + "print(f\"\\nparent: {parent_userlist}\")\n", + "\n", + "print(f\"login-account: {login_account}\")\n", + "print(f\"linked-account: {linked_account}\")\n", + "\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(user_list_data._pb))\n", + "print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "94d1c221", + "metadata": { + "id": "CrmA_yg0fUad" + }, + "source": [ + "### Step 5. Ingest Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9efe18a", + "metadata": { + "id": "iTYvfXYnM-v-" + }, + "outputs": [], + "source": [ + "ingestion_request_id = None\n", + "\n", + "if not destination_id:\n", + " print(\n", + " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n", + " )\n", + "else:\n", + " print(f\"5. Ingesting sample data to User List {destination_id}...\")\n", + "\n", + " # Build the Destination object explicitly based on the persona\n", + " destination_obj = datamanager_v1.Destination(\n", + " login_account=datamanager_v1.ProductAccount(\n", + " account_type=DATA_PARTNER, account_id=login_account_id\n", + " ),\n", + " operating_account=datamanager_v1.ProductAccount(\n", + " account_type=GOOGLE_ADS, account_id=operating_account_id\n", + " ),\n", + " product_destination_id=str(destination_id),\n", + " )\n", + "\n", + " # Add the destination_obj directly to the payload\n", + " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n", + " consent=datamanager_v1.Consent(\n", + " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n", + " ),\n", + " encoding=datamanager_v1.Encoding.HEX,\n", + " terms_of_service=datamanager_v1.TermsOfService(\n", + " customer_match_terms_of_service_status=\"ACCEPTED\"\n", + " ),\n", + " validate_only=False,\n", + " audience_members=[\n", + " datamanager_v1.AudienceMember(\n", + " user_data=datamanager_v1.UserData(\n", + " user_identifiers=[\n", + " # NOTE: email_address values must be hex-encoded SHA-256\n", + " # hashes of the normalized email addresses (lowercase, dots\n", + " # removed from gmail, etc.).\n", + " # Do NOT use plain text email addresses here.\n", + " # See Data Manager API documentation for formatting rules:\n", + " # https://developers.google.com/data-manager/api/devguides/concepts/formatting\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n", + " ),\n", + " datamanager_v1.UserIdentifier(\n", + " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n", + " ),\n", + " ]\n", + " )\n", + " )\n", + " ],\n", + " destinations=[destination_obj],\n", + " )\n", + "\n", + " try:\n", + " ingest_res = sdk.ingestion_service.ingest_audience_members(\n", + " request=ingest_payload\n", + " )\n", + "\n", + " ingestion_request_id = ingest_res.request_id\n", + " print(f\"Ingestion Submitted!\")\n", + " print(f\"Request ID: {ingestion_request_id}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to ingest data: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(ingest_payload._pb))\n", + " print(\"----------------------------\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "5fedc5fa", + "metadata": { + "id": "DcXOh5xJfYdh" + }, + "source": [ + "### Step 6. Check Status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e056b4d", + "metadata": { + "id": "DiEenmMYiIN3" + }, + "outputs": [], + "source": [ + "if not ingestion_request_id:\n", + " print(\"Status check skipped because ingestion request ID is not set.\")\n", + "else:\n", + " print(f\"6. Checking status for Request ID: {ingestion_request_id}...\")\n", + "\n", + " # Note: Headers aren't needed for this request.\n", + " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n", + " request_id=ingestion_request_id\n", + " )\n", + "\n", + " try:\n", + " status_res = sdk.ingestion_service.retrieve_request_status(\n", + " request=status_req\n", + " )\n", + "\n", + " print(f\"Successfully retrieved status!\")\n", + "\n", + " for dest_status in status_res.request_status_per_destination:\n", + " dest_id = dest_status.destination.product_destination_id\n", + " current_status = dest_status.request_status.name\n", + "\n", + " print(f\"Status for destination {dest_id}: {current_status}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to retrieve status: {e}\")\n", + "\n", + " print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n", + " )\n", + " print(\"\\n--- Request JSON Payload ---\")\n", + " print(MessageToJson(status_req._pb))\n", + " print(\"----------------------------\")" + ] + }, + { + "cell_type": "markdown", + "id": "9d5671dc", + "metadata": { + "id": "gYjD5Fzgfbzu" + }, + "source": [ + "### Step 7. [Optional] Remove Product Link" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a61d62ee", + "metadata": { + "id": "7NY8t6Y3jSL-" + }, + "outputs": [], + "source": [ + "if product_link_id:\n", + " print(f\"\\nAttempting to remove product link {product_link_id}...\")\n", + "\n", + " link_resource_name = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}/partnerLinks/{product_link_id}\"\n", + "\n", + " delete_req = datamanager_v1.DeletePartnerLinkRequest(\n", + " name=link_resource_name\n", + " )\n", + "\n", + " try:\n", + " sdk.link_service.delete_partner_link(request=delete_req)\n", + " print(\"Successfully removed product link.\")\n", + "\n", + " except Exception as e:\n", + " print(f\"Failed to remove product link: {e}\")\n", + "else:\n", + " print(\"\\nNo product_link_id found to delete. Did you run Step 1?\")\n", + "\n", + "print(\n", + " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/delete?apix=true\"\n", + ")\n", + "print(\"\\n--- Request JSON Payload ---\")\n", + "print(MessageToJson(delete_req._pb))\n", + "print(\"----------------------------\")" + ] + } + ], + "metadata": { + "colab": { + "private_outputs": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/pyproject.toml b/pyproject.toml index eaf9e3e..c78e298 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ [project.optional-dependencies] dev = [ - "black", + "black[jupyter]", "nox >= 2020.12.31, < 2022.6", # Include the dependencies for samples for convenience, since sample updates # are often part of the dev process.