/gdata/base/__init__.py
Python | 687 lines | 650 code | 10 blank | 27 comment | 0 complexity | 65e1bcb23bb05a28b86e601f6618dc44 MD5 | raw file
1#!/usr/bin/python 2# 3# Copyright (C) 2006 Google Inc. 4# 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 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 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. 16 17"""Contains extensions to Atom objects used with Google Base.""" 18 19 20__author__ = 'api.jscudder (Jeffrey Scudder)' 21 22 23try: 24 from xml.etree import cElementTree as ElementTree 25except ImportError: 26 try: 27 import cElementTree as ElementTree 28 except ImportError: 29 try: 30 from xml.etree import ElementTree 31 except ImportError: 32 from elementtree import ElementTree 33import atom 34import gdata 35 36 37# XML namespaces which are often used in Google Base entities. 38GBASE_NAMESPACE = 'http://base.google.com/ns/1.0' 39GBASE_TEMPLATE = '{http://base.google.com/ns/1.0}%s' 40GMETA_NAMESPACE = 'http://base.google.com/ns-metadata/1.0' 41GMETA_TEMPLATE = '{http://base.google.com/ns-metadata/1.0}%s' 42 43 44class ItemAttributeContainer(object): 45 """Provides methods for finding Google Base Item attributes. 46 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. 50 """ 51 52 def GetItemAttributes(self, name): 53 """Returns a list of all item attributes which have the desired name. 54 55 Args: 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. 59 60 Returns: 61 A list of matching ItemAttribute objects. 62 """ 63 result = [] 64 for attrib in self.item_attributes: 65 if attrib.name == name: 66 result.append(attrib) 67 return result 68 69 def FindItemAttribute(self, name): 70 """Get the contents of the first Base item attribute which matches name. 71 72 This method is deprecated, please use GetItemAttributes instead. 73 74 Args: 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. 78 79 Returns: 80 The text contents of the item attribute, or none if the attribute was 81 not found. 82 """ 83 84 for attrib in self.item_attributes: 85 if attrib.name == name: 86 return attrib.text 87 return None 88 89 def AddItemAttribute(self, name, value, value_type=None, access=None): 90 """Adds a new item attribute tag containing the value. 91 92 Creates a new extension element in the GBase namespace to represent a 93 Google Base item attribute. 94 95 Args: 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 100 float 101 access: str (optional) Used to hide attributes. The attribute is not 102 exposed in the snippets feed if access is set to 'private'. 103 """ 104 105 new_attribute = ItemAttribute(name, text=value, 106 text_type=value_type, access=access) 107 self.item_attributes.append(new_attribute) 108 109 def SetItemAttribute(self, name, value): 110 """Changes an existing item attribute's value.""" 111 112 for attrib in self.item_attributes: 113 if attrib.name == name: 114 attrib.text = value 115 return 116 117 def RemoveItemAttribute(self, name): 118 """Deletes the first extension element which matches name. 119 120 Deletes the first extension element which matches name. 121 """ 122 123 for i in xrange(len(self.item_attributes)): 124 if self.item_attributes[i].name == name: 125 del self.item_attributes[i] 126 return 127 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)) 143 else: 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'] 152 else: 153 value_type = None 154 self.AddItemAttribute(name, value, value_type) 155 else: 156 atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) 157 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) 168 if member is None: 169 pass 170 elif isinstance(member, list): 171 for instance in member: 172 instance._BecomeChildElement(tree) 173 else: 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) 186 187 188class ItemAttribute(atom.Text): 189 """An optional or user defined attribute for a GBase item. 190 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. 195 """ 196 197 _namespace = GBASE_NAMESPACE 198 _children = atom.Text._children.copy() 199 _attributes = atom.Text._attributes.copy() 200 _attributes['access'] = 'access' 201 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 205 206 Args: 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 212 snippets feed 213 text: str (optional) The text data in the this element 214 extension_elements: list (optional) A list of ExtensionElement 215 instances 216 extension_attributes: dict (optional) A dictionary of attribute 217 value string pairs 218 """ 219 220 self.name = name 221 self.type = text_type 222 self.access = access 223 self.text = text 224 self.extension_elements = extension_elements or [] 225 self.extension_attributes = extension_attributes or {} 226 227 def _BecomeChildElement(self, tree): 228 new_child = ElementTree.Element('') 229 tree.append(new_child) 230 new_child.tag = '{%s}%s' % (self.__class__._namespace, 231 self.name) 232 self._AddMembersToElementTree(new_child) 233 234 def _ToElementTree(self): 235 new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace, 236 self.name)) 237 self._AddMembersToElementTree(new_tree) 238 return new_tree 239 240 241def ItemAttributeFromString(xml_string): 242 element_tree = ElementTree.fromstring(xml_string) 243 return _ItemAttributeFromElementTree(element_tree) 244 245 246def _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 != '': 252 return to_return 253 return None 254 255 256class Label(atom.AtomBase): 257 """The Google Base label element""" 258 259 _tag = 'label' 260 _namespace = GBASE_NAMESPACE 261 _children = atom.AtomBase._children.copy() 262 _attributes = atom.AtomBase._attributes.copy() 263 264 def __init__(self, text=None, extension_elements=None, 265 extension_attributes=None): 266 self.text = text 267 self.extension_elements = extension_elements or [] 268 self.extension_attributes = extension_attributes or {} 269 270 271def LabelFromString(xml_string): 272 return atom.CreateClassFromXMLString(Label, xml_string) 273 274 275class Thumbnail(atom.AtomBase): 276 """The Google Base thumbnail element""" 277 278 _tag = 'thumbnail' 279 _namespace = GMETA_NAMESPACE 280 _children = atom.AtomBase._children.copy() 281 _attributes = atom.AtomBase._attributes.copy() 282 _attributes['width'] = 'width' 283 _attributes['height'] = 'height' 284 285 def __init__(self, width=None, height=None, text=None, extension_elements=None, 286 extension_attributes=None): 287 self.text = text 288 self.extension_elements = extension_elements or [] 289 self.extension_attributes = extension_attributes or {} 290 self.width = width 291 self.height = height 292 293 294def ThumbnailFromString(xml_string): 295 return atom.CreateClassFromXMLString(Thumbnail, xml_string) 296 297 298class ImageLink(atom.Text): 299 """The Google Base image_link element""" 300 301 _tag = 'image_link' 302 _namespace = GBASE_NAMESPACE 303 _children = atom.Text._children.copy() 304 _attributes = atom.Text._attributes.copy() 305 _children['{%s}thumbnail' % GMETA_NAMESPACE] = ('thumbnail', [Thumbnail]) 306 307 def __init__(self, thumbnail=None, text=None, extension_elements=None, 308 text_type=None, extension_attributes=None): 309 self.thumbnail = thumbnail or [] 310 self.text = text 311 self.type = text_type 312 self.extension_elements = extension_elements or [] 313 self.extension_attributes = extension_attributes or {} 314 315 316def ImageLinkFromString(xml_string): 317 return atom.CreateClassFromXMLString(ImageLink, xml_string) 318 319 320class ItemType(atom.Text): 321 """The Google Base item_type element""" 322 323 _tag = 'item_type' 324 _namespace = GBASE_NAMESPACE 325 _children = atom.Text._children.copy() 326 _attributes = atom.Text._attributes.copy() 327 328 def __init__(self, text=None, extension_elements=None, 329 text_type=None, extension_attributes=None): 330 self.text = text 331 self.type = text_type 332 self.extension_elements = extension_elements or [] 333 self.extension_attributes = extension_attributes or {} 334 335 336def ItemTypeFromString(xml_string): 337 return atom.CreateClassFromXMLString(ItemType, xml_string) 338 339 340class MetaItemType(ItemType): 341 """The Google Base item_type element""" 342 343 _tag = 'item_type' 344 _namespace = GMETA_NAMESPACE 345 _children = ItemType._children.copy() 346 _attributes = ItemType._attributes.copy() 347 348 349def MetaItemTypeFromString(xml_string): 350 return atom.CreateClassFromXMLString(MetaItemType, xml_string) 351 352 353class Value(atom.AtomBase): 354 """Metadata about common values for a given attribute 355 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. 360 """ 361 362 _tag = 'value' 363 _namespace = GMETA_NAMESPACE 364 _children = atom.AtomBase._children.copy() 365 _attributes = atom.AtomBase._attributes.copy() 366 _attributes['count'] = 'count' 367 368 def __init__(self, count=None, text=None, extension_elements=None, 369 extension_attributes=None): 370 """Constructor for Attribute metadata element 371 372 Args: 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 377 instances 378 extension_attributes: dict (optional) A dictionary of attribute value 379 string pairs 380 """ 381 382 self.count = count 383 self.text = text 384 self.extension_elements = extension_elements or [] 385 self.extension_attributes = extension_attributes or {} 386 387 388def ValueFromString(xml_string): 389 return atom.CreateClassFromXMLString(Value, xml_string) 390 391 392class Attribute(atom.Text): 393 """Metadata about an attribute from the attributes feed 394 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 397 use the attribute. 398 """ 399 400 _tag = 'attribute' 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' 407 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 411 412 Args: 413 name: str (optional) The name of the attribute 414 attribute_type: str (optional) The type for the attribute. Examples: 415 test, float, etc. 416 count: str (optional) The number of times this attribute appears in 417 the query results. 418 value: list (optional) The values which are often used for this 419 attirbute. 420 text: str (optional) The text contents of the XML for this attribute. 421 extension_elements: list (optional) A list of ExtensionElement 422 instances 423 extension_attributes: dict (optional) A dictionary of attribute value 424 string pairs 425 """ 426 427 self.name = name 428 self.type = attribute_type 429 self.count = count 430 self.value = value or [] 431 self.text = text 432 self.extension_elements = extension_elements or [] 433 self.extension_attributes = extension_attributes or {} 434 435 436def AttributeFromString(xml_string): 437 return atom.CreateClassFromXMLString(Attribute, xml_string) 438 439 440class Attributes(atom.AtomBase): 441 """A collection of Google Base metadata attributes""" 442 443 _tag = '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]) 448 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 {} 454 self.text = text 455 456 457class GBaseItem(ItemAttributeContainer, gdata.BatchEntry): 458 """An Google Base flavor of an Atom Entry. 459 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 465 RemoveItemAttribute. 466 467 The Base Item 468 """ 469 470 _tag = 'entry' 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) 476 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 [] 487 self.id = atom_id 488 self.link = link or [] 489 self.published = published 490 self.rights = rights 491 self.source = source 492 self.summary = summary 493 self.title = title 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 502 self.text = text 503 self.extension_elements = extension_elements or [] 504 self.extension_attributes = extension_attributes or {} 505 506 507def GBaseItemFromString(xml_string): 508 return atom.CreateClassFromXMLString(GBaseItem, xml_string) 509 510 511class GBaseSnippet(GBaseItem): 512 _tag = 'entry' 513 _namespace = atom.ATOM_NAMESPACE 514 _children = GBaseItem._children.copy() 515 _attributes = GBaseItem._attributes.copy() 516 517 518def GBaseSnippetFromString(xml_string): 519 return atom.CreateClassFromXMLString(GBaseSnippet, xml_string) 520 521 522class GBaseAttributeEntry(gdata.GDataEntry): 523 """An Atom Entry from the attributes feed""" 524 525 _tag = 'entry' 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]) 530 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 [] 540 self.id = atom_id 541 self.link = link or [] 542 self.published = published 543 self.rights = rights 544 self.source = source 545 self.summary = summary 546 self.control = control 547 self.title = title 548 self.updated = updated 549 self.label = label or [] 550 self.attribute = attribute or [] 551 self.text = text 552 self.extension_elements = extension_elements or [] 553 self.extension_attributes = extension_attributes or {} 554 555 556def GBaseAttributeEntryFromString(xml_string): 557 return atom.CreateClassFromXMLString(GBaseAttributeEntry, xml_string) 558 559 560class GBaseItemTypeEntry(gdata.GDataEntry): 561 """An Atom entry from the item types feed 562 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. 566 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. 569 """ 570 571 _tag = 'entry' 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) 578 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 [] 588 self.id = atom_id 589 self.link = link or [] 590 self.published = published 591 self.rights = rights 592 self.source = source 593 self.summary = summary 594 self.title = title 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 [] 601 self.text = text 602 self.extension_elements = extension_elements or [] 603 self.extension_attributes = extension_attributes or {} 604 605 606def GBaseItemTypeEntryFromString(xml_string): 607 return atom.CreateClassFromXMLString(GBaseItemTypeEntry, xml_string) 608 609 610class GBaseItemFeed(gdata.BatchFeed): 611 """A feed containing Google Base Items""" 612 613 _tag = 'feed' 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]) 618 619 620def GBaseItemFeedFromString(xml_string): 621 return atom.CreateClassFromXMLString(GBaseItemFeed, xml_string) 622 623 624class GBaseSnippetFeed(gdata.GDataFeed): 625 """A feed containing Google Base Snippets""" 626 627 _tag = 'feed' 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]) 632 633 634def GBaseSnippetFeedFromString(xml_string): 635 return atom.CreateClassFromXMLString(GBaseSnippetFeed, xml_string) 636 637 638class GBaseAttributesFeed(gdata.GDataFeed): 639 """A feed containing Google Base Attributes 640 641 A query sent to the attributes feed will return a feed of 642 attributes which are present in the items that match the 643 query. 644 """ 645 646 _tag = 'feed' 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]) 652 653 654def GBaseAttributesFeedFromString(xml_string): 655 return atom.CreateClassFromXMLString(GBaseAttributesFeed, xml_string) 656 657 658class GBaseLocalesFeed(gdata.GDataFeed): 659 """The locales feed from Google Base. 660 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 663 feed. 664 """ 665 666 _tag = 'feed' 667 _namespace = atom.ATOM_NAMESPACE 668 _children = gdata.GDataFeed._children.copy() 669 _attributes = gdata.GDataFeed._attributes.copy() 670 671 672def GBaseLocalesFeedFromString(xml_string): 673 return atom.CreateClassFromXMLString(GBaseLocalesFeed, xml_string) 674 675 676class GBaseItemTypesFeed(gdata.GDataFeed): 677 """A feed from the Google Base item types feed""" 678 679 _tag = '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]) 684 685 686def GBaseItemTypesFeedFromString(xml_string): 687 return atom.CreateClassFromXMLString(GBaseItemTypesFeed, xml_string)