Skip to content

Commit 18c16f8

Browse files
authored
Add update_flights_feed_item_string_attribute_value example (#371)
1 parent 2ac6d39 commit 18c16f8

File tree

1 file changed

+338
-0
lines changed

1 file changed

+338
-0
lines changed
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
#!/usr/bin/env python
2+
# Copyright 2020 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+
"""Updates a feed item attribute value in a flights feed.
16+
17+
To create a flights feed, run the remarketing/add_flights_feed.py example.
18+
This example is specific to feeds of type DYNAMIC_FLIGHT. The attribute you are
19+
updating must be present on the feed.
20+
21+
This example is specifically for updating the string attribute of a flights feed
22+
item, but it can also be changed to work with other data types of an attribute
23+
and feed types.
24+
25+
To make this work with other data types, replace `string_value` with the type of
26+
an attribute you wish to update, when creating a FeedItemAttributeValue instance
27+
in this example. To make this work with other feed types, replace the
28+
FlightPlaceholderField enum with the equivalent one of your feed type, and
29+
replace Feeds::flightPlaceholderFieldsMapFor() with the method that can return
30+
a similar value for your feed type. Check the flightPlaceholderFieldsMapFor()
31+
method for details.
32+
"""
33+
34+
import argparse
35+
import sys
36+
37+
from google.ads.google_ads.client import GoogleAdsClient
38+
from google.ads.google_ads.errors import GoogleAdsException
39+
from google.ads.google_ads.util import ResourceName
40+
from google.api_core import protobuf_helpers
41+
42+
43+
def main(
44+
client,
45+
customer_id,
46+
feed_id,
47+
feed_item_id,
48+
flight_placeholder_field_name,
49+
attribute_value,
50+
):
51+
"""The main method that creates all necessary entities for the example.
52+
53+
Args:
54+
client: an initialized GoogleAdsClient instance
55+
customer_id: a client customer ID
56+
feed_id: the ID of feed containing the feed item to be updated
57+
feed_item_id: The ID of the feed item to be updated
58+
flight_placeholder_field_name: the flight placeholder field name for
59+
the attribute to be updated
60+
attribute_value: the new value to set the feed attribute to
61+
"""
62+
try:
63+
feed_service = client.get_service("FeedService", version="v6")
64+
# Gets a map of the placeholder values to feed attributes.
65+
placeholders_to_feed_attribute_map = _flight_placeholder_fields_map(
66+
client, customer_id, feed_service.feed_path(customer_id, feed_id)
67+
)
68+
# Gets the ID of the feed attribute for the placeholder field. This is
69+
# needed to specify which feed item attribute value will be updated in
70+
# the given feed item.
71+
flight_placeholder_field_enum = client.get_type(
72+
"FlightPlaceholderFieldEnum", version="v6"
73+
)
74+
flight_placeholder_enum_value = getattr(
75+
flight_placeholder_field_enum, flight_placeholder_field_name
76+
)
77+
attribute_id = placeholders_to_feed_attribute_map[
78+
flight_placeholder_enum_value
79+
].id
80+
81+
# Creates the updated feed item attribute value.
82+
updated_feed_item_attribute_value = client.get_type(
83+
"FeedItemAttributeValue", version="v6"
84+
)
85+
updated_feed_item_attribute_value.feed_attribute_id = attribute_id
86+
updated_feed_item_attribute_value.string_value = attribute_value
87+
88+
# Retrieves the feed item and its associated attributes based on the
89+
# resource name.
90+
feed_item_service = client.get_service("FeedItemService", version="v6")
91+
feed_item = _get_feed_item(
92+
client,
93+
customer_id,
94+
feed_item_service.feed_item_path(
95+
customer_id,
96+
ResourceName.format_composite(feed_id, feed_item_id),
97+
),
98+
)
99+
100+
# Gets the index of the attribute value that will be updated in the
101+
# feed item.
102+
attribute_index = _get_attribute_index(
103+
updated_feed_item_attribute_value, feed_item
104+
)
105+
# Any feed item attribute values that are not included in the updated
106+
# feed item will be removed from the feed item, which is why you must
107+
# create the feed item from the existing feed item and its attribute
108+
# values. Then, update only the attribute that you want.
109+
feed_item_operation = client.get_type("FeedItemOperation", version="v6")
110+
updated_feed_item = feed_item_operation.update
111+
updated_feed_item.CopyFrom(feed_item)
112+
updated_feed_item.attribute_values[attribute_index].CopyFrom(
113+
updated_feed_item_attribute_value
114+
)
115+
116+
# Create a field mask using the old feed_item and the updated_feed_item.
117+
field_mask = protobuf_helpers.field_mask(feed_item, updated_feed_item)
118+
feed_item_operation.update_mask.CopyFrom(field_mask)
119+
120+
response = feed_item_service.mutate_feed_items(
121+
customer_id, [feed_item_operation]
122+
)
123+
print(
124+
"Feed item with resource name: "
125+
f"'{response.results[0].resource_name}' was updated."
126+
)
127+
except GoogleAdsException as ex:
128+
print(
129+
f'Request with ID "{ex.request_id}" failed with status '
130+
f'"{ex.error.code().name}" and includes the following errors:'
131+
)
132+
for error in ex.failure.errors:
133+
print(f'\tError with message "{error.message}".')
134+
if error.location:
135+
for field_path_element in error.location.field_path_elements:
136+
print(f"\t\tOn field: {field_path_element.field_name}")
137+
sys.exit(1)
138+
139+
140+
def _flight_placeholder_fields_map(client, customer_id, feed_resource_name):
141+
"""Maps place holder fields and feed attributes for a flights feed.
142+
143+
See FlightPlaceholderField.php for all available placeholder field values.
144+
145+
Args:
146+
client: an initialized GoogleAdsClient instance
147+
customer_id: a client customer ID
148+
feed_resource_name: a resource name for a Feed
149+
150+
Returns:
151+
a dict mapping placeholder fields to feed attributes
152+
"""
153+
flight_placeholder_field_enum = client.get_type(
154+
"FlightPlaceholderFieldEnum", version="v6"
155+
)
156+
157+
return _placeholder_field_maps(
158+
client,
159+
customer_id,
160+
feed_resource_name,
161+
{
162+
"Flight Description": flight_placeholder_field_enum.FLIGHT_DESCRIPTION,
163+
"Destination ID": flight_placeholder_field_enum.DESTINATION_ID,
164+
"Flight Price": flight_placeholder_field_enum.FLIGHT_PRICE,
165+
"Flight Sale Price": flight_placeholder_field_enum.FLIGHT_SALE_PRICE,
166+
"Final URLs": flight_placeholder_field_enum.FINAL_URLS,
167+
},
168+
)
169+
170+
171+
def _placeholder_field_maps(
172+
client, customer_id, feed_resource_name, feed_attribute_names_map
173+
):
174+
"""Retrieves the placeholder fields to feed attributes map for a feed.
175+
176+
The initial query retrieves the feed attributes, or columns, of the feed.
177+
Each feed attribute will also include the feed attribute ID, which will be
178+
used in a subsequent step.
179+
180+
Then a map is created for the feed attributes (columns) and returned:
181+
- The keys are the placeholder types that the columns will be.
182+
- The values are the feed attributes.
183+
184+
Args:
185+
client: an initialized GoogleAdsClient instance
186+
customer_id: a client customer ID
187+
feed_resource_name: a resource name for a Feed
188+
feed_attribute_names_map: the associative array mapping from feed
189+
attribute names to placeholder fields
190+
191+
Returns:
192+
a dict mapping placeholder fields to feed attributes
193+
"""
194+
google_ads_service = client.get_service("GoogleAdsService", version="v6")
195+
# Constructs the query to get the feed attributes for the specified feed
196+
# resource name.
197+
query = f"""
198+
SELECT
199+
feed.attributes
200+
FROM feed
201+
WHERE feed.resource_name = '{feed_resource_name}'"""
202+
# Issues a search request. The page_size is set to 1 because we're only
203+
# requesting a single result.
204+
response = google_ads_service.search(customer_id, query, page_size=1)
205+
row = list(response)[0]
206+
# Gets the attributes list from the feed and creates a map with keys of
207+
# placeholder fields and values of feed attributes.
208+
feed_attributes = row.feed.attributes
209+
# Creates map with keys of placeholder fields and values of feed
210+
# attributes.
211+
return {
212+
feed_attribute_names_map[feed_attribute.name]: feed_attribute
213+
for feed_attribute in feed_attributes
214+
}
215+
216+
217+
def _get_feed_item(client, customer_id, feed_item_resource_name):
218+
"""Retrieves a feed item and its attribute values given a resource name.
219+
220+
Args:
221+
client: an initialized GoogleAdsClient instance
222+
customer_id: a client customer ID
223+
feed_resource_name: a resource name for a FeedItem
224+
225+
Returns:
226+
a FeedItem instance
227+
"""
228+
google_ads_service = client.get_service("GoogleAdsService", version="v6")
229+
# Constructs the query to get the feed item with attribute values.
230+
query = f"""
231+
SELECT
232+
feed_item.attribute_values
233+
FROM feed_item
234+
WHERE feed_item.resource_name = '{feed_item_resource_name}'"""
235+
236+
response = google_ads_service.search(customer_id, query, page_size=1)
237+
238+
# Returns the feed item attribute values, which belongs to the first item.
239+
# We can ensure it belongs to the first one because we specified the feed
240+
# item resource name in the query.
241+
return list(response)[0].feed_item
242+
243+
244+
def _get_attribute_index(target_feed_item_attribute_value, feed_item):
245+
"""Gets the index of the target feed item attribute value.
246+
247+
This is needed to specify which feed item attribute value will be updated
248+
in the given feed item.
249+
250+
Args:
251+
target_feed_item_attribute_value: the new feed item attribute value that
252+
will be updated
253+
feed_item: the feed item that will be updated. It should be populated
254+
with the current attribute values
255+
256+
Returns:
257+
the index number of the attribute
258+
"""
259+
attribute_index = -1
260+
261+
# Loops through attribute values to find the index of the feed item
262+
# attribute value to update.
263+
for feed_item_attribute_value in feed_item.attribute_values:
264+
attribute_index += 1
265+
# Checks if the current feedItemAttributeValue is the one we are
266+
# updating.
267+
if (
268+
feed_item_attribute_value.feed_attribute_id
269+
== target_feed_item_attribute_value.feed_attribute_id
270+
):
271+
break
272+
273+
if attribute_index == -1:
274+
raise ValueError(
275+
"No matching feed attribute for feed item attribute "
276+
f"ID: {feed_item_attribute_value.feed_attribute_id}"
277+
)
278+
279+
return attribute_index
280+
281+
282+
if __name__ == "__main__":
283+
# GoogleAdsClient will read the google-ads.yaml configuration file in the
284+
# home directory if none is specified.
285+
google_ads_client = GoogleAdsClient.load_from_storage()
286+
287+
parser = argparse.ArgumentParser(
288+
description="Updates a feed item attribute value in a flights feed."
289+
)
290+
# The following argument(s) should be provided to run the example.
291+
parser.add_argument(
292+
"-c",
293+
"--customer_id",
294+
type=str,
295+
required=True,
296+
help="The Google Ads customer ID.",
297+
)
298+
parser.add_argument(
299+
"-f",
300+
"--feed_id",
301+
type=str,
302+
required=True,
303+
help="The ID of feed containing the feed item to be updated.",
304+
)
305+
parser.add_argument(
306+
"-i",
307+
"--feed_item_id",
308+
type=str,
309+
required=True,
310+
help="The ID of the feed item to be updated.",
311+
)
312+
parser.add_argument(
313+
"-p",
314+
"--flight_placeholder_field_name",
315+
type=str,
316+
required=True,
317+
help=(
318+
"The flight placeholder field name for the attribute to be "
319+
"updated.",
320+
),
321+
)
322+
parser.add_argument(
323+
"-a",
324+
"--attribute_value",
325+
type=str,
326+
required=True,
327+
help="The new value to set the feed attribute to.",
328+
)
329+
args = parser.parse_args()
330+
331+
main(
332+
google_ads_client,
333+
args.customer_id,
334+
args.feed_id,
335+
args.feed_item_id,
336+
args.flight_placeholder_field_name,
337+
args.attribute_value,
338+
)

0 commit comments

Comments
 (0)