Skip to content

Commit

Permalink
Updated campaign geo targeting. create_geo_targeting_for_campaign end…
Browse files Browse the repository at this point in the history
…point can now automatically add all suggested locations (#880)
  • Loading branch information
rjambrecic committed Aug 13, 2024
1 parent 19e52e6 commit fbf1b99
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 27 deletions.
65 changes: 52 additions & 13 deletions google_ads/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -1410,9 +1410,9 @@ async def add_keywords_to_ad_group(
)


def _get_geo_target_constant_by_names(
client: GoogleAdsClient, location_names: List[str]
) -> str:
def _get_geo_target_constant_suggestions(
client: GoogleAdsClient, location_names: List[str], target_type: Optional[str]
) -> Any:
gtc_service = client.get_service("GeoTargetConstantService")
gtc_request = client.get_type("SuggestGeoTargetConstantsRequest")

Expand All @@ -1421,11 +1421,40 @@ def _get_geo_target_constant_by_names(

results = gtc_service.suggest_geo_target_constants(gtc_request)

geo_target_constant_suggestions = results.geo_target_constant_suggestions

# filter by target type
if target_type:
geo_target_constant_suggestions = [
suggestion
for suggestion in geo_target_constant_suggestions
if suggestion.geo_target_constant.target_type == target_type
]

return geo_target_constant_suggestions


def _get_geo_target_constant_by_names(
client: GoogleAdsClient,
location_names: List[str],
target_type: Optional[str],
add_all_suggestions: Optional[bool],
) -> Union[str | List[str]]:
geo_target_constant_suggestions = _get_geo_target_constant_suggestions(
client=client, location_names=location_names, target_type=target_type
)

if add_all_suggestions:
return [
suggestion.geo_target_constant.id
for suggestion in geo_target_constant_suggestions
]

return_text = (
"Below is a list of possible locations in the following format '(name, country_code, target_type)'."
"Please send them to the client as smart suggestions with type 'manyOf' (do not display the location_id to him):\n\n"
)
for suggestion in results.geo_target_constant_suggestions:
for suggestion in geo_target_constant_suggestions:
geo_target_constant = suggestion.geo_target_constant
text = (
f"location_id: {geo_target_constant.id}, "
Expand Down Expand Up @@ -1511,18 +1540,28 @@ async def create_geo_targeting_for_campaign(
client = await _get_client(user_id=user_id, login_customer_id=login_customer_id)

if location_ids is None:
return _get_geo_target_constant_by_names(
suggestions_or_ids = _get_geo_target_constant_by_names(
client=client,
location_names=location_names, # type: ignore[arg-type]
target_type=model.target_type,
add_all_suggestions=model.add_all_suggestions,
)

return _create_locations_by_ids_to_campaign(
client=client,
customer_id=model.customer_id, # type: ignore
campaign_id=model.campaign_id, # type: ignore
location_ids=location_ids,
negative=model.negative,
)
if isinstance(suggestions_or_ids, str):
return suggestions_or_ids
else:
location_ids = suggestions_or_ids
try:
return _create_locations_by_ids_to_campaign(
client=client,
customer_id=model.customer_id, # type: ignore
campaign_id=model.campaign_id, # type: ignore
location_ids=location_ids,
negative=model.negative,
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
) from e


@router.get("/remove-google-ads-resource")
Expand Down
2 changes: 2 additions & 0 deletions google_ads/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,5 @@ class GeoTargetCriterion(BaseModel):
location_ids: Optional[List[str]] = Field(Query(default=None))
location_names: Optional[List[str]] = Field(Query(default=None))
negative: Optional[bool] = None
target_type: Optional[Literal["Country", "County", "City", "Region"]] = None
add_all_suggestions: Optional[bool] = None
46 changes: 32 additions & 14 deletions tests/ci/google_ads/test_application.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from unittest.mock import MagicMock

import pytest
from fastapi import HTTPException, status
Expand Down Expand Up @@ -33,29 +34,46 @@ async def test_add_geo_targeting_to_campaign_raises_exception_if_location_names_


@pytest.mark.asyncio
async def test_add_geo_targeting_to_campaign_raises_exception_if_location_ids_are_none() -> (
None
):
async def test_add_geo_targeting_to_campaign_success() -> None:
geo_target = GeoTargetCriterion(
customer_id="123",
campaign_id="456",
location_names=["New York"],
location_ids=None,
add_all_suggestions=True,
)

with unittest.mock.patch(
"google_ads.application._get_geo_target_constant_by_names",
) as mock_get_geo_target_constant_by_names:
with unittest.mock.patch(
suggestions = [
MagicMock(geo_target_constant=MagicMock(id="123")),
MagicMock(geo_target_constant=MagicMock(id="345")),
]

with (
unittest.mock.patch(
"google_ads.application._get_client",
) as mock_get_client:
mock_get_geo_target_constant_by_names.return_value = None
mock_get_client.return_value = None
return_value=None,
),
unittest.mock.patch(
"google_ads.application._get_geo_target_constant_suggestions",
return_value=suggestions,
) as mock_get_geo_target_constant_suggestions,
unittest.mock.patch(
"google_ads.application._create_locations_by_ids_to_campaign",
return_value="Created",
) as mock_create_locations_by_ids_to_campaign,
):
await create_geo_targeting_for_campaign(user_id=-1, model=geo_target)
mock_get_geo_target_constant_suggestions.assert_called_once_with(
client=None, location_names=["New York"], target_type=None
)

await create_geo_targeting_for_campaign(user_id=-1, model=geo_target)
mock_get_geo_target_constant_by_names.assert_called_once_with(
client=None, location_names=["New York"]
)
mock_create_locations_by_ids_to_campaign.assert_called_once_with(
client=None,
customer_id="123",
campaign_id="456",
location_ids=["123", "345"],
negative=None,
)


@pytest.mark.asyncio
Expand Down

0 comments on commit fbf1b99

Please sign in to comment.