Skip to content

Commit a6e610f

Browse files
authored
Merge pull request #20 from googleads/v23_fix
Add campaign with start_date_time and end_date_time.
2 parents 6535210 + 05b5dd6 commit a6e610f

2 files changed

Lines changed: 233 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""This example demonstrates how to create a campaign with start and end date times.
17+
18+
This feature was added in v23 of the Google Ads API.
19+
"""
20+
21+
import argparse
22+
import sys
23+
import uuid
24+
from datetime import datetime, timedelta
25+
26+
from google.ads.googleads.client import GoogleAdsClient
27+
from google.ads.googleads.errors import GoogleAdsException
28+
29+
30+
def main(client, customer_id):
31+
"""The main method that creates all necessary entities for the example.
32+
33+
Args:
34+
client: an initialized GoogleAdsClient instance.
35+
customer_id: a client customer ID.
36+
"""
37+
campaign_budget_service = client.get_service("CampaignBudgetService")
38+
campaign_service = client.get_service("CampaignService")
39+
40+
# Create a budget, which is a required constraint when creating a campaign.
41+
campaign_budget_operation = client.get_type("CampaignBudgetOperation")
42+
campaign_budget = campaign_budget_operation.create
43+
campaign_budget.name = f"Interplanetary Budget {uuid.uuid4()}"
44+
campaign_budget.delivery_method = (
45+
client.enums.BudgetDeliveryMethodEnum.STANDARD
46+
)
47+
campaign_budget.amount_micros = 500000
48+
49+
# Add budget.
50+
try:
51+
campaign_budget_response = campaign_budget_service.mutate_campaign_budgets(
52+
customer_id=customer_id, operations=[campaign_budget_operation]
53+
)
54+
except GoogleAdsException as ex:
55+
_handle_google_ads_exception(ex)
56+
57+
campaign_budget_resource_name = campaign_budget_response.results[0].resource_name
58+
print(
59+
f"Created campaign budget with resource name: '{campaign_budget_resource_name}'"
60+
)
61+
62+
# Create campaign.
63+
campaign_operation = client.get_type("CampaignOperation")
64+
campaign = campaign_operation.create
65+
campaign.name = f"Interplanetary Cruise Campaign {uuid.uuid4()}"
66+
campaign.advertising_channel_type = (
67+
client.enums.AdvertisingChannelTypeEnum.SEARCH
68+
)
69+
70+
# Recommendation: Set the campaign to PAUSED when creating it to prevent
71+
# the ads from immediately serving.
72+
campaign.status = client.enums.CampaignStatusEnum.PAUSED
73+
74+
# Set the budget.
75+
campaign.campaign_budget = campaign_budget_resource_name
76+
77+
# Set the network settings.
78+
campaign.network_settings.target_google_search = True
79+
campaign.network_settings.target_search_network = True
80+
campaign.network_settings.target_content_network = False
81+
campaign.network_settings.target_partner_search_network = False
82+
83+
# Optional: Set the start date time and end date time.
84+
# Note: These fields are only available in v23 and later.
85+
# The format must be 'yyyy-mm-dd hh:mm:ss'.
86+
# We will set the start time to 1 day from now, and end time to 30 days from now.
87+
now = datetime.now()
88+
start_time = now + timedelta(days=1)
89+
end_time = now + timedelta(days=31)
90+
91+
campaign.start_date_time = start_time.strftime("%Y-%m-%d %H:%M:%S")
92+
campaign.end_date_time = end_time.strftime("%Y-%m-%d %H:%M:%S")
93+
94+
# Add the campaign.
95+
try:
96+
campaign_response = campaign_service.mutate_campaigns(
97+
customer_id=customer_id, operations=[campaign_operation]
98+
)
99+
print(
100+
f"Created campaign with resource name: '{campaign_response.results[0].resource_name}'"
101+
)
102+
print(f"Start date time: {campaign.start_date_time}")
103+
print(f"End date time: {campaign.end_date_time}")
104+
except GoogleAdsException as ex:
105+
_handle_google_ads_exception(ex)
106+
107+
108+
def _handle_google_ads_exception(exception):
109+
"""Prints the details of a GoogleAdsException object.
110+
111+
Args:
112+
exception: an instance of GoogleAdsException.
113+
"""
114+
print(
115+
f"Request with ID '{exception.request_id}' failed with status "
116+
f"'{exception.error.code().name}' and includes the following errors:"
117+
)
118+
for error in exception.failure.errors:
119+
print(f"\tError with message '{error.message}'.")
120+
if error.location:
121+
for field_path_element in error.location.field_path_elements:
122+
print(f"\t\tOn field: {field_path_element.field_name}")
123+
sys.exit(1)
124+
125+
126+
if __name__ == "__main__":
127+
# GoogleAdsClient will read the google-ads.yaml configuration file in the
128+
# home directory if none is specified.
129+
googleads_client = GoogleAdsClient.load_from_storage(version="v23")
130+
131+
parser = argparse.ArgumentParser(
132+
description="Creates a campaign with start and end date times."
133+
)
134+
# The following argument(s) are required to run the example.
135+
parser.add_argument(
136+
"-c",
137+
"--customer_id",
138+
type=str,
139+
required=True,
140+
help="The Google Ads customer ID.",
141+
)
142+
args = parser.parse_args()
143+
144+
main(googleads_client, args.customer_id)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import unittest
17+
from unittest.mock import MagicMock
18+
import sys
19+
import os
20+
21+
# Add the parent directory to sys.path to import the example script
22+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
23+
24+
# Import the main function from the example script
25+
# We need to import it as a module to mock it properly
26+
from api_examples import add_campaign_with_date_times
27+
28+
class TestAddCampaignWithDateTimes(unittest.TestCase):
29+
def setUp(self):
30+
self.mock_client = MagicMock()
31+
self.customer_id = "1234567890"
32+
33+
def test_main(self):
34+
# Mock services
35+
mock_campaign_budget_service = MagicMock()
36+
mock_campaign_service = MagicMock()
37+
38+
def get_service_side_effect(service_name):
39+
if service_name == "CampaignBudgetService":
40+
return mock_campaign_budget_service
41+
elif service_name == "CampaignService":
42+
return mock_campaign_service
43+
return MagicMock()
44+
45+
self.mock_client.get_service.side_effect = get_service_side_effect
46+
47+
# Mock types
48+
mock_campaign_budget_operation = MagicMock()
49+
mock_campaign_operation = MagicMock()
50+
51+
def get_type_side_effect(type_name):
52+
if type_name == "CampaignBudgetOperation":
53+
return mock_campaign_budget_operation
54+
elif type_name == "CampaignOperation":
55+
return mock_campaign_operation
56+
return MagicMock()
57+
58+
self.mock_client.get_type.side_effect = get_type_side_effect
59+
60+
# Mock Enums
61+
self.mock_client.enums.BudgetDeliveryMethodEnum.STANDARD = "STANDARD"
62+
self.mock_client.enums.AdvertisingChannelTypeEnum.SEARCH = "SEARCH"
63+
self.mock_client.enums.CampaignStatusEnum.PAUSED = "PAUSED"
64+
65+
# Mock responses
66+
mock_budget_response = MagicMock()
67+
mock_budget_response.results = [MagicMock(resource_name="budget_resource_name")]
68+
mock_campaign_budget_service.mutate_campaign_budgets.return_value = mock_budget_response
69+
70+
mock_campaign_response = MagicMock()
71+
mock_campaign_response.results = [MagicMock(resource_name="campaign_resource_name")]
72+
mock_campaign_service.mutate_campaigns.return_value = mock_campaign_response
73+
74+
# Run main
75+
add_campaign_with_date_times.main(self.mock_client, self.customer_id)
76+
77+
# Asserts
78+
mock_campaign_budget_service.mutate_campaign_budgets.assert_called_once()
79+
mock_campaign_service.mutate_campaigns.assert_called_once()
80+
81+
# Check if created campaign has start_date_time and end_date_time set
82+
created_campaign = mock_campaign_operation.create
83+
self.assertTrue(hasattr(created_campaign, "start_date_time"))
84+
self.assertTrue(hasattr(created_campaign, "end_date_time"))
85+
self.assertIsNotNone(created_campaign.start_date_time)
86+
self.assertIsNotNone(created_campaign.end_date_time)
87+
88+
if __name__ == "__main__":
89+
unittest.main()

0 commit comments

Comments
 (0)