From f49c642c957994bf4bab4bf423979f7f03c05987 Mon Sep 17 00:00:00 2001 From: Robert Jambrecic Date: Thu, 26 Sep 2024 09:20:36 +0200 Subject: [PATCH 1/5] wip --- google_ads/model.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/google_ads/model.py b/google_ads/model.py index 47b8a860..9656ed4e 100644 --- a/google_ads/model.py +++ b/google_ads/model.py @@ -215,3 +215,9 @@ class CampaignSharedSet(BaseModel): customer_id: str campaign_id: str shared_set_name: str + + +class PageFeedItems(BaseModel): + login_customer_id: Optional[str] = None + customer_id: str + asset_set_resource_name: str From a08c49dccf39ae75ab290da90a7bdb80dc6c81fa Mon Sep 17 00:00:00 2001 From: Robert Jambrecic Date: Thu, 26 Sep 2024 09:38:31 +0200 Subject: [PATCH 2/5] wip --- google_ads/application.py | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/google_ads/application.py b/google_ads/application.py index bc37c1ed..ed5286a2 100644 --- a/google_ads/application.py +++ b/google_ads/application.py @@ -33,6 +33,7 @@ ExistingCampaignSitelinks, GeoTargetCriterion, NewCampaignSitelinks, + PageFeedItems, RemoveResource, ) @@ -1953,3 +1954,91 @@ async def add_shared_set_to_campaign( raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) from e + + +def _add_assets_to_asset_set( + client: Any, + customer_id: str, + asset_resource_names: List[str], + asset_set_resource_name: str, +) -> str: + """Adds assets to an asset set by creating an asset set asset link. + + Args: + client: an initialized GoogleAdsClient instance. + customer_id: a client customer ID. + asset_resource_names: a list of asset resource names. + asset_set_resource_name: a resource name for an asset set. + """ + operations = [] + for resource_name in asset_resource_names: + # Creates an asset set asset operation and adds it to the list of + # operations. + operation = client.get_type("AssetSetAssetOperation") + asset_set_asset = operation.create + asset_set_asset.asset = resource_name + asset_set_asset.asset_set = asset_set_resource_name + operations.append(operation) + + # Issues a mutate request to add the asset set assets and prints its + # information. + asset_set_asset_service = client.get_service("AssetSetAssetService") + response = asset_set_asset_service.mutate_asset_set_assets( + customer_id=customer_id, operations=operations + ) + + print(f"Added {len(response.results)} asset set assets:") + + return_text = "" + for result in response.results: + return_text += f"Created an asset set asset link with resource name '{result.resource_name}'\n" + return return_text + + +@router.post("/add-item-to-page-feed") +async def add_items_to_page_feed( + user_id: int, + model: PageFeedItems, +) -> str: + client = await _get_client( + user_id=user_id, login_customer_id=model.login_customer_id + ) + + urls = [ + "http://www.example.com/discounts/rental-cars", + "http://www.example.com/discounts/hotel-deals", + "http://www.example.com/discounts/flight-deals", + ] + operations = [] + + # Creates one asset per URL. + for url in urls: + # Creates an asset operation and adds it to the list of operations. + operation = client.get_type("AssetOperation") + asset = operation.create + page_feed_asset = asset.page_feed_asset + page_feed_asset.page_url = url + # Recommended: adds labels to the asset. These labels can be used later + # in ad group targeting to restrict the set of pages that can serve. + page_feed_asset.labels.append("TODO label") + operations.append(operation) + + # Issues a mutate request to add the assets and prints its information. + asset_service = client.get_service("AssetService") + response = asset_service.mutate_assets( + customer_id=model.customer_id, operations=operations + ) + + resource_names = [] + return_text = "" + for result in response.results: + resource_name = result.resource_name + return_text += f"Created an asset with resource name: '{resource_name}'\n" + resource_names.append(resource_name) + + return _add_assets_to_asset_set( + client=client, + customer_id=model.customer_id, + asset_resource_names=resource_names, + asset_set_resource_name=model.asset_set_resource_name, + ) From f3c950a0d60c1026c51ebe3612a7e7df22a4c0f6 Mon Sep 17 00:00:00 2001 From: Robert Jambrecic Date: Mon, 30 Sep 2024 11:11:09 +0200 Subject: [PATCH 3/5] Update add-items-to-page-feed endpoint --- google_ads/application.py | 15 ++++----------- google_ads/model.py | 1 + 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/google_ads/application.py b/google_ads/application.py index ed5286a2..7d942db2 100644 --- a/google_ads/application.py +++ b/google_ads/application.py @@ -1987,15 +1987,13 @@ def _add_assets_to_asset_set( customer_id=customer_id, operations=operations ) - print(f"Added {len(response.results)} asset set assets:") - return_text = "" for result in response.results: return_text += f"Created an asset set asset link with resource name '{result.resource_name}'\n" return return_text -@router.post("/add-item-to-page-feed") +@router.post("/add-items-to-page-feed") async def add_items_to_page_feed( user_id: int, model: PageFeedItems, @@ -2003,16 +2001,10 @@ async def add_items_to_page_feed( client = await _get_client( user_id=user_id, login_customer_id=model.login_customer_id ) - - urls = [ - "http://www.example.com/discounts/rental-cars", - "http://www.example.com/discounts/hotel-deals", - "http://www.example.com/discounts/flight-deals", - ] operations = [] # Creates one asset per URL. - for url in urls: + for url, label in model.urls_and_labels.items(): # Creates an asset operation and adds it to the list of operations. operation = client.get_type("AssetOperation") asset = operation.create @@ -2020,7 +2012,8 @@ async def add_items_to_page_feed( page_feed_asset.page_url = url # Recommended: adds labels to the asset. These labels can be used later # in ad group targeting to restrict the set of pages that can serve. - page_feed_asset.labels.append("TODO label") + if label: + page_feed_asset.labels.append(label) operations.append(operation) # Issues a mutate request to add the assets and prints its information. diff --git a/google_ads/model.py b/google_ads/model.py index 9656ed4e..50a85488 100644 --- a/google_ads/model.py +++ b/google_ads/model.py @@ -221,3 +221,4 @@ class PageFeedItems(BaseModel): login_customer_id: Optional[str] = None customer_id: str asset_set_resource_name: str + urls_and_labels: Dict[str, Optional[str]] From 67fa79cb3e555f563afbee925c88aa9d37fd0276 Mon Sep 17 00:00:00 2001 From: Robert Jambrecic Date: Mon, 30 Sep 2024 13:16:48 +0200 Subject: [PATCH 4/5] Implement list-page-feed-items --- .../tools/_gbb_google_sheets_team_tools.py | 10 ++--- captn/google_ads/client.py | 8 ++-- google_ads/application.py | 39 +++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/captn/captn_agents/backend/tools/_gbb_google_sheets_team_tools.py b/captn/captn_agents/backend/tools/_gbb_google_sheets_team_tools.py index b2e2c59f..7245108a 100644 --- a/captn/captn_agents/backend/tools/_gbb_google_sheets_team_tools.py +++ b/captn/captn_agents/backend/tools/_gbb_google_sheets_team_tools.py @@ -30,7 +30,7 @@ from ....google_ads.client import ( execute_query, google_ads_create_update, - google_ads_post, + google_ads_post_or_get, ) from ....google_ads.client import ( list_accessible_customers_with_account_types as list_accessible_customers_with_account_types_client, @@ -296,7 +296,7 @@ def _add_negative_campaign_keywords_lists( campaign_id=campaign_id, shared_set_name=row["keyword"], ) - google_ads_post( + google_ads_post_or_get( user_id=context.user_id, conv_id=context.conv_id, recommended_modifications_and_answer_list=context.recommended_modifications_and_answer_list, @@ -505,7 +505,7 @@ def _update_callouts( callouts=callouts, ) - response = google_ads_post( + response = google_ads_post_or_get( user_id=context.user_id, conv_id=context.conv_id, recommended_modifications_and_answer_list=context.recommended_modifications_and_answer_list, @@ -636,7 +636,7 @@ def _add_existing_sitelinks( sitelink_ids=sitelink_ids, ) - google_ads_post( + google_ads_post_or_get( user_id=context.user_id, conv_id=context.conv_id, recommended_modifications_and_answer_list=context.recommended_modifications_and_answer_list, @@ -687,7 +687,7 @@ def _add_new_sitelinks( site_links=site_links, ) - google_ads_post( + google_ads_post_or_get( user_id=context.user_id, conv_id=context.conv_id, recommended_modifications_and_answer_list=context.recommended_modifications_and_answer_list, diff --git a/captn/google_ads/client.py b/captn/google_ads/client.py index d217924e..8b131786 100644 --- a/captn/google_ads/client.py +++ b/captn/google_ads/client.py @@ -1,6 +1,6 @@ import json from os import environ -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Literal, Optional, Tuple, Union from pydantic import BaseModel from requests import get as requests_get @@ -351,7 +351,7 @@ def google_ads_create_update( return response_dict -def google_ads_post( +def google_ads_post_or_get( user_id: int, conv_id: int, model: BaseModel, @@ -360,6 +360,7 @@ def google_ads_post( ], endpoint: str, already_checked_clients_approval: bool = False, + requests_method: Literal["get", "post"] = "post", ) -> Union[Dict[str, Any], str]: login_url_response = _check_for_approval_and_get_login_url( user_id=user_id, @@ -377,7 +378,8 @@ def google_ads_post( body = model.model_dump() - response = requests_post( + requests_methods = {"get": requests_get, "post": requests_post} + response = requests_methods[requests_method]( # type: ignore[operator] f"{BASE_URL}{endpoint}", json=body, params=params, timeout=60 ) diff --git a/google_ads/application.py b/google_ads/application.py index 7584b642..d0d83f5b 100644 --- a/google_ads/application.py +++ b/google_ads/application.py @@ -2037,3 +2037,42 @@ async def add_items_to_page_feed( asset_resource_names=resource_names, asset_set_resource_name=model.asset_set_resource_name, ) + + +@router.get("/list-page-feed-items") +async def list_page_feed_items( + user_id: int, + model: PageFeedItems, +) -> Dict[str, Any]: + # client = await _get_client( + # user_id=user_id, login_customer_id=model.login_customer_id + # ) + query = f""" +SELECT +# asset_set_asset.asset, +# asset_set_asset.asset_set, + asset.id, + asset.name, + asset.type, + asset.page_feed_asset.page_url +FROM + asset_set_asset +WHERE + asset.type = 'PAGE_FEED' + AND asset_set_asset.asset_set = '{model.asset_set_resource_name}' + AND asset_set_asset.status != 'REMOVED' +# AND asset.page_feed_asset.page_url IN ({", ".join([f'"{url}"' for url in model.urls_and_labels.keys()])}) +""" # nosec: [B608] + + page_feed_assets_response = await search( + user_id=user_id, + customer_ids=[model.customer_id], + query=query, + login_customer_id=model.login_customer_id, + ) + + # page_urls = [] + # for asset in page_feed_assets_response[model.customer_id]: + # page_urls.append(asset["asset"]["pageFeedAsset"]["pageUrl"]) + + return page_feed_assets_response From 91ceefc884e24b96b40fc2c3cf3a150350352599 Mon Sep 17 00:00:00 2001 From: Robert Jambrecic Date: Tue, 1 Oct 2024 11:04:47 +0200 Subject: [PATCH 5/5] Page feed items deletion enabled --- google_ads/application.py | 10 ++++++++-- google_ads/model.py | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/google_ads/application.py b/google_ads/application.py index d0d83f5b..511b2e7b 100644 --- a/google_ads/application.py +++ b/google_ads/application.py @@ -21,6 +21,7 @@ from .model import ( AdBase, AdCopy, + AddPageFeedItems, AdGroup, AdGroupAd, AdGroupCriterion, @@ -932,6 +933,12 @@ def _create_ad_group_ad_set_attr( "setattr_create_func": _keywords_setattr, "setattr_update_func": _set_fields, }, + "asset_set_asset": { + "service": "AssetSetAssetService", + "operation": "AssetSetAssetOperation", + "mutate": "mutate_asset_set_assets", + "service_path_update_delete": "asset_set_asset_path", + }, } @@ -1998,7 +2005,7 @@ def _add_assets_to_asset_set( @router.post("/add-items-to-page-feed") async def add_items_to_page_feed( user_id: int, - model: PageFeedItems, + model: AddPageFeedItems, ) -> str: client = await _get_client( user_id=user_id, login_customer_id=model.login_customer_id @@ -2061,7 +2068,6 @@ async def list_page_feed_items( asset.type = 'PAGE_FEED' AND asset_set_asset.asset_set = '{model.asset_set_resource_name}' AND asset_set_asset.status != 'REMOVED' -# AND asset.page_feed_asset.page_url IN ({", ".join([f'"{url}"' for url in model.urls_and_labels.keys()])}) """ # nosec: [B608] page_feed_assets_response = await search( diff --git a/google_ads/model.py b/google_ads/model.py index 50a85488..44eedb71 100644 --- a/google_ads/model.py +++ b/google_ads/model.py @@ -146,6 +146,7 @@ class RemoveResource(BaseModel): "ad", "ad_group_criterion", "campaign_criterion", + "asset_set_asset", ] @@ -221,4 +222,7 @@ class PageFeedItems(BaseModel): login_customer_id: Optional[str] = None customer_id: str asset_set_resource_name: str + + +class AddPageFeedItems(PageFeedItems): urls_and_labels: Dict[str, Optional[str]]