3
# Copyright (C) 2006 Google Inc.
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
17
"""Contains extensions to Atom objects used with Google Base."""
20
__author__ = 'api.jscudder (Jeffrey Scudder)'
24
from xml.etree import cElementTree as ElementTree
27
import cElementTree as ElementTree
30
from xml.etree import ElementTree
32
from elementtree import ElementTree
37
# XML namespaces which are often used in Google Base entities.
38
GBASE_NAMESPACE = 'http://base.google.com/ns/1.0'
39
GBASE_TEMPLATE = '{http://base.google.com/ns/1.0}%s'
40
GMETA_NAMESPACE = 'http://base.google.com/ns-metadata/1.0'
41
GMETA_TEMPLATE = '{http://base.google.com/ns-metadata/1.0}%s'
44
class ItemAttributeContainer(object):
45
"""Provides methods for finding Google Base Item attributes.
47
Google Base item attributes are child nodes in the gbase namespace. Google
48
Base allows you to define your own item attributes and this class provides
49
methods to interact with the custom attributes.
52
def GetItemAttributes(self, name):
53
"""Returns a list of all item attributes which have the desired name.
56
name: str The tag of the desired base attributes. For example, calling
57
this method with 'rating' would return a list of ItemAttributes
58
represented by a 'g:rating' tag.
61
A list of matching ItemAttribute objects.
64
for attrib in self.item_attributes:
65
if attrib.name == name:
69
def FindItemAttribute(self, name):
70
"""Get the contents of the first Base item attribute which matches name.
72
This method is deprecated, please use GetItemAttributes instead.
75
name: str The tag of the desired base attribute. For example, calling
76
this method with name = 'rating' would search for a tag rating
77
in the GBase namespace in the item attributes.
80
The text contents of the item attribute, or none if the attribute was
84
for attrib in self.item_attributes:
85
if attrib.name == name:
89
def AddItemAttribute(self, name, value, value_type=None, access=None):
90
"""Adds a new item attribute tag containing the value.
92
Creates a new extension element in the GBase namespace to represent a
93
Google Base item attribute.
96
name: str The tag name for the new attribute. This must be a valid xml
97
tag name. The tag will be placed in the GBase namespace.
98
value: str Contents for the item attribute
99
value_type: str (optional) The type of data in the vlaue, Examples: text
101
access: str (optional) Used to hide attributes. The attribute is not
102
exposed in the snippets feed if access is set to 'private'.
105
new_attribute = ItemAttribute(name, text=value,
106
text_type=value_type, access=access)
107
self.item_attributes.append(new_attribute)
109
def SetItemAttribute(self, name, value):
110
"""Changes an existing item attribute's value."""
112
for attrib in self.item_attributes:
113
if attrib.name == name:
117
def RemoveItemAttribute(self, name):
118
"""Deletes the first extension element which matches name.
120
Deletes the first extension element which matches name.
123
for i in xrange(len(self.item_attributes)):
124
if self.item_attributes[i].name == name:
125
del self.item_attributes[i]
128
# We need to overwrite _ConvertElementTreeToMember to add special logic to
129
# convert custom attributes to members
130
def _ConvertElementTreeToMember(self, child_tree):
131
# Find the element's tag in this class's list of child members
132
if self.__class__._children.has_key(child_tree.tag):
133
member_name = self.__class__._children[child_tree.tag][0]
134
member_class = self.__class__._children[child_tree.tag][1]
135
# If the class member is supposed to contain a list, make sure the
136
# matching member is set to a list, then append the new member
137
# instance to the list.
138
if isinstance(member_class, list):
139
if getattr(self, member_name) is None:
140
setattr(self, member_name, [])
141
getattr(self, member_name).append(atom._CreateClassFromElementTree(
142
member_class[0], child_tree))
144
setattr(self, member_name,
145
atom._CreateClassFromElementTree(member_class, child_tree))
146
elif child_tree.tag.find('{%s}' % GBASE_NAMESPACE) == 0:
147
# If this is in the gbase namespace, make it into an extension element.
148
name = child_tree.tag[child_tree.tag.index('}')+1:]
149
value = child_tree.text
150
if child_tree.attrib.has_key('type'):
151
value_type = child_tree.attrib['type']
154
self.AddItemAttribute(name, value, value_type)
156
atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree)
158
# We need to overwtite _AddMembersToElementTree to add special logic to
159
# convert custom members to XML nodes.
160
def _AddMembersToElementTree(self, tree):
161
# Convert the members of this class which are XML child nodes.
162
# This uses the class's _children dictionary to find the members which
163
# should become XML child nodes.
164
member_node_names = [values[0] for tag, values in
165
self.__class__._children.iteritems()]
166
for member_name in member_node_names:
167
member = getattr(self, member_name)
170
elif isinstance(member, list):
171
for instance in member:
172
instance._BecomeChildElement(tree)
174
member._BecomeChildElement(tree)
175
# Convert the members of this class which are XML attributes.
176
for xml_attribute, member_name in self.__class__._attributes.iteritems():
177
member = getattr(self, member_name)
178
if member is not None:
179
tree.attrib[xml_attribute] = member
180
# Convert all special custom item attributes to nodes
181
for attribute in self.item_attributes:
182
attribute._BecomeChildElement(tree)
183
# Lastly, call the ExtensionContainers's _AddMembersToElementTree to
184
# convert any extension attributes.
185
atom.ExtensionContainer._AddMembersToElementTree(self, tree)
188
class ItemAttribute(atom.Text):
189
"""An optional or user defined attribute for a GBase item.
191
Google Base allows items to have custom attribute child nodes. These nodes
192
have contents and a type attribute which tells Google Base whether the
193
contents are text, a float value with units, etc. The Atom text class has
194
the same structure, so this class inherits from Text.
197
_namespace = GBASE_NAMESPACE
198
_children = atom.Text._children.copy()
199
_attributes = atom.Text._attributes.copy()
200
_attributes['access'] = 'access'
202
def __init__(self, name, text_type=None, access=None, text=None,
203
extension_elements=None, extension_attributes=None):
204
"""Constructor for a GBase item attribute
207
name: str The name of the attribute. Examples include
208
price, color, make, model, pages, salary, etc.
209
text_type: str (optional) The type associated with the text contents
210
access: str (optional) If the access attribute is set to 'private', the
211
attribute will not be included in the item's description in the
213
text: str (optional) The text data in the this element
214
extension_elements: list (optional) A list of ExtensionElement
216
extension_attributes: dict (optional) A dictionary of attribute
221
self.type = text_type
224
self.extension_elements = extension_elements or []
225
self.extension_attributes = extension_attributes or {}
227
def _BecomeChildElement(self, tree):
228
new_child = ElementTree.Element('')
229
tree.append(new_child)
230
new_child.tag = '{%s}%s' % (self.__class__._namespace,
232
self._AddMembersToElementTree(new_child)
234
def _ToElementTree(self):
235
new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace,
237
self._AddMembersToElementTree(new_tree)
241
def ItemAttributeFromString(xml_string):
242
element_tree = ElementTree.fromstring(xml_string)
243
return _ItemAttributeFromElementTree(element_tree)
246
def _ItemAttributeFromElementTree(element_tree):
247
if element_tree.tag.find(GBASE_TEMPLATE % '') == 0:
248
to_return = ItemAttribute('')
249
to_return._HarvestElementTree(element_tree)
250
to_return.name = element_tree.tag[element_tree.tag.index('}')+1:]
251
if to_return.name and to_return.name != '':
256
class Label(atom.AtomBase):
257
"""The Google Base label element"""
260
_namespace = GBASE_NAMESPACE
261
_children = atom.AtomBase._children.copy()
262
_attributes = atom.AtomBase._attributes.copy()
264
def __init__(self, text=None, extension_elements=None,
265
extension_attributes=None):
267
self.extension_elements = extension_elements or []
268
self.extension_attributes = extension_attributes or {}
271
def LabelFromString(xml_string):
272
return atom.CreateClassFromXMLString(Label, xml_string)
275
class Thumbnail(atom.AtomBase):
276
"""The Google Base thumbnail element"""
279
_namespace = GMETA_NAMESPACE
280
_children = atom.AtomBase._children.copy()
281
_attributes = atom.AtomBase._attributes.copy()
282
_attributes['width'] = 'width'
283
_attributes['height'] = 'height'
285
def __init__(self, width=None, height=None, text=None, extension_elements=None,
286
extension_attributes=None):
288
self.extension_elements = extension_elements or []
289
self.extension_attributes = extension_attributes or {}
294
def ThumbnailFromString(xml_string):
295
return atom.CreateClassFromXMLString(Thumbnail, xml_string)
298
class ImageLink(atom.Text):
299
"""The Google Base image_link element"""
302
_namespace = GBASE_NAMESPACE
303
_children = atom.Text._children.copy()
304
_attributes = atom.Text._attributes.copy()
305
_children['{%s}thumbnail' % GMETA_NAMESPACE] = ('thumbnail', [Thumbnail])
307
def __init__(self, thumbnail=None, text=None, extension_elements=None,
308
text_type=None, extension_attributes=None):
309
self.thumbnail = thumbnail or []
311
self.type = text_type
312
self.extension_elements = extension_elements or []
313
self.extension_attributes = extension_attributes or {}
316
def ImageLinkFromString(xml_string):
317
return atom.CreateClassFromXMLString(ImageLink, xml_string)
320
class ItemType(atom.Text):
321
"""The Google Base item_type element"""
324
_namespace = GBASE_NAMESPACE
325
_children = atom.Text._children.copy()
326
_attributes = atom.Text._attributes.copy()
328
def __init__(self, text=None, extension_elements=None,
329
text_type=None, extension_attributes=None):
331
self.type = text_type
332
self.extension_elements = extension_elements or []
333
self.extension_attributes = extension_attributes or {}
336
def ItemTypeFromString(xml_string):
337
return atom.CreateClassFromXMLString(ItemType, xml_string)
340
class MetaItemType(ItemType):
341
"""The Google Base item_type element"""
344
_namespace = GMETA_NAMESPACE
345
_children = ItemType._children.copy()
346
_attributes = ItemType._attributes.copy()
349
def MetaItemTypeFromString(xml_string):
350
return atom.CreateClassFromXMLString(MetaItemType, xml_string)
353
class Value(atom.AtomBase):
354
"""Metadata about common values for a given attribute
356
A value is a child of an attribute which comes from the attributes feed.
357
The value's text is a commonly used value paired with an attribute name
358
and the value's count tells how often this value appears for the given
359
attribute in the search results.
363
_namespace = GMETA_NAMESPACE
364
_children = atom.AtomBase._children.copy()
365
_attributes = atom.AtomBase._attributes.copy()
366
_attributes['count'] = 'count'
368
def __init__(self, count=None, text=None, extension_elements=None,
369
extension_attributes=None):
370
"""Constructor for Attribute metadata element
373
count: str (optional) The number of times the value in text is given
374
for the parent attribute.
375
text: str (optional) The value which appears in the search results.
376
extension_elements: list (optional) A list of ExtensionElement
378
extension_attributes: dict (optional) A dictionary of attribute value
384
self.extension_elements = extension_elements or []
385
self.extension_attributes = extension_attributes or {}
388
def ValueFromString(xml_string):
389
return atom.CreateClassFromXMLString(Value, xml_string)
392
class Attribute(atom.Text):
393
"""Metadata about an attribute from the attributes feed
395
An entry from the attributes feed contains a list of attributes. Each
396
attribute describes the attribute's type and count of the items which
401
_namespace = GMETA_NAMESPACE
402
_children = atom.Text._children.copy()
403
_attributes = atom.Text._attributes.copy()
404
_children['{%s}value' % GMETA_NAMESPACE] = ('value', [Value])
405
_attributes['count'] = 'count'
406
_attributes['name'] = 'name'
408
def __init__(self, name=None, attribute_type=None, count=None, value=None,
409
text=None, extension_elements=None, extension_attributes=None):
410
"""Constructor for Attribute metadata element
413
name: str (optional) The name of the attribute
414
attribute_type: str (optional) The type for the attribute. Examples:
416
count: str (optional) The number of times this attribute appears in
418
value: list (optional) The values which are often used for this
420
text: str (optional) The text contents of the XML for this attribute.
421
extension_elements: list (optional) A list of ExtensionElement
423
extension_attributes: dict (optional) A dictionary of attribute value
428
self.type = attribute_type
430
self.value = value or []
432
self.extension_elements = extension_elements or []
433
self.extension_attributes = extension_attributes or {}
436
def AttributeFromString(xml_string):
437
return atom.CreateClassFromXMLString(Attribute, xml_string)
440
class Attributes(atom.AtomBase):
441
"""A collection of Google Base metadata attributes"""
444
_namespace = GMETA_NAMESPACE
445
_children = atom.AtomBase._children.copy()
446
_attributes = atom.AtomBase._attributes.copy()
447
_children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute])
449
def __init__(self, attribute=None, extension_elements=None,
450
extension_attributes=None, text=None):
451
self.attribute = attribute or []
452
self.extension_elements = extension_elements or []
453
self.extension_attributes = extension_attributes or {}
457
class GBaseItem(ItemAttributeContainer, gdata.BatchEntry):
458
"""An Google Base flavor of an Atom Entry.
460
Google Base items have required attributes, recommended attributes, and user
461
defined attributes. The required attributes are stored in this class as
462
members, and other attributes are stored as extension elements. You can
463
access the recommended and user defined attributes by using
464
AddItemAttribute, SetItemAttribute, FindItemAttribute, and
471
_namespace = atom.ATOM_NAMESPACE
472
_children = gdata.BatchEntry._children.copy()
473
_attributes = gdata.BatchEntry._attributes.copy()
474
_children['{%s}label' % GBASE_NAMESPACE] = ('label', [Label])
475
_children['{%s}item_type' % GBASE_NAMESPACE] = ('item_type', ItemType)
477
def __init__(self, author=None, category=None, content=None,
478
contributor=None, atom_id=None, link=None, published=None, rights=None,
479
source=None, summary=None, title=None, updated=None, control=None,
480
label=None, item_type=None, item_attributes=None,
481
batch_operation=None, batch_id=None, batch_status=None,
482
text=None, extension_elements=None, extension_attributes=None):
483
self.author = author or []
484
self.category = category or []
485
self.content = content
486
self.contributor = contributor or []
488
self.link = link or []
489
self.published = published
492
self.summary = summary
494
self.updated = updated
495
self.control = control
496
self.label = label or []
497
self.item_type = item_type
498
self.item_attributes = item_attributes or []
499
self.batch_operation = batch_operation
500
self.batch_id = batch_id
501
self.batch_status = batch_status
503
self.extension_elements = extension_elements or []
504
self.extension_attributes = extension_attributes or {}
507
def GBaseItemFromString(xml_string):
508
return atom.CreateClassFromXMLString(GBaseItem, xml_string)
511
class GBaseSnippet(GBaseItem):
513
_namespace = atom.ATOM_NAMESPACE
514
_children = GBaseItem._children.copy()
515
_attributes = GBaseItem._attributes.copy()
518
def GBaseSnippetFromString(xml_string):
519
return atom.CreateClassFromXMLString(GBaseSnippet, xml_string)
522
class GBaseAttributeEntry(gdata.GDataEntry):
523
"""An Atom Entry from the attributes feed"""
526
_namespace = atom.ATOM_NAMESPACE
527
_children = gdata.GDataEntry._children.copy()
528
_attributes = gdata.GDataEntry._attributes.copy()
529
_children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute])
531
def __init__(self, author=None, category=None, content=None,
532
contributor=None, atom_id=None, link=None, published=None, rights=None,
533
source=None, summary=None, title=None, updated=None, label=None,
534
attribute=None, control=None,
535
text=None, extension_elements=None, extension_attributes=None):
536
self.author = author or []
537
self.category = category or []
538
self.content = content
539
self.contributor = contributor or []
541
self.link = link or []
542
self.published = published
545
self.summary = summary
546
self.control = control
548
self.updated = updated
549
self.label = label or []
550
self.attribute = attribute or []
552
self.extension_elements = extension_elements or []
553
self.extension_attributes = extension_attributes or {}
556
def GBaseAttributeEntryFromString(xml_string):
557
return atom.CreateClassFromXMLString(GBaseAttributeEntry, xml_string)
560
class GBaseItemTypeEntry(gdata.GDataEntry):
561
"""An Atom entry from the item types feed
563
These entries contain a list of attributes which are stored in one
564
XML node called attributes. This class simplifies the data structure
565
by treating attributes as a list of attribute instances.
567
Note that the item_type for an item type entry is in the Google Base meta
568
namespace as opposed to item_types encountered in other feeds.
572
_namespace = atom.ATOM_NAMESPACE
573
_children = gdata.GDataEntry._children.copy()
574
_attributes = gdata.GDataEntry._attributes.copy()
575
_children['{%s}attributes' % GMETA_NAMESPACE] = ('attributes', Attributes)
576
_children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute])
577
_children['{%s}item_type' % GMETA_NAMESPACE] = ('item_type', MetaItemType)
579
def __init__(self, author=None, category=None, content=None,
580
contributor=None, atom_id=None, link=None, published=None, rights=None,
581
source=None, summary=None, title=None, updated=None, label=None,
582
item_type=None, control=None, attribute=None, attributes=None,
583
text=None, extension_elements=None, extension_attributes=None):
584
self.author = author or []
585
self.category = category or []
586
self.content = content
587
self.contributor = contributor or []
589
self.link = link or []
590
self.published = published
593
self.summary = summary
595
self.updated = updated
596
self.control = control
597
self.label = label or []
598
self.item_type = item_type
599
self.attributes = attributes
600
self.attribute = attribute or []
602
self.extension_elements = extension_elements or []
603
self.extension_attributes = extension_attributes or {}
606
def GBaseItemTypeEntryFromString(xml_string):
607
return atom.CreateClassFromXMLString(GBaseItemTypeEntry, xml_string)
610
class GBaseItemFeed(gdata.BatchFeed):
611
"""A feed containing Google Base Items"""
614
_namespace = atom.ATOM_NAMESPACE
615
_children = gdata.BatchFeed._children.copy()
616
_attributes = gdata.BatchFeed._attributes.copy()
617
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseItem])
620
def GBaseItemFeedFromString(xml_string):
621
return atom.CreateClassFromXMLString(GBaseItemFeed, xml_string)
624
class GBaseSnippetFeed(gdata.GDataFeed):
625
"""A feed containing Google Base Snippets"""
628
_namespace = atom.ATOM_NAMESPACE
629
_children = gdata.GDataFeed._children.copy()
630
_attributes = gdata.GDataFeed._attributes.copy()
631
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseSnippet])
634
def GBaseSnippetFeedFromString(xml_string):
635
return atom.CreateClassFromXMLString(GBaseSnippetFeed, xml_string)
638
class GBaseAttributesFeed(gdata.GDataFeed):
639
"""A feed containing Google Base Attributes
641
A query sent to the attributes feed will return a feed of
642
attributes which are present in the items that match the
647
_namespace = atom.ATOM_NAMESPACE
648
_children = gdata.GDataFeed._children.copy()
649
_attributes = gdata.GDataFeed._attributes.copy()
650
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
651
[GBaseAttributeEntry])
654
def GBaseAttributesFeedFromString(xml_string):
655
return atom.CreateClassFromXMLString(GBaseAttributesFeed, xml_string)
658
class GBaseLocalesFeed(gdata.GDataFeed):
659
"""The locales feed from Google Base.
661
This read-only feed defines the permitted locales for Google Base. The
662
locale value identifies the language, currency, and date formats used in a
667
_namespace = atom.ATOM_NAMESPACE
668
_children = gdata.GDataFeed._children.copy()
669
_attributes = gdata.GDataFeed._attributes.copy()
672
def GBaseLocalesFeedFromString(xml_string):
673
return atom.CreateClassFromXMLString(GBaseLocalesFeed, xml_string)
676
class GBaseItemTypesFeed(gdata.GDataFeed):
677
"""A feed from the Google Base item types feed"""
680
_namespace = atom.ATOM_NAMESPACE
681
_children = gdata.GDataFeed._children.copy()
682
_attributes = gdata.GDataFeed._attributes.copy()
683
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseItemTypeEntry])
686
def GBaseItemTypesFeedFromString(xml_string):
687
return atom.CreateClassFromXMLString(GBaseItemTypesFeed, xml_string)