| #!/usr/bin/python |
| # |
| # Copyright (C) 2006 Google Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| |
| """Contains classes representing Google Data elements. |
| |
| Extends Atom classes to add Google Data specific elements. |
| """ |
| |
| |
| __author__ = 'j.s@google.com (Jeffrey Scudder)' |
| |
| import os |
| import atom |
| try: |
| from xml.etree import cElementTree as ElementTree |
| except ImportError: |
| try: |
| import cElementTree as ElementTree |
| except ImportError: |
| try: |
| from xml.etree import ElementTree |
| except ImportError: |
| from elementtree import ElementTree |
| |
| |
| # XML namespaces which are often used in GData entities. |
| GDATA_NAMESPACE = 'http://schemas.google.com/g/2005' |
| GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s' |
| OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/' |
| OPENSEARCH_TEMPLATE = '{http://a9.com/-/spec/opensearchrss/1.0/}%s' |
| BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch' |
| GACL_NAMESPACE = 'http://schemas.google.com/acl/2007' |
| GACL_TEMPLATE = '{http://schemas.google.com/acl/2007}%s' |
| |
| |
| # Labels used in batch request entries to specify the desired CRUD operation. |
| BATCH_INSERT = 'insert' |
| BATCH_UPDATE = 'update' |
| BATCH_DELETE = 'delete' |
| BATCH_QUERY = 'query' |
| |
| class Error(Exception): |
| pass |
| |
| |
| class MissingRequiredParameters(Error): |
| pass |
| |
| |
| class MediaSource(object): |
| """GData Entries can refer to media sources, so this class provides a |
| place to store references to these objects along with some metadata. |
| """ |
| |
| def __init__(self, file_handle=None, content_type=None, content_length=None, |
| file_path=None, file_name=None): |
| """Creates an object of type MediaSource. |
| |
| Args: |
| file_handle: A file handle pointing to the file to be encapsulated in the |
| MediaSource |
| content_type: string The MIME type of the file. Required if a file_handle |
| is given. |
| content_length: int The size of the file. Required if a file_handle is |
| given. |
| file_path: string (optional) A full path name to the file. Used in |
| place of a file_handle. |
| file_name: string The name of the file without any path information. |
| Required if a file_handle is given. |
| """ |
| self.file_handle = file_handle |
| self.content_type = content_type |
| self.content_length = content_length |
| self.file_name = file_name |
| |
| if (file_handle is None and content_type is not None and |
| file_path is not None): |
| self.setFile(file_path, content_type) |
| |
| def setFile(self, file_name, content_type): |
| """A helper function which can create a file handle from a given filename |
| and set the content type and length all at once. |
| |
| Args: |
| file_name: string The path and file name to the file containing the media |
| content_type: string A MIME type representing the type of the media |
| """ |
| |
| self.file_handle = open(file_name, 'rb') |
| self.content_type = content_type |
| self.content_length = os.path.getsize(file_name) |
| self.file_name = os.path.basename(file_name) |
| |
| |
| class LinkFinder(atom.LinkFinder): |
| """An "interface" providing methods to find link elements |
| |
| GData Entry elements often contain multiple links which differ in the rel |
| attribute or content type. Often, developers are interested in a specific |
| type of link so this class provides methods to find specific classes of |
| links. |
| |
| This class is used as a mixin in GData entries. |
| """ |
| |
| def GetSelfLink(self): |
| """Find the first link with rel set to 'self' |
| |
| Returns: |
| An atom.Link or none if none of the links had rel equal to 'self' |
| """ |
| |
| for a_link in self.link: |
| if a_link.rel == 'self': |
| return a_link |
| return None |
| |
| def GetEditLink(self): |
| for a_link in self.link: |
| if a_link.rel == 'edit': |
| return a_link |
| return None |
| |
| def GetEditMediaLink(self): |
| """The Picasa API mistakenly returns media-edit rather than edit-media, but |
| this may change soon. |
| """ |
| for a_link in self.link: |
| if a_link.rel == 'edit-media': |
| return a_link |
| if a_link.rel == 'media-edit': |
| return a_link |
| return None |
| |
| def GetHtmlLink(self): |
| """Find the first link with rel of alternate and type of text/html |
| |
| Returns: |
| An atom.Link or None if no links matched |
| """ |
| for a_link in self.link: |
| if a_link.rel == 'alternate' and a_link.type == 'text/html': |
| return a_link |
| return None |
| |
| def GetPostLink(self): |
| """Get a link containing the POST target URL. |
| |
| The POST target URL is used to insert new entries. |
| |
| Returns: |
| A link object with a rel matching the POST type. |
| """ |
| for a_link in self.link: |
| if a_link.rel == 'http://schemas.google.com/g/2005#post': |
| return a_link |
| return None |
| |
| def GetAclLink(self): |
| for a_link in self.link: |
| if a_link.rel == 'http://schemas.google.com/acl/2007#accessControlList': |
| return a_link |
| return None |
| |
| def GetFeedLink(self): |
| for a_link in self.link: |
| if a_link.rel == 'http://schemas.google.com/g/2005#feed': |
| return a_link |
| return None |
| |
| def GetNextLink(self): |
| for a_link in self.link: |
| if a_link.rel == 'next': |
| return a_link |
| return None |
| |
| def GetPrevLink(self): |
| for a_link in self.link: |
| if a_link.rel == 'previous': |
| return a_link |
| return None |
| |
| |
| class TotalResults(atom.AtomBase): |
| """opensearch:TotalResults for a GData feed""" |
| |
| _tag = 'totalResults' |
| _namespace = OPENSEARCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| |
| def __init__(self, extension_elements=None, |
| extension_attributes=None, text=None): |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def TotalResultsFromString(xml_string): |
| return atom.CreateClassFromXMLString(TotalResults, xml_string) |
| |
| |
| class StartIndex(atom.AtomBase): |
| """The opensearch:startIndex element in GData feed""" |
| |
| _tag = 'startIndex' |
| _namespace = OPENSEARCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| |
| def __init__(self, extension_elements=None, |
| extension_attributes=None, text=None): |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def StartIndexFromString(xml_string): |
| return atom.CreateClassFromXMLString(StartIndex, xml_string) |
| |
| |
| class ItemsPerPage(atom.AtomBase): |
| """The opensearch:itemsPerPage element in GData feed""" |
| |
| _tag = 'itemsPerPage' |
| _namespace = OPENSEARCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| |
| def __init__(self, extension_elements=None, |
| extension_attributes=None, text=None): |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def ItemsPerPageFromString(xml_string): |
| return atom.CreateClassFromXMLString(ItemsPerPage, xml_string) |
| |
| |
| class ExtendedProperty(atom.AtomBase): |
| """The Google Data extendedProperty element. |
| |
| Used to store arbitrary key-value information specific to your |
| application. The value can either be a text string stored as an XML |
| attribute (.value), or an XML node (XmlBlob) as a child element. |
| |
| This element is used in the Google Calendar data API and the Google |
| Contacts data API. |
| """ |
| |
| _tag = 'extendedProperty' |
| _namespace = GDATA_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| _attributes['name'] = 'name' |
| _attributes['value'] = 'value' |
| |
| def __init__(self, name=None, value=None, extension_elements=None, |
| extension_attributes=None, text=None): |
| self.name = name |
| self.value = value |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| def GetXmlBlobExtensionElement(self): |
| """Returns the XML blob as an atom.ExtensionElement. |
| |
| Returns: |
| An atom.ExtensionElement representing the blob's XML, or None if no |
| blob was set. |
| """ |
| if len(self.extension_elements) < 1: |
| return None |
| else: |
| return self.extension_elements[0] |
| |
| def GetXmlBlobString(self): |
| """Returns the XML blob as a string. |
| |
| Returns: |
| A string containing the blob's XML, or None if no blob was set. |
| """ |
| blob = self.GetXmlBlobExtensionElement() |
| if blob: |
| return blob.ToString() |
| return None |
| |
| def SetXmlBlob(self, blob): |
| """Sets the contents of the extendedProperty to XML as a child node. |
| |
| Since the extendedProperty is only allowed one child element as an XML |
| blob, setting the XML blob will erase any preexisting extension elements |
| in this object. |
| |
| Args: |
| blob: str, ElementTree Element or atom.ExtensionElement representing |
| the XML blob stored in the extendedProperty. |
| """ |
| # Erase any existing extension_elements, clears the child nodes from the |
| # extendedProperty. |
| self.extension_elements = [] |
| if isinstance(blob, atom.ExtensionElement): |
| self.extension_elements.append(blob) |
| elif ElementTree.iselement(blob): |
| self.extension_elements.append(atom._ExtensionElementFromElementTree( |
| blob)) |
| else: |
| self.extension_elements.append(atom.ExtensionElementFromString(blob)) |
| |
| |
| def ExtendedPropertyFromString(xml_string): |
| return atom.CreateClassFromXMLString(ExtendedProperty, xml_string) |
| |
| |
| class GDataEntry(atom.Entry, LinkFinder): |
| """Extends Atom Entry to provide data processing""" |
| |
| _tag = atom.Entry._tag |
| _namespace = atom.Entry._namespace |
| _children = atom.Entry._children.copy() |
| _attributes = atom.Entry._attributes.copy() |
| |
| def __GetId(self): |
| return self.__id |
| |
| # This method was created to strip the unwanted whitespace from the id's |
| # text node. |
| def __SetId(self, id): |
| self.__id = id |
| if id is not None and id.text is not None: |
| self.__id.text = id.text.strip() |
| |
| id = property(__GetId, __SetId) |
| |
| def IsMedia(self): |
| """Determines whether or not an entry is a GData Media entry. |
| """ |
| if (self.GetEditMediaLink()): |
| return True |
| else: |
| return False |
| |
| def GetMediaURL(self): |
| """Returns the URL to the media content, if the entry is a media entry. |
| Otherwise returns None. |
| """ |
| if not self.IsMedia(): |
| return None |
| else: |
| return self.content.src |
| |
| |
| def GDataEntryFromString(xml_string): |
| """Creates a new GDataEntry instance given a string of XML.""" |
| return atom.CreateClassFromXMLString(GDataEntry, xml_string) |
| |
| |
| class GDataFeed(atom.Feed, LinkFinder): |
| """A Feed from a GData service""" |
| |
| _tag = 'feed' |
| _namespace = atom.ATOM_NAMESPACE |
| _children = atom.Feed._children.copy() |
| _attributes = atom.Feed._attributes.copy() |
| _children['{%s}totalResults' % OPENSEARCH_NAMESPACE] = ('total_results', |
| TotalResults) |
| _children['{%s}startIndex' % OPENSEARCH_NAMESPACE] = ('start_index', |
| StartIndex) |
| _children['{%s}itemsPerPage' % OPENSEARCH_NAMESPACE] = ('items_per_page', |
| ItemsPerPage) |
| # Add a conversion rule for atom:entry to make it into a GData |
| # Entry. |
| _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GDataEntry]) |
| |
| def __GetId(self): |
| return self.__id |
| |
| def __SetId(self, id): |
| self.__id = id |
| if id is not None and id.text is not None: |
| self.__id.text = id.text.strip() |
| |
| id = property(__GetId, __SetId) |
| |
| def __GetGenerator(self): |
| return self.__generator |
| |
| def __SetGenerator(self, generator): |
| self.__generator = generator |
| if generator is not None: |
| self.__generator.text = generator.text.strip() |
| |
| generator = property(__GetGenerator, __SetGenerator) |
| |
| def __init__(self, author=None, category=None, contributor=None, |
| generator=None, icon=None, atom_id=None, link=None, logo=None, |
| rights=None, subtitle=None, title=None, updated=None, entry=None, |
| total_results=None, start_index=None, items_per_page=None, |
| extension_elements=None, extension_attributes=None, text=None): |
| """Constructor for Source |
| |
| Args: |
| author: list (optional) A list of Author instances which belong to this |
| class. |
| category: list (optional) A list of Category instances |
| contributor: list (optional) A list on Contributor instances |
| generator: Generator (optional) |
| icon: Icon (optional) |
| id: Id (optional) The entry's Id element |
| link: list (optional) A list of Link instances |
| logo: Logo (optional) |
| rights: Rights (optional) The entry's Rights element |
| subtitle: Subtitle (optional) The entry's subtitle element |
| title: Title (optional) the entry's title element |
| updated: Updated (optional) the entry's updated element |
| entry: list (optional) A list of the Entry instances contained in the |
| feed. |
| text: String (optional) The text contents of the element. This is the |
| contents of the Entry's XML text node. |
| (Example: <foo>This is the text</foo>) |
| extension_elements: list (optional) A list of ExtensionElement instances |
| which are children of this element. |
| extension_attributes: dict (optional) A dictionary of strings which are |
| the values for additional XML attributes of this element. |
| """ |
| |
| self.author = author or [] |
| self.category = category or [] |
| self.contributor = contributor or [] |
| self.generator = generator |
| self.icon = icon |
| self.id = atom_id |
| self.link = link or [] |
| self.logo = logo |
| self.rights = rights |
| self.subtitle = subtitle |
| self.title = title |
| self.updated = updated |
| self.entry = entry or [] |
| self.total_results = total_results |
| self.start_index = start_index |
| self.items_per_page = items_per_page |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def GDataFeedFromString(xml_string): |
| return atom.CreateClassFromXMLString(GDataFeed, xml_string) |
| |
| |
| class BatchId(atom.AtomBase): |
| _tag = 'id' |
| _namespace = BATCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| |
| |
| def BatchIdFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchId, xml_string) |
| |
| |
| class BatchOperation(atom.AtomBase): |
| _tag = 'operation' |
| _namespace = BATCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| _attributes['type'] = 'type' |
| |
| def __init__(self, op_type=None, extension_elements=None, |
| extension_attributes=None, |
| text=None): |
| self.type = op_type |
| atom.AtomBase.__init__(self, |
| extension_elements=extension_elements, |
| extension_attributes=extension_attributes, |
| text=text) |
| |
| |
| def BatchOperationFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchOperation, xml_string) |
| |
| |
| class BatchStatus(atom.AtomBase): |
| """The batch:status element present in a batch response entry. |
| |
| A status element contains the code (HTTP response code) and |
| reason as elements. In a single request these fields would |
| be part of the HTTP response, but in a batch request each |
| Entry operation has a corresponding Entry in the response |
| feed which includes status information. |
| |
| See http://code.google.com/apis/gdata/batch.html#Handling_Errors |
| """ |
| |
| _tag = 'status' |
| _namespace = BATCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| _attributes['code'] = 'code' |
| _attributes['reason'] = 'reason' |
| _attributes['content-type'] = 'content_type' |
| |
| def __init__(self, code=None, reason=None, content_type=None, |
| extension_elements=None, extension_attributes=None, text=None): |
| self.code = code |
| self.reason = reason |
| self.content_type = content_type |
| atom.AtomBase.__init__(self, extension_elements=extension_elements, |
| extension_attributes=extension_attributes, |
| text=text) |
| |
| |
| def BatchStatusFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchStatus, xml_string) |
| |
| |
| class BatchEntry(GDataEntry): |
| """An atom:entry for use in batch requests. |
| |
| The BatchEntry contains additional members to specify the operation to be |
| performed on this entry and a batch ID so that the server can reference |
| individual operations in the response feed. For more information, see: |
| http://code.google.com/apis/gdata/batch.html |
| """ |
| |
| _tag = GDataEntry._tag |
| _namespace = GDataEntry._namespace |
| _children = GDataEntry._children.copy() |
| _children['{%s}operation' % BATCH_NAMESPACE] = ('batch_operation', BatchOperation) |
| _children['{%s}id' % BATCH_NAMESPACE] = ('batch_id', BatchId) |
| _children['{%s}status' % BATCH_NAMESPACE] = ('batch_status', BatchStatus) |
| _attributes = GDataEntry._attributes.copy() |
| |
| def __init__(self, author=None, category=None, content=None, |
| contributor=None, atom_id=None, link=None, published=None, rights=None, |
| source=None, summary=None, control=None, title=None, updated=None, |
| batch_operation=None, batch_id=None, batch_status=None, |
| extension_elements=None, extension_attributes=None, text=None): |
| self.batch_operation = batch_operation |
| self.batch_id = batch_id |
| self.batch_status = batch_status |
| GDataEntry.__init__(self, author=author, category=category, |
| content=content, contributor=contributor, atom_id=atom_id, link=link, |
| published=published, rights=rights, source=source, summary=summary, |
| control=control, title=title, updated=updated, |
| extension_elements=extension_elements, |
| extension_attributes=extension_attributes, text=text) |
| |
| |
| def BatchEntryFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchEntry, xml_string) |
| |
| |
| class BatchInterrupted(atom.AtomBase): |
| """The batch:interrupted element sent if batch request was interrupted. |
| |
| Only appears in a feed if some of the batch entries could not be processed. |
| See: http://code.google.com/apis/gdata/batch.html#Handling_Errors |
| """ |
| |
| _tag = 'interrupted' |
| _namespace = BATCH_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| _attributes['reason'] = 'reason' |
| _attributes['success'] = 'success' |
| _attributes['failures'] = 'failures' |
| _attributes['parsed'] = 'parsed' |
| |
| def __init__(self, reason=None, success=None, failures=None, parsed=None, |
| extension_elements=None, extension_attributes=None, text=None): |
| self.reason = reason |
| self.success = success |
| self.failures = failures |
| self.parsed = parsed |
| atom.AtomBase.__init__(self, extension_elements=extension_elements, |
| extension_attributes=extension_attributes, |
| text=text) |
| |
| |
| def BatchInterruptedFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchInterrupted, xml_string) |
| |
| |
| class BatchFeed(GDataFeed): |
| """A feed containing a list of batch request entries.""" |
| |
| _tag = GDataFeed._tag |
| _namespace = GDataFeed._namespace |
| _children = GDataFeed._children.copy() |
| _attributes = GDataFeed._attributes.copy() |
| _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BatchEntry]) |
| _children['{%s}interrupted' % BATCH_NAMESPACE] = ('interrupted', BatchInterrupted) |
| |
| def __init__(self, author=None, category=None, contributor=None, |
| generator=None, icon=None, atom_id=None, link=None, logo=None, |
| rights=None, subtitle=None, title=None, updated=None, entry=None, |
| total_results=None, start_index=None, items_per_page=None, |
| interrupted=None, |
| extension_elements=None, extension_attributes=None, text=None): |
| self.interrupted = interrupted |
| GDataFeed.__init__(self, author=author, category=category, |
| contributor=contributor, generator=generator, |
| icon=icon, atom_id=atom_id, link=link, |
| logo=logo, rights=rights, subtitle=subtitle, |
| title=title, updated=updated, entry=entry, |
| total_results=total_results, start_index=start_index, |
| items_per_page=items_per_page, |
| extension_elements=extension_elements, |
| extension_attributes=extension_attributes, |
| text=text) |
| |
| def AddBatchEntry(self, entry=None, id_url_string=None, |
| batch_id_string=None, operation_string=None): |
| """Logic for populating members of a BatchEntry and adding to the feed. |
| |
| |
| If the entry is not a BatchEntry, it is converted to a BatchEntry so |
| that the batch specific members will be present. |
| |
| The id_url_string can be used in place of an entry if the batch operation |
| applies to a URL. For example query and delete operations require just |
| the URL of an entry, no body is sent in the HTTP request. If an |
| id_url_string is sent instead of an entry, a BatchEntry is created and |
| added to the feed. |
| |
| This method also assigns the desired batch id to the entry so that it |
| can be referenced in the server's response. If the batch_id_string is |
| None, this method will assign a batch_id to be the index at which this |
| entry will be in the feed's entry list. |
| |
| Args: |
| entry: BatchEntry, atom.Entry, or another Entry flavor (optional) The |
| entry which will be sent to the server as part of the batch request. |
| The item must have a valid atom id so that the server knows which |
| entry this request references. |
| id_url_string: str (optional) The URL of the entry to be acted on. You |
| can find this URL in the text member of the atom id for an entry. |
| If an entry is not sent, this id will be used to construct a new |
| BatchEntry which will be added to the request feed. |
| batch_id_string: str (optional) The batch ID to be used to reference |
| this batch operation in the results feed. If this parameter is None, |
| the current length of the feed's entry array will be used as a |
| count. Note that batch_ids should either always be specified or |
| never, mixing could potentially result in duplicate batch ids. |
| operation_string: str (optional) The desired batch operation which will |
| set the batch_operation.type member of the entry. Options are |
| 'insert', 'update', 'delete', and 'query' |
| |
| Raises: |
| MissingRequiredParameters: Raised if neither an id_ url_string nor an |
| entry are provided in the request. |
| |
| Returns: |
| The added entry. |
| """ |
| if entry is None and id_url_string is None: |
| raise MissingRequiredParameters('supply either an entry or URL string') |
| if entry is None and id_url_string is not None: |
| entry = BatchEntry(atom_id=atom.Id(text=id_url_string)) |
| # TODO: handle cases in which the entry lacks batch_... members. |
| #if not isinstance(entry, BatchEntry): |
| # Convert the entry to a batch entry. |
| if batch_id_string is not None: |
| entry.batch_id = BatchId(text=batch_id_string) |
| elif entry.batch_id is None or entry.batch_id.text is None: |
| entry.batch_id = BatchId(text=str(len(self.entry))) |
| if operation_string is not None: |
| entry.batch_operation = BatchOperation(op_type=operation_string) |
| self.entry.append(entry) |
| return entry |
| |
| def AddInsert(self, entry, batch_id_string=None): |
| """Add an insert request to the operations in this batch request feed. |
| |
| If the entry doesn't yet have an operation or a batch id, these will |
| be set to the insert operation and a batch_id specified as a parameter. |
| |
| Args: |
| entry: BatchEntry The entry which will be sent in the batch feed as an |
| insert request. |
| batch_id_string: str (optional) The batch ID to be used to reference |
| this batch operation in the results feed. If this parameter is None, |
| the current length of the feed's entry array will be used as a |
| count. Note that batch_ids should either always be specified or |
| never, mixing could potentially result in duplicate batch ids. |
| """ |
| entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, |
| operation_string=BATCH_INSERT) |
| |
| def AddUpdate(self, entry, batch_id_string=None): |
| """Add an update request to the list of batch operations in this feed. |
| |
| Sets the operation type of the entry to insert if it is not already set |
| and assigns the desired batch id to the entry so that it can be |
| referenced in the server's response. |
| |
| Args: |
| entry: BatchEntry The entry which will be sent to the server as an |
| update (HTTP PUT) request. The item must have a valid atom id |
| so that the server knows which entry to replace. |
| batch_id_string: str (optional) The batch ID to be used to reference |
| this batch operation in the results feed. If this parameter is None, |
| the current length of the feed's entry array will be used as a |
| count. See also comments for AddInsert. |
| """ |
| entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, |
| operation_string=BATCH_UPDATE) |
| |
| def AddDelete(self, url_string=None, entry=None, batch_id_string=None): |
| """Adds a delete request to the batch request feed. |
| |
| This method takes either the url_string which is the atom id of the item |
| to be deleted, or the entry itself. The atom id of the entry must be |
| present so that the server knows which entry should be deleted. |
| |
| Args: |
| url_string: str (optional) The URL of the entry to be deleted. You can |
| find this URL in the text member of the atom id for an entry. |
| entry: BatchEntry (optional) The entry to be deleted. |
| batch_id_string: str (optional) |
| |
| Raises: |
| MissingRequiredParameters: Raised if neither a url_string nor an entry |
| are provided in the request. |
| """ |
| entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, |
| batch_id_string=batch_id_string, |
| operation_string=BATCH_DELETE) |
| |
| def AddQuery(self, url_string=None, entry=None, batch_id_string=None): |
| """Adds a query request to the batch request feed. |
| |
| This method takes either the url_string which is the query URL |
| whose results will be added to the result feed. The query URL will |
| be encapsulated in a BatchEntry, and you may pass in the BatchEntry |
| with a query URL instead of sending a url_string. |
| |
| Args: |
| url_string: str (optional) |
| entry: BatchEntry (optional) |
| batch_id_string: str (optional) |
| |
| Raises: |
| MissingRequiredParameters |
| """ |
| entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, |
| batch_id_string=batch_id_string, |
| operation_string=BATCH_QUERY) |
| |
| def GetBatchLink(self): |
| for link in self.link: |
| if link.rel == 'http://schemas.google.com/g/2005#batch': |
| return link |
| return None |
| |
| |
| def BatchFeedFromString(xml_string): |
| return atom.CreateClassFromXMLString(BatchFeed, xml_string) |
| |
| |
| class EntryLink(atom.AtomBase): |
| """The gd:entryLink element""" |
| |
| _tag = 'entryLink' |
| _namespace = GDATA_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| # The entry used to be an atom.Entry, now it is a GDataEntry. |
| _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', GDataEntry) |
| _attributes['rel'] = 'rel' |
| _attributes['readOnly'] = 'read_only' |
| _attributes['href'] = 'href' |
| |
| def __init__(self, href=None, read_only=None, rel=None, |
| entry=None, extension_elements=None, |
| extension_attributes=None, text=None): |
| self.href = href |
| self.read_only = read_only |
| self.rel = rel |
| self.entry = entry |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def EntryLinkFromString(xml_string): |
| return atom.CreateClassFromXMLString(EntryLink, xml_string) |
| |
| |
| class FeedLink(atom.AtomBase): |
| """The gd:feedLink element""" |
| |
| _tag = 'feedLink' |
| _namespace = GDATA_NAMESPACE |
| _children = atom.AtomBase._children.copy() |
| _attributes = atom.AtomBase._attributes.copy() |
| _children['{%s}feed' % atom.ATOM_NAMESPACE] = ('feed', GDataFeed) |
| _attributes['rel'] = 'rel' |
| _attributes['readOnly'] = 'read_only' |
| _attributes['countHint'] = 'count_hint' |
| _attributes['href'] = 'href' |
| |
| def __init__(self, count_hint=None, href=None, read_only=None, rel=None, |
| feed=None, extension_elements=None, extension_attributes=None, |
| text=None): |
| self.count_hint = count_hint |
| self.href = href |
| self.read_only = read_only |
| self.rel = rel |
| self.feed = feed |
| self.text = text |
| self.extension_elements = extension_elements or [] |
| self.extension_attributes = extension_attributes or {} |
| |
| |
| def FeedLinkFromString(xml_string): |
| return atom.CreateClassFromXMLString(FeedLink, xml_string) |