/gdata/__init__.py
Python | 835 lines | 770 code | 20 blank | 45 comment | 4 complexity | 5dcaa4ad36cb319798ea710077c41115 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 18"""Contains classes representing Google Data elements. 19 20 Extends Atom classes to add Google Data specific elements. 21""" 22 23 24__author__ = 'j.s@google.com (Jeffrey Scudder)' 25 26import os 27import atom 28try: 29 from xml.etree import cElementTree as ElementTree 30except ImportError: 31 try: 32 import cElementTree as ElementTree 33 except ImportError: 34 try: 35 from xml.etree import ElementTree 36 except ImportError: 37 from elementtree import ElementTree 38 39 40# XML namespaces which are often used in GData entities. 41GDATA_NAMESPACE = 'http://schemas.google.com/g/2005' 42GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s' 43OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/' 44OPENSEARCH_TEMPLATE = '{http://a9.com/-/spec/opensearchrss/1.0/}%s' 45BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch' 46GACL_NAMESPACE = 'http://schemas.google.com/acl/2007' 47GACL_TEMPLATE = '{http://schemas.google.com/acl/2007}%s' 48 49 50# Labels used in batch request entries to specify the desired CRUD operation. 51BATCH_INSERT = 'insert' 52BATCH_UPDATE = 'update' 53BATCH_DELETE = 'delete' 54BATCH_QUERY = 'query' 55 56class Error(Exception): 57 pass 58 59 60class MissingRequiredParameters(Error): 61 pass 62 63 64class MediaSource(object): 65 """GData Entries can refer to media sources, so this class provides a 66 place to store references to these objects along with some metadata. 67 """ 68 69 def __init__(self, file_handle=None, content_type=None, content_length=None, 70 file_path=None, file_name=None): 71 """Creates an object of type MediaSource. 72 73 Args: 74 file_handle: A file handle pointing to the file to be encapsulated in the 75 MediaSource 76 content_type: string The MIME type of the file. Required if a file_handle 77 is given. 78 content_length: int The size of the file. Required if a file_handle is 79 given. 80 file_path: string (optional) A full path name to the file. Used in 81 place of a file_handle. 82 file_name: string The name of the file without any path information. 83 Required if a file_handle is given. 84 """ 85 self.file_handle = file_handle 86 self.content_type = content_type 87 self.content_length = content_length 88 self.file_name = file_name 89 90 if (file_handle is None and content_type is not None and 91 file_path is not None): 92 self.setFile(file_path, content_type) 93 94 def setFile(self, file_name, content_type): 95 """A helper function which can create a file handle from a given filename 96 and set the content type and length all at once. 97 98 Args: 99 file_name: string The path and file name to the file containing the media 100 content_type: string A MIME type representing the type of the media 101 """ 102 103 self.file_handle = open(file_name, 'rb') 104 self.content_type = content_type 105 self.content_length = os.path.getsize(file_name) 106 self.file_name = os.path.basename(file_name) 107 108 109class LinkFinder(atom.LinkFinder): 110 """An "interface" providing methods to find link elements 111 112 GData Entry elements often contain multiple links which differ in the rel 113 attribute or content type. Often, developers are interested in a specific 114 type of link so this class provides methods to find specific classes of 115 links. 116 117 This class is used as a mixin in GData entries. 118 """ 119 120 def GetSelfLink(self): 121 """Find the first link with rel set to 'self' 122 123 Returns: 124 An atom.Link or none if none of the links had rel equal to 'self' 125 """ 126 127 for a_link in self.link: 128 if a_link.rel == 'self': 129 return a_link 130 return None 131 132 def GetEditLink(self): 133 for a_link in self.link: 134 if a_link.rel == 'edit': 135 return a_link 136 return None 137 138 def GetEditMediaLink(self): 139 """The Picasa API mistakenly returns media-edit rather than edit-media, but 140 this may change soon. 141 """ 142 for a_link in self.link: 143 if a_link.rel == 'edit-media': 144 return a_link 145 if a_link.rel == 'media-edit': 146 return a_link 147 return None 148 149 def GetHtmlLink(self): 150 """Find the first link with rel of alternate and type of text/html 151 152 Returns: 153 An atom.Link or None if no links matched 154 """ 155 for a_link in self.link: 156 if a_link.rel == 'alternate' and a_link.type == 'text/html': 157 return a_link 158 return None 159 160 def GetPostLink(self): 161 """Get a link containing the POST target URL. 162 163 The POST target URL is used to insert new entries. 164 165 Returns: 166 A link object with a rel matching the POST type. 167 """ 168 for a_link in self.link: 169 if a_link.rel == 'http://schemas.google.com/g/2005#post': 170 return a_link 171 return None 172 173 def GetAclLink(self): 174 for a_link in self.link: 175 if a_link.rel == 'http://schemas.google.com/acl/2007#accessControlList': 176 return a_link 177 return None 178 179 def GetFeedLink(self): 180 for a_link in self.link: 181 if a_link.rel == 'http://schemas.google.com/g/2005#feed': 182 return a_link 183 return None 184 185 def GetNextLink(self): 186 for a_link in self.link: 187 if a_link.rel == 'next': 188 return a_link 189 return None 190 191 def GetPrevLink(self): 192 for a_link in self.link: 193 if a_link.rel == 'previous': 194 return a_link 195 return None 196 197 198class TotalResults(atom.AtomBase): 199 """opensearch:TotalResults for a GData feed""" 200 201 _tag = 'totalResults' 202 _namespace = OPENSEARCH_NAMESPACE 203 _children = atom.AtomBase._children.copy() 204 _attributes = atom.AtomBase._attributes.copy() 205 206 def __init__(self, extension_elements=None, 207 extension_attributes=None, text=None): 208 self.text = text 209 self.extension_elements = extension_elements or [] 210 self.extension_attributes = extension_attributes or {} 211 212 213def TotalResultsFromString(xml_string): 214 return atom.CreateClassFromXMLString(TotalResults, xml_string) 215 216 217class StartIndex(atom.AtomBase): 218 """The opensearch:startIndex element in GData feed""" 219 220 _tag = 'startIndex' 221 _namespace = OPENSEARCH_NAMESPACE 222 _children = atom.AtomBase._children.copy() 223 _attributes = atom.AtomBase._attributes.copy() 224 225 def __init__(self, extension_elements=None, 226 extension_attributes=None, text=None): 227 self.text = text 228 self.extension_elements = extension_elements or [] 229 self.extension_attributes = extension_attributes or {} 230 231 232def StartIndexFromString(xml_string): 233 return atom.CreateClassFromXMLString(StartIndex, xml_string) 234 235 236class ItemsPerPage(atom.AtomBase): 237 """The opensearch:itemsPerPage element in GData feed""" 238 239 _tag = 'itemsPerPage' 240 _namespace = OPENSEARCH_NAMESPACE 241 _children = atom.AtomBase._children.copy() 242 _attributes = atom.AtomBase._attributes.copy() 243 244 def __init__(self, extension_elements=None, 245 extension_attributes=None, text=None): 246 self.text = text 247 self.extension_elements = extension_elements or [] 248 self.extension_attributes = extension_attributes or {} 249 250 251def ItemsPerPageFromString(xml_string): 252 return atom.CreateClassFromXMLString(ItemsPerPage, xml_string) 253 254 255class ExtendedProperty(atom.AtomBase): 256 """The Google Data extendedProperty element. 257 258 Used to store arbitrary key-value information specific to your 259 application. The value can either be a text string stored as an XML 260 attribute (.value), or an XML node (XmlBlob) as a child element. 261 262 This element is used in the Google Calendar data API and the Google 263 Contacts data API. 264 """ 265 266 _tag = 'extendedProperty' 267 _namespace = GDATA_NAMESPACE 268 _children = atom.AtomBase._children.copy() 269 _attributes = atom.AtomBase._attributes.copy() 270 _attributes['name'] = 'name' 271 _attributes['value'] = 'value' 272 273 def __init__(self, name=None, value=None, extension_elements=None, 274 extension_attributes=None, text=None): 275 self.name = name 276 self.value = value 277 self.text = text 278 self.extension_elements = extension_elements or [] 279 self.extension_attributes = extension_attributes or {} 280 281 def GetXmlBlobExtensionElement(self): 282 """Returns the XML blob as an atom.ExtensionElement. 283 284 Returns: 285 An atom.ExtensionElement representing the blob's XML, or None if no 286 blob was set. 287 """ 288 if len(self.extension_elements) < 1: 289 return None 290 else: 291 return self.extension_elements[0] 292 293 def GetXmlBlobString(self): 294 """Returns the XML blob as a string. 295 296 Returns: 297 A string containing the blob's XML, or None if no blob was set. 298 """ 299 blob = self.GetXmlBlobExtensionElement() 300 if blob: 301 return blob.ToString() 302 return None 303 304 def SetXmlBlob(self, blob): 305 """Sets the contents of the extendedProperty to XML as a child node. 306 307 Since the extendedProperty is only allowed one child element as an XML 308 blob, setting the XML blob will erase any preexisting extension elements 309 in this object. 310 311 Args: 312 blob: str, ElementTree Element or atom.ExtensionElement representing 313 the XML blob stored in the extendedProperty. 314 """ 315 # Erase any existing extension_elements, clears the child nodes from the 316 # extendedProperty. 317 self.extension_elements = [] 318 if isinstance(blob, atom.ExtensionElement): 319 self.extension_elements.append(blob) 320 elif ElementTree.iselement(blob): 321 self.extension_elements.append(atom._ExtensionElementFromElementTree( 322 blob)) 323 else: 324 self.extension_elements.append(atom.ExtensionElementFromString(blob)) 325 326 327def ExtendedPropertyFromString(xml_string): 328 return atom.CreateClassFromXMLString(ExtendedProperty, xml_string) 329 330 331class GDataEntry(atom.Entry, LinkFinder): 332 """Extends Atom Entry to provide data processing""" 333 334 _tag = atom.Entry._tag 335 _namespace = atom.Entry._namespace 336 _children = atom.Entry._children.copy() 337 _attributes = atom.Entry._attributes.copy() 338 339 def __GetId(self): 340 return self.__id 341 342 # This method was created to strip the unwanted whitespace from the id's 343 # text node. 344 def __SetId(self, id): 345 self.__id = id 346 if id is not None and id.text is not None: 347 self.__id.text = id.text.strip() 348 349 id = property(__GetId, __SetId) 350 351 def IsMedia(self): 352 """Determines whether or not an entry is a GData Media entry. 353 """ 354 if (self.GetEditMediaLink()): 355 return True 356 else: 357 return False 358 359 def GetMediaURL(self): 360 """Returns the URL to the media content, if the entry is a media entry. 361 Otherwise returns None. 362 """ 363 if not self.IsMedia(): 364 return None 365 else: 366 return self.content.src 367 368 369def GDataEntryFromString(xml_string): 370 """Creates a new GDataEntry instance given a string of XML.""" 371 return atom.CreateClassFromXMLString(GDataEntry, xml_string) 372 373 374class GDataFeed(atom.Feed, LinkFinder): 375 """A Feed from a GData service""" 376 377 _tag = 'feed' 378 _namespace = atom.ATOM_NAMESPACE 379 _children = atom.Feed._children.copy() 380 _attributes = atom.Feed._attributes.copy() 381 _children['{%s}totalResults' % OPENSEARCH_NAMESPACE] = ('total_results', 382 TotalResults) 383 _children['{%s}startIndex' % OPENSEARCH_NAMESPACE] = ('start_index', 384 StartIndex) 385 _children['{%s}itemsPerPage' % OPENSEARCH_NAMESPACE] = ('items_per_page', 386 ItemsPerPage) 387 # Add a conversion rule for atom:entry to make it into a GData 388 # Entry. 389 _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GDataEntry]) 390 391 def __GetId(self): 392 return self.__id 393 394 def __SetId(self, id): 395 self.__id = id 396 if id is not None and id.text is not None: 397 self.__id.text = id.text.strip() 398 399 id = property(__GetId, __SetId) 400 401 def __GetGenerator(self): 402 return self.__generator 403 404 def __SetGenerator(self, generator): 405 self.__generator = generator 406 if generator is not None: 407 self.__generator.text = generator.text.strip() 408 409 generator = property(__GetGenerator, __SetGenerator) 410 411 def __init__(self, author=None, category=None, contributor=None, 412 generator=None, icon=None, atom_id=None, link=None, logo=None, 413 rights=None, subtitle=None, title=None, updated=None, entry=None, 414 total_results=None, start_index=None, items_per_page=None, 415 extension_elements=None, extension_attributes=None, text=None): 416 """Constructor for Source 417 418 Args: 419 author: list (optional) A list of Author instances which belong to this 420 class. 421 category: list (optional) A list of Category instances 422 contributor: list (optional) A list on Contributor instances 423 generator: Generator (optional) 424 icon: Icon (optional) 425 id: Id (optional) The entry's Id element 426 link: list (optional) A list of Link instances 427 logo: Logo (optional) 428 rights: Rights (optional) The entry's Rights element 429 subtitle: Subtitle (optional) The entry's subtitle element 430 title: Title (optional) the entry's title element 431 updated: Updated (optional) the entry's updated element 432 entry: list (optional) A list of the Entry instances contained in the 433 feed. 434 text: String (optional) The text contents of the element. This is the 435 contents of the Entry's XML text node. 436 (Example: <foo>This is the text</foo>) 437 extension_elements: list (optional) A list of ExtensionElement instances 438 which are children of this element. 439 extension_attributes: dict (optional) A dictionary of strings which are 440 the values for additional XML attributes of this element. 441 """ 442 443 self.author = author or [] 444 self.category = category or [] 445 self.contributor = contributor or [] 446 self.generator = generator 447 self.icon = icon 448 self.id = atom_id 449 self.link = link or [] 450 self.logo = logo 451 self.rights = rights 452 self.subtitle = subtitle 453 self.title = title 454 self.updated = updated 455 self.entry = entry or [] 456 self.total_results = total_results 457 self.start_index = start_index 458 self.items_per_page = items_per_page 459 self.text = text 460 self.extension_elements = extension_elements or [] 461 self.extension_attributes = extension_attributes or {} 462 463 464def GDataFeedFromString(xml_string): 465 return atom.CreateClassFromXMLString(GDataFeed, xml_string) 466 467 468class BatchId(atom.AtomBase): 469 _tag = 'id' 470 _namespace = BATCH_NAMESPACE 471 _children = atom.AtomBase._children.copy() 472 _attributes = atom.AtomBase._attributes.copy() 473 474 475def BatchIdFromString(xml_string): 476 return atom.CreateClassFromXMLString(BatchId, xml_string) 477 478 479class BatchOperation(atom.AtomBase): 480 _tag = 'operation' 481 _namespace = BATCH_NAMESPACE 482 _children = atom.AtomBase._children.copy() 483 _attributes = atom.AtomBase._attributes.copy() 484 _attributes['type'] = 'type' 485 486 def __init__(self, op_type=None, extension_elements=None, 487 extension_attributes=None, 488 text=None): 489 self.type = op_type 490 atom.AtomBase.__init__(self, 491 extension_elements=extension_elements, 492 extension_attributes=extension_attributes, 493 text=text) 494 495 496def BatchOperationFromString(xml_string): 497 return atom.CreateClassFromXMLString(BatchOperation, xml_string) 498 499 500class BatchStatus(atom.AtomBase): 501 """The batch:status element present in a batch response entry. 502 503 A status element contains the code (HTTP response code) and 504 reason as elements. In a single request these fields would 505 be part of the HTTP response, but in a batch request each 506 Entry operation has a corresponding Entry in the response 507 feed which includes status information. 508 509 See http://code.google.com/apis/gdata/batch.html#Handling_Errors 510 """ 511 512 _tag = 'status' 513 _namespace = BATCH_NAMESPACE 514 _children = atom.AtomBase._children.copy() 515 _attributes = atom.AtomBase._attributes.copy() 516 _attributes['code'] = 'code' 517 _attributes['reason'] = 'reason' 518 _attributes['content-type'] = 'content_type' 519 520 def __init__(self, code=None, reason=None, content_type=None, 521 extension_elements=None, extension_attributes=None, text=None): 522 self.code = code 523 self.reason = reason 524 self.content_type = content_type 525 atom.AtomBase.__init__(self, extension_elements=extension_elements, 526 extension_attributes=extension_attributes, 527 text=text) 528 529 530def BatchStatusFromString(xml_string): 531 return atom.CreateClassFromXMLString(BatchStatus, xml_string) 532 533 534class BatchEntry(GDataEntry): 535 """An atom:entry for use in batch requests. 536 537 The BatchEntry contains additional members to specify the operation to be 538 performed on this entry and a batch ID so that the server can reference 539 individual operations in the response feed. For more information, see: 540 http://code.google.com/apis/gdata/batch.html 541 """ 542 543 _tag = GDataEntry._tag 544 _namespace = GDataEntry._namespace 545 _children = GDataEntry._children.copy() 546 _children['{%s}operation' % BATCH_NAMESPACE] = ('batch_operation', BatchOperation) 547 _children['{%s}id' % BATCH_NAMESPACE] = ('batch_id', BatchId) 548 _children['{%s}status' % BATCH_NAMESPACE] = ('batch_status', BatchStatus) 549 _attributes = GDataEntry._attributes.copy() 550 551 def __init__(self, author=None, category=None, content=None, 552 contributor=None, atom_id=None, link=None, published=None, rights=None, 553 source=None, summary=None, control=None, title=None, updated=None, 554 batch_operation=None, batch_id=None, batch_status=None, 555 extension_elements=None, extension_attributes=None, text=None): 556 self.batch_operation = batch_operation 557 self.batch_id = batch_id 558 self.batch_status = batch_status 559 GDataEntry.__init__(self, author=author, category=category, 560 content=content, contributor=contributor, atom_id=atom_id, link=link, 561 published=published, rights=rights, source=source, summary=summary, 562 control=control, title=title, updated=updated, 563 extension_elements=extension_elements, 564 extension_attributes=extension_attributes, text=text) 565 566 567def BatchEntryFromString(xml_string): 568 return atom.CreateClassFromXMLString(BatchEntry, xml_string) 569 570 571class BatchInterrupted(atom.AtomBase): 572 """The batch:interrupted element sent if batch request was interrupted. 573 574 Only appears in a feed if some of the batch entries could not be processed. 575 See: http://code.google.com/apis/gdata/batch.html#Handling_Errors 576 """ 577 578 _tag = 'interrupted' 579 _namespace = BATCH_NAMESPACE 580 _children = atom.AtomBase._children.copy() 581 _attributes = atom.AtomBase._attributes.copy() 582 _attributes['reason'] = 'reason' 583 _attributes['success'] = 'success' 584 _attributes['failures'] = 'failures' 585 _attributes['parsed'] = 'parsed' 586 587 def __init__(self, reason=None, success=None, failures=None, parsed=None, 588 extension_elements=None, extension_attributes=None, text=None): 589 self.reason = reason 590 self.success = success 591 self.failures = failures 592 self.parsed = parsed 593 atom.AtomBase.__init__(self, extension_elements=extension_elements, 594 extension_attributes=extension_attributes, 595 text=text) 596 597 598def BatchInterruptedFromString(xml_string): 599 return atom.CreateClassFromXMLString(BatchInterrupted, xml_string) 600 601 602class BatchFeed(GDataFeed): 603 """A feed containing a list of batch request entries.""" 604 605 _tag = GDataFeed._tag 606 _namespace = GDataFeed._namespace 607 _children = GDataFeed._children.copy() 608 _attributes = GDataFeed._attributes.copy() 609 _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BatchEntry]) 610 _children['{%s}interrupted' % BATCH_NAMESPACE] = ('interrupted', BatchInterrupted) 611 612 def __init__(self, author=None, category=None, contributor=None, 613 generator=None, icon=None, atom_id=None, link=None, logo=None, 614 rights=None, subtitle=None, title=None, updated=None, entry=None, 615 total_results=None, start_index=None, items_per_page=None, 616 interrupted=None, 617 extension_elements=None, extension_attributes=None, text=None): 618 self.interrupted = interrupted 619 GDataFeed.__init__(self, author=author, category=category, 620 contributor=contributor, generator=generator, 621 icon=icon, atom_id=atom_id, link=link, 622 logo=logo, rights=rights, subtitle=subtitle, 623 title=title, updated=updated, entry=entry, 624 total_results=total_results, start_index=start_index, 625 items_per_page=items_per_page, 626 extension_elements=extension_elements, 627 extension_attributes=extension_attributes, 628 text=text) 629 630 def AddBatchEntry(self, entry=None, id_url_string=None, 631 batch_id_string=None, operation_string=None): 632 """Logic for populating members of a BatchEntry and adding to the feed. 633 634 635 If the entry is not a BatchEntry, it is converted to a BatchEntry so 636 that the batch specific members will be present. 637 638 The id_url_string can be used in place of an entry if the batch operation 639 applies to a URL. For example query and delete operations require just 640 the URL of an entry, no body is sent in the HTTP request. If an 641 id_url_string is sent instead of an entry, a BatchEntry is created and 642 added to the feed. 643 644 This method also assigns the desired batch id to the entry so that it 645 can be referenced in the server's response. If the batch_id_string is 646 None, this method will assign a batch_id to be the index at which this 647 entry will be in the feed's entry list. 648 649 Args: 650 entry: BatchEntry, atom.Entry, or another Entry flavor (optional) The 651 entry which will be sent to the server as part of the batch request. 652 The item must have a valid atom id so that the server knows which 653 entry this request references. 654 id_url_string: str (optional) The URL of the entry to be acted on. You 655 can find this URL in the text member of the atom id for an entry. 656 If an entry is not sent, this id will be used to construct a new 657 BatchEntry which will be added to the request feed. 658 batch_id_string: str (optional) The batch ID to be used to reference 659 this batch operation in the results feed. If this parameter is None, 660 the current length of the feed's entry array will be used as a 661 count. Note that batch_ids should either always be specified or 662 never, mixing could potentially result in duplicate batch ids. 663 operation_string: str (optional) The desired batch operation which will 664 set the batch_operation.type member of the entry. Options are 665 'insert', 'update', 'delete', and 'query' 666 667 Raises: 668 MissingRequiredParameters: Raised if neither an id_ url_string nor an 669 entry are provided in the request. 670 671 Returns: 672 The added entry. 673 """ 674 if entry is None and id_url_string is None: 675 raise MissingRequiredParameters('supply either an entry or URL string') 676 if entry is None and id_url_string is not None: 677 entry = BatchEntry(atom_id=atom.Id(text=id_url_string)) 678 # TODO: handle cases in which the entry lacks batch_... members. 679 #if not isinstance(entry, BatchEntry): 680 # Convert the entry to a batch entry. 681 if batch_id_string is not None: 682 entry.batch_id = BatchId(text=batch_id_string) 683 elif entry.batch_id is None or entry.batch_id.text is None: 684 entry.batch_id = BatchId(text=str(len(self.entry))) 685 if operation_string is not None: 686 entry.batch_operation = BatchOperation(op_type=operation_string) 687 self.entry.append(entry) 688 return entry 689 690 def AddInsert(self, entry, batch_id_string=None): 691 """Add an insert request to the operations in this batch request feed. 692 693 If the entry doesn't yet have an operation or a batch id, these will 694 be set to the insert operation and a batch_id specified as a parameter. 695 696 Args: 697 entry: BatchEntry The entry which will be sent in the batch feed as an 698 insert request. 699 batch_id_string: str (optional) The batch ID to be used to reference 700 this batch operation in the results feed. If this parameter is None, 701 the current length of the feed's entry array will be used as a 702 count. Note that batch_ids should either always be specified or 703 never, mixing could potentially result in duplicate batch ids. 704 """ 705 entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, 706 operation_string=BATCH_INSERT) 707 708 def AddUpdate(self, entry, batch_id_string=None): 709 """Add an update request to the list of batch operations in this feed. 710 711 Sets the operation type of the entry to insert if it is not already set 712 and assigns the desired batch id to the entry so that it can be 713 referenced in the server's response. 714 715 Args: 716 entry: BatchEntry The entry which will be sent to the server as an 717 update (HTTP PUT) request. The item must have a valid atom id 718 so that the server knows which entry to replace. 719 batch_id_string: str (optional) The batch ID to be used to reference 720 this batch operation in the results feed. If this parameter is None, 721 the current length of the feed's entry array will be used as a 722 count. See also comments for AddInsert. 723 """ 724 entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, 725 operation_string=BATCH_UPDATE) 726 727 def AddDelete(self, url_string=None, entry=None, batch_id_string=None): 728 """Adds a delete request to the batch request feed. 729 730 This method takes either the url_string which is the atom id of the item 731 to be deleted, or the entry itself. The atom id of the entry must be 732 present so that the server knows which entry should be deleted. 733 734 Args: 735 url_string: str (optional) The URL of the entry to be deleted. You can 736 find this URL in the text member of the atom id for an entry. 737 entry: BatchEntry (optional) The entry to be deleted. 738 batch_id_string: str (optional) 739 740 Raises: 741 MissingRequiredParameters: Raised if neither a url_string nor an entry 742 are provided in the request. 743 """ 744 entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, 745 batch_id_string=batch_id_string, 746 operation_string=BATCH_DELETE) 747 748 def AddQuery(self, url_string=None, entry=None, batch_id_string=None): 749 """Adds a query request to the batch request feed. 750 751 This method takes either the url_string which is the query URL 752 whose results will be added to the result feed. The query URL will 753 be encapsulated in a BatchEntry, and you may pass in the BatchEntry 754 with a query URL instead of sending a url_string. 755 756 Args: 757 url_string: str (optional) 758 entry: BatchEntry (optional) 759 batch_id_string: str (optional) 760 761 Raises: 762 MissingRequiredParameters 763 """ 764 entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, 765 batch_id_string=batch_id_string, 766 operation_string=BATCH_QUERY) 767 768 def GetBatchLink(self): 769 for link in self.link: 770 if link.rel == 'http://schemas.google.com/g/2005#batch': 771 return link 772 return None 773 774 775def BatchFeedFromString(xml_string): 776 return atom.CreateClassFromXMLString(BatchFeed, xml_string) 777 778 779class EntryLink(atom.AtomBase): 780 """The gd:entryLink element""" 781 782 _tag = 'entryLink' 783 _namespace = GDATA_NAMESPACE 784 _children = atom.AtomBase._children.copy() 785 _attributes = atom.AtomBase._attributes.copy() 786 # The entry used to be an atom.Entry, now it is a GDataEntry. 787 _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', GDataEntry) 788 _attributes['rel'] = 'rel' 789 _attributes['readOnly'] = 'read_only' 790 _attributes['href'] = 'href' 791 792 def __init__(self, href=None, read_only=None, rel=None, 793 entry=None, extension_elements=None, 794 extension_attributes=None, text=None): 795 self.href = href 796 self.read_only = read_only 797 self.rel = rel 798 self.entry = entry 799 self.text = text 800 self.extension_elements = extension_elements or [] 801 self.extension_attributes = extension_attributes or {} 802 803 804def EntryLinkFromString(xml_string): 805 return atom.CreateClassFromXMLString(EntryLink, xml_string) 806 807 808class FeedLink(atom.AtomBase): 809 """The gd:feedLink element""" 810 811 _tag = 'feedLink' 812 _namespace = GDATA_NAMESPACE 813 _children = atom.AtomBase._children.copy() 814 _attributes = atom.AtomBase._attributes.copy() 815 _children['{%s}feed' % atom.ATOM_NAMESPACE] = ('feed', GDataFeed) 816 _attributes['rel'] = 'rel' 817 _attributes['readOnly'] = 'read_only' 818 _attributes['countHint'] = 'count_hint' 819 _attributes['href'] = 'href' 820 821 def __init__(self, count_hint=None, href=None, read_only=None, rel=None, 822 feed=None, extension_elements=None, extension_attributes=None, 823 text=None): 824 self.count_hint = count_hint 825 self.href = href 826 self.read_only = read_only 827 self.rel = rel 828 self.feed = feed 829 self.text = text 830 self.extension_elements = extension_elements or [] 831 self.extension_attributes = extension_attributes or {} 832 833 834def FeedLinkFromString(xml_string): 835 return atom.CreateClassFromXMLString(FeedLink, xml_string)