diff --git a/api_examples/add_campaign_with_date_times.py b/api_examples/add_campaign_with_date_times.py new file mode 100644 index 0000000..ceb797b --- /dev/null +++ b/api_examples/add_campaign_with_date_times.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This example demonstrates how to create a campaign with start and end date times. + +This feature was added in v23 of the Google Ads API. +""" + +import argparse +import sys +import uuid +from datetime import datetime, timedelta + +from google.ads.googleads.client import GoogleAdsClient +from google.ads.googleads.errors import GoogleAdsException + + +def main(client, customer_id): + """The main method that creates all necessary entities for the example. + + Args: + client: an initialized GoogleAdsClient instance. + customer_id: a client customer ID. + """ + campaign_budget_service = client.get_service("CampaignBudgetService") + campaign_service = client.get_service("CampaignService") + + # Create a budget, which is a required constraint when creating a campaign. + campaign_budget_operation = client.get_type("CampaignBudgetOperation") + campaign_budget = campaign_budget_operation.create + campaign_budget.name = f"Interplanetary Budget {uuid.uuid4()}" + campaign_budget.delivery_method = ( + client.enums.BudgetDeliveryMethodEnum.STANDARD + ) + campaign_budget.amount_micros = 500000 + + # Add budget. + try: + campaign_budget_response = campaign_budget_service.mutate_campaign_budgets( + customer_id=customer_id, operations=[campaign_budget_operation] + ) + except GoogleAdsException as ex: + _handle_google_ads_exception(ex) + + campaign_budget_resource_name = campaign_budget_response.results[0].resource_name + print( + f"Created campaign budget with resource name: '{campaign_budget_resource_name}'" + ) + + # Create campaign. + campaign_operation = client.get_type("CampaignOperation") + campaign = campaign_operation.create + campaign.name = f"Interplanetary Cruise Campaign {uuid.uuid4()}" + campaign.advertising_channel_type = ( + client.enums.AdvertisingChannelTypeEnum.SEARCH + ) + + # Recommendation: Set the campaign to PAUSED when creating it to prevent + # the ads from immediately serving. + campaign.status = client.enums.CampaignStatusEnum.PAUSED + + # Set the budget. + campaign.campaign_budget = campaign_budget_resource_name + + # Set the network settings. + campaign.network_settings.target_google_search = True + campaign.network_settings.target_search_network = True + campaign.network_settings.target_content_network = False + campaign.network_settings.target_partner_search_network = False + + # Optional: Set the start date time and end date time. + # Note: These fields are only available in v23 and later. + # The format must be 'yyyy-mm-dd hh:mm:ss'. + # We will set the start time to 1 day from now, and end time to 30 days from now. + now = datetime.now() + start_time = now + timedelta(days=1) + end_time = now + timedelta(days=31) + + campaign.start_date_time = start_time.strftime("%Y-%m-%d %H:%M:%S") + campaign.end_date_time = end_time.strftime("%Y-%m-%d %H:%M:%S") + + # Add the campaign. + try: + campaign_response = campaign_service.mutate_campaigns( + customer_id=customer_id, operations=[campaign_operation] + ) + print( + f"Created campaign with resource name: '{campaign_response.results[0].resource_name}'" + ) + print(f"Start date time: {campaign.start_date_time}") + print(f"End date time: {campaign.end_date_time}") + except GoogleAdsException as ex: + _handle_google_ads_exception(ex) + + +def _handle_google_ads_exception(exception): + """Prints the details of a GoogleAdsException object. + + Args: + exception: an instance of GoogleAdsException. + """ + print( + f"Request with ID '{exception.request_id}' failed with status " + f"'{exception.error.code().name}' and includes the following errors:" + ) + for error in exception.failure.errors: + print(f"\tError with message '{error.message}'.") + if error.location: + for field_path_element in error.location.field_path_elements: + print(f"\t\tOn field: {field_path_element.field_name}") + sys.exit(1) + + +if __name__ == "__main__": + # GoogleAdsClient will read the google-ads.yaml configuration file in the + # home directory if none is specified. + googleads_client = GoogleAdsClient.load_from_storage(version="v23") + + parser = argparse.ArgumentParser( + description="Creates a campaign with start and end date times." + ) + # The following argument(s) are required to run the example. + parser.add_argument( + "-c", + "--customer_id", + type=str, + required=True, + help="The Google Ads customer ID.", + ) + args = parser.parse_args() + + main(googleads_client, args.customer_id) diff --git a/api_examples/tests/test_add_campaign_with_date_times.py b/api_examples/tests/test_add_campaign_with_date_times.py new file mode 100644 index 0000000..5aa983f --- /dev/null +++ b/api_examples/tests/test_add_campaign_with_date_times.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from unittest.mock import MagicMock +import sys +import os + +# Add the parent directory to sys.path to import the example script +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# Import the main function from the example script +# We need to import it as a module to mock it properly +from api_examples import add_campaign_with_date_times + +class TestAddCampaignWithDateTimes(unittest.TestCase): + def setUp(self): + self.mock_client = MagicMock() + self.customer_id = "1234567890" + + def test_main(self): + # Mock services + mock_campaign_budget_service = MagicMock() + mock_campaign_service = MagicMock() + + def get_service_side_effect(service_name): + if service_name == "CampaignBudgetService": + return mock_campaign_budget_service + elif service_name == "CampaignService": + return mock_campaign_service + return MagicMock() + + self.mock_client.get_service.side_effect = get_service_side_effect + + # Mock types + mock_campaign_budget_operation = MagicMock() + mock_campaign_operation = MagicMock() + + def get_type_side_effect(type_name): + if type_name == "CampaignBudgetOperation": + return mock_campaign_budget_operation + elif type_name == "CampaignOperation": + return mock_campaign_operation + return MagicMock() + + self.mock_client.get_type.side_effect = get_type_side_effect + + # Mock Enums + self.mock_client.enums.BudgetDeliveryMethodEnum.STANDARD = "STANDARD" + self.mock_client.enums.AdvertisingChannelTypeEnum.SEARCH = "SEARCH" + self.mock_client.enums.CampaignStatusEnum.PAUSED = "PAUSED" + + # Mock responses + mock_budget_response = MagicMock() + mock_budget_response.results = [MagicMock(resource_name="budget_resource_name")] + mock_campaign_budget_service.mutate_campaign_budgets.return_value = mock_budget_response + + mock_campaign_response = MagicMock() + mock_campaign_response.results = [MagicMock(resource_name="campaign_resource_name")] + mock_campaign_service.mutate_campaigns.return_value = mock_campaign_response + + # Run main + add_campaign_with_date_times.main(self.mock_client, self.customer_id) + + # Asserts + mock_campaign_budget_service.mutate_campaign_budgets.assert_called_once() + mock_campaign_service.mutate_campaigns.assert_called_once() + + # Check if created campaign has start_date_time and end_date_time set + created_campaign = mock_campaign_operation.create + self.assertTrue(hasattr(created_campaign, "start_date_time")) + self.assertTrue(hasattr(created_campaign, "end_date_time")) + self.assertIsNotNone(created_campaign.start_date_time) + self.assertIsNotNone(created_campaign.end_date_time) + +if __name__ == "__main__": + unittest.main()