faset over fra Z3950 til google books
This commit is contained in:
474
python/gdata/spreadsheet/__init__.py
Normal file
474
python/gdata/spreadsheet/__init__.py
Normal file
@@ -0,0 +1,474 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2007 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 extensions to Atom objects used with Google Spreadsheets.
|
||||
"""
|
||||
|
||||
__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)'
|
||||
|
||||
|
||||
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
|
||||
import atom
|
||||
import gdata
|
||||
import re
|
||||
import string
|
||||
|
||||
|
||||
# XML namespaces which are often used in Google Spreadsheets entities.
|
||||
GSPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006'
|
||||
GSPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s'
|
||||
|
||||
GSPREADSHEETS_EXTENDED_NAMESPACE = ('http://schemas.google.com/spreadsheets'
|
||||
'/2006/extended')
|
||||
GSPREADSHEETS_EXTENDED_TEMPLATE = ('{http://schemas.google.com/spreadsheets'
|
||||
'/2006/extended}%s')
|
||||
|
||||
|
||||
class ColCount(atom.AtomBase):
|
||||
"""The Google Spreadsheets colCount element """
|
||||
|
||||
_tag = 'colCount'
|
||||
_namespace = GSPREADSHEETS_NAMESPACE
|
||||
_children = atom.AtomBase._children.copy()
|
||||
_attributes = atom.AtomBase._attributes.copy()
|
||||
|
||||
def __init__(self, text=None, extension_elements=None,
|
||||
extension_attributes=None):
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
def ColCountFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(ColCount, xml_string)
|
||||
|
||||
|
||||
class RowCount(atom.AtomBase):
|
||||
"""The Google Spreadsheets rowCount element """
|
||||
|
||||
_tag = 'rowCount'
|
||||
_namespace = GSPREADSHEETS_NAMESPACE
|
||||
_children = atom.AtomBase._children.copy()
|
||||
_attributes = atom.AtomBase._attributes.copy()
|
||||
|
||||
def __init__(self, text=None, extension_elements=None,
|
||||
extension_attributes=None):
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
def RowCountFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(RowCount, xml_string)
|
||||
|
||||
|
||||
class Cell(atom.AtomBase):
|
||||
"""The Google Spreadsheets cell element """
|
||||
|
||||
_tag = 'cell'
|
||||
_namespace = GSPREADSHEETS_NAMESPACE
|
||||
_children = atom.AtomBase._children.copy()
|
||||
_attributes = atom.AtomBase._attributes.copy()
|
||||
_attributes['row'] = 'row'
|
||||
_attributes['col'] = 'col'
|
||||
_attributes['inputValue'] = 'inputValue'
|
||||
_attributes['numericValue'] = 'numericValue'
|
||||
|
||||
def __init__(self, text=None, row=None, col=None, inputValue=None,
|
||||
numericValue=None, extension_elements=None, extension_attributes=None):
|
||||
self.text = text
|
||||
self.row = row
|
||||
self.col = col
|
||||
self.inputValue = inputValue
|
||||
self.numericValue = numericValue
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
def CellFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(Cell, xml_string)
|
||||
|
||||
|
||||
class Custom(atom.AtomBase):
|
||||
"""The Google Spreadsheets custom element"""
|
||||
|
||||
_namespace = GSPREADSHEETS_EXTENDED_NAMESPACE
|
||||
_children = atom.AtomBase._children.copy()
|
||||
_attributes = atom.AtomBase._attributes.copy()
|
||||
|
||||
def __init__(self, column=None, text=None, extension_elements=None,
|
||||
extension_attributes=None):
|
||||
self.column = column # The name of the column
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
def _BecomeChildElement(self, tree):
|
||||
new_child = ElementTree.Element('')
|
||||
tree.append(new_child)
|
||||
new_child.tag = '{%s}%s' % (self.__class__._namespace,
|
||||
self.column)
|
||||
self._AddMembersToElementTree(new_child)
|
||||
|
||||
def _ToElementTree(self):
|
||||
new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace,
|
||||
self.column))
|
||||
self._AddMembersToElementTree(new_tree)
|
||||
return new_tree
|
||||
|
||||
def _HarvestElementTree(self, tree):
|
||||
namespace_uri, local_tag = string.split(tree.tag[1:], "}", 1)
|
||||
self.column = local_tag
|
||||
# Fill in the instance members from the contents of the XML tree.
|
||||
for child in tree:
|
||||
self._ConvertElementTreeToMember(child)
|
||||
for attribute, value in tree.attrib.iteritems():
|
||||
self._ConvertElementAttributeToMember(attribute, value)
|
||||
self.text = tree.text
|
||||
|
||||
|
||||
def CustomFromString(xml_string):
|
||||
element_tree = ElementTree.fromstring(xml_string)
|
||||
return _CustomFromElementTree(element_tree)
|
||||
|
||||
|
||||
def _CustomFromElementTree(element_tree):
|
||||
namespace_uri, local_tag = string.split(element_tree.tag[1:], "}", 1)
|
||||
if namespace_uri == GSPREADSHEETS_EXTENDED_NAMESPACE:
|
||||
new_custom = Custom()
|
||||
new_custom._HarvestElementTree(element_tree)
|
||||
new_custom.column = local_tag
|
||||
return new_custom
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SpreadsheetsSpreadsheet(gdata.GDataEntry):
|
||||
"""A Google Spreadsheets flavor of a Spreadsheet Atom Entry """
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.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, title=None, control=None, updated=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
self.author = author or []
|
||||
self.category = category or []
|
||||
self.content = content
|
||||
self.contributor = contributor or []
|
||||
self.id = atom_id
|
||||
self.link = link or []
|
||||
self.published = published
|
||||
self.rights = rights
|
||||
self.source = source
|
||||
self.summary = summary
|
||||
self.control = control
|
||||
self.title = title
|
||||
self.updated = updated
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
def SpreadsheetsSpreadsheetFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheet,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsWorksheet(gdata.GDataEntry):
|
||||
"""A Google Spreadsheets flavor of a Worksheet Atom Entry """
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.GDataEntry._attributes.copy()
|
||||
_children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count',
|
||||
RowCount)
|
||||
_children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count',
|
||||
ColCount)
|
||||
|
||||
def __init__(self, author=None, category=None, content=None,
|
||||
contributor=None, atom_id=None, link=None, published=None, rights=None,
|
||||
source=None, summary=None, title=None, control=None, updated=None,
|
||||
row_count=None, col_count=None, text=None, extension_elements=None,
|
||||
extension_attributes=None):
|
||||
self.author = author or []
|
||||
self.category = category or []
|
||||
self.content = content
|
||||
self.contributor = contributor or []
|
||||
self.id = atom_id
|
||||
self.link = link or []
|
||||
self.published = published
|
||||
self.rights = rights
|
||||
self.source = source
|
||||
self.summary = summary
|
||||
self.control = control
|
||||
self.title = title
|
||||
self.updated = updated
|
||||
self.row_count = row_count
|
||||
self.col_count = col_count
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
def SpreadsheetsWorksheetFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsWorksheet,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsCell(gdata.BatchEntry):
|
||||
"""A Google Spreadsheets flavor of a Cell Atom Entry """
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.BatchEntry._children.copy()
|
||||
_attributes = gdata.BatchEntry._attributes.copy()
|
||||
_children['{%s}cell' % GSPREADSHEETS_NAMESPACE] = ('cell', Cell)
|
||||
|
||||
def __init__(self, author=None, category=None, content=None,
|
||||
contributor=None, atom_id=None, link=None, published=None, rights=None,
|
||||
source=None, summary=None, title=None, control=None, updated=None,
|
||||
cell=None, batch_operation=None, batch_id=None, batch_status=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
self.author = author or []
|
||||
self.category = category or []
|
||||
self.content = content
|
||||
self.contributor = contributor or []
|
||||
self.id = atom_id
|
||||
self.link = link or []
|
||||
self.published = published
|
||||
self.rights = rights
|
||||
self.source = source
|
||||
self.summary = summary
|
||||
self.control = control
|
||||
self.title = title
|
||||
self.batch_operation = batch_operation
|
||||
self.batch_id = batch_id
|
||||
self.batch_status = batch_status
|
||||
self.updated = updated
|
||||
self.cell = cell
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
|
||||
def SpreadsheetsCellFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsCell,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsList(gdata.GDataEntry):
|
||||
"""A Google Spreadsheets flavor of a List Atom Entry """
|
||||
|
||||
_tag = 'entry'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataEntry._children.copy()
|
||||
_attributes = gdata.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, title=None, control=None, updated=None,
|
||||
custom=None,
|
||||
text=None, extension_elements=None, extension_attributes=None):
|
||||
self.author = author or []
|
||||
self.category = category or []
|
||||
self.content = content
|
||||
self.contributor = contributor or []
|
||||
self.id = atom_id
|
||||
self.link = link or []
|
||||
self.published = published
|
||||
self.rights = rights
|
||||
self.source = source
|
||||
self.summary = summary
|
||||
self.control = control
|
||||
self.title = title
|
||||
self.updated = updated
|
||||
self.custom = custom or {}
|
||||
self.text = text
|
||||
self.extension_elements = extension_elements or []
|
||||
self.extension_attributes = extension_attributes or {}
|
||||
|
||||
# We need to overwrite _ConvertElementTreeToMember to add special logic to
|
||||
# convert custom attributes to members
|
||||
def _ConvertElementTreeToMember(self, child_tree):
|
||||
# Find the element's tag in this class's list of child members
|
||||
if self.__class__._children.has_key(child_tree.tag):
|
||||
member_name = self.__class__._children[child_tree.tag][0]
|
||||
member_class = self.__class__._children[child_tree.tag][1]
|
||||
# If the class member is supposed to contain a list, make sure the
|
||||
# matching member is set to a list, then append the new member
|
||||
# instance to the list.
|
||||
if isinstance(member_class, list):
|
||||
if getattr(self, member_name) is None:
|
||||
setattr(self, member_name, [])
|
||||
getattr(self, member_name).append(atom._CreateClassFromElementTree(
|
||||
member_class[0], child_tree))
|
||||
else:
|
||||
setattr(self, member_name,
|
||||
atom._CreateClassFromElementTree(member_class, child_tree))
|
||||
elif child_tree.tag.find('{%s}' % GSPREADSHEETS_EXTENDED_NAMESPACE) == 0:
|
||||
# If this is in the custom namespace, make add it to the custom dict.
|
||||
name = child_tree.tag[child_tree.tag.index('}')+1:]
|
||||
custom = _CustomFromElementTree(child_tree)
|
||||
if custom:
|
||||
self.custom[name] = custom
|
||||
else:
|
||||
atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree)
|
||||
|
||||
# We need to overwtite _AddMembersToElementTree to add special logic to
|
||||
# convert custom members to XML nodes.
|
||||
def _AddMembersToElementTree(self, tree):
|
||||
# Convert the members of this class which are XML child nodes.
|
||||
# This uses the class's _children dictionary to find the members which
|
||||
# should become XML child nodes.
|
||||
member_node_names = [values[0] for tag, values in
|
||||
self.__class__._children.iteritems()]
|
||||
for member_name in member_node_names:
|
||||
member = getattr(self, member_name)
|
||||
if member is None:
|
||||
pass
|
||||
elif isinstance(member, list):
|
||||
for instance in member:
|
||||
instance._BecomeChildElement(tree)
|
||||
else:
|
||||
member._BecomeChildElement(tree)
|
||||
# Convert the members of this class which are XML attributes.
|
||||
for xml_attribute, member_name in self.__class__._attributes.iteritems():
|
||||
member = getattr(self, member_name)
|
||||
if member is not None:
|
||||
tree.attrib[xml_attribute] = member
|
||||
# Convert all special custom item attributes to nodes
|
||||
for name, custom in self.custom.iteritems():
|
||||
custom._BecomeChildElement(tree)
|
||||
# Lastly, call the ExtensionContainers's _AddMembersToElementTree to
|
||||
# convert any extension attributes.
|
||||
atom.ExtensionContainer._AddMembersToElementTree(self, tree)
|
||||
|
||||
|
||||
def SpreadsheetsListFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsList,
|
||||
xml_string)
|
||||
element_tree = ElementTree.fromstring(xml_string)
|
||||
return _SpreadsheetsListFromElementTree(element_tree)
|
||||
|
||||
|
||||
class SpreadsheetsSpreadsheetsFeed(gdata.GDataFeed):
|
||||
"""A feed containing Google Spreadsheets Spreadsheets"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataFeed._children.copy()
|
||||
_attributes = gdata.GDataFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[SpreadsheetsSpreadsheet])
|
||||
|
||||
|
||||
def SpreadsheetsSpreadsheetsFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheetsFeed,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsWorksheetsFeed(gdata.GDataFeed):
|
||||
"""A feed containing Google Spreadsheets Spreadsheets"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataFeed._children.copy()
|
||||
_attributes = gdata.GDataFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[SpreadsheetsWorksheet])
|
||||
|
||||
|
||||
def SpreadsheetsWorksheetsFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsWorksheetsFeed,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsCellsFeed(gdata.BatchFeed):
|
||||
"""A feed containing Google Spreadsheets Cells"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.BatchFeed._children.copy()
|
||||
_attributes = gdata.BatchFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[SpreadsheetsCell])
|
||||
_children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count',
|
||||
RowCount)
|
||||
_children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count',
|
||||
ColCount)
|
||||
|
||||
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, row_count=None,
|
||||
col_count=None, interrupted=None):
|
||||
gdata.BatchFeed.__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, interrupted=interrupted)
|
||||
self.row_count = row_count
|
||||
self.col_count = col_count
|
||||
|
||||
def GetBatchLink(self):
|
||||
for link in self.link:
|
||||
if link.rel == 'http://schemas.google.com/g/2005#batch':
|
||||
return link
|
||||
return None
|
||||
|
||||
|
||||
def SpreadsheetsCellsFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsCellsFeed,
|
||||
xml_string)
|
||||
|
||||
|
||||
class SpreadsheetsListFeed(gdata.GDataFeed):
|
||||
"""A feed containing Google Spreadsheets Spreadsheets"""
|
||||
|
||||
_tag = 'feed'
|
||||
_namespace = atom.ATOM_NAMESPACE
|
||||
_children = gdata.GDataFeed._children.copy()
|
||||
_attributes = gdata.GDataFeed._attributes.copy()
|
||||
_children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry',
|
||||
[SpreadsheetsList])
|
||||
|
||||
|
||||
def SpreadsheetsListFeedFromString(xml_string):
|
||||
return atom.CreateClassFromXMLString(SpreadsheetsListFeed,
|
||||
xml_string)
|
484
python/gdata/spreadsheet/service.py
Normal file
484
python/gdata/spreadsheet/service.py
Normal file
@@ -0,0 +1,484 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2007 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.
|
||||
|
||||
"""SpreadsheetsService extends the GDataService to streamline Google
|
||||
Spreadsheets operations.
|
||||
|
||||
SpreadsheetService: Provides methods to query feeds and manipulate items.
|
||||
Extends GDataService.
|
||||
|
||||
DictionaryToParamList: Function which converts a dictionary into a list of
|
||||
URL arguments (represented as strings). This is a
|
||||
utility function used in CRUD operations.
|
||||
"""
|
||||
|
||||
__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)'
|
||||
|
||||
|
||||
import gdata
|
||||
import atom.service
|
||||
import gdata.service
|
||||
import gdata.spreadsheet
|
||||
import atom
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
pass
|
||||
|
||||
|
||||
class RequestError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class SpreadsheetsService(gdata.service.GDataService):
|
||||
"""Client for the Google Spreadsheets service."""
|
||||
|
||||
def __init__(self, email=None, password=None, source=None,
|
||||
server='spreadsheets.google.com', additional_headers=None,
|
||||
**kwargs):
|
||||
"""Creates a client for the Google Spreadsheets service.
|
||||
|
||||
Args:
|
||||
email: string (optional) The user's email address, used for
|
||||
authentication.
|
||||
password: string (optional) The user's password.
|
||||
source: string (optional) The name of the user's application.
|
||||
server: string (optional) The name of the server to which a connection
|
||||
will be opened. Default value: 'spreadsheets.google.com'.
|
||||
**kwargs: The other parameters to pass to gdata.service.GDataService
|
||||
constructor.
|
||||
"""
|
||||
gdata.service.GDataService.__init__(
|
||||
self, email=email, password=password, service='wise', source=source,
|
||||
server=server, additional_headers=additional_headers, **kwargs)
|
||||
|
||||
def GetSpreadsheetsFeed(self, key=None, query=None, visibility='private',
|
||||
projection='full'):
|
||||
"""Gets a spreadsheets feed or a specific entry if a key is defined
|
||||
Args:
|
||||
key: string (optional) The spreadsheet key defined in /ccc?key=
|
||||
query: DocumentQuery (optional) Query parameters
|
||||
|
||||
Returns:
|
||||
If there is no key, then a SpreadsheetsSpreadsheetsFeed.
|
||||
If there is a key, then a SpreadsheetsSpreadsheet.
|
||||
"""
|
||||
|
||||
uri = ('https://%s/feeds/spreadsheets/%s/%s'
|
||||
% (self.server, visibility, projection))
|
||||
|
||||
if key is not None:
|
||||
uri = '%s/%s' % (uri, key)
|
||||
|
||||
if query != None:
|
||||
query.feed = uri
|
||||
uri = query.ToUri()
|
||||
|
||||
if key:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsSpreadsheetFromString)
|
||||
else:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsSpreadsheetsFeedFromString)
|
||||
|
||||
def GetWorksheetsFeed(self, key, wksht_id=None, query=None,
|
||||
visibility='private', projection='full'):
|
||||
"""Gets a worksheets feed or a specific entry if a wksht is defined
|
||||
Args:
|
||||
key: string The spreadsheet key defined in /ccc?key=
|
||||
wksht_id: string (optional) The id for a specific worksheet entry
|
||||
query: DocumentQuery (optional) Query parameters
|
||||
|
||||
Returns:
|
||||
If there is no wksht_id, then a SpreadsheetsWorksheetsFeed.
|
||||
If there is a wksht_id, then a SpreadsheetsWorksheet.
|
||||
"""
|
||||
|
||||
uri = ('https://%s/feeds/worksheets/%s/%s/%s'
|
||||
% (self.server, key, visibility, projection))
|
||||
|
||||
if wksht_id != None:
|
||||
uri = '%s/%s' % (uri, wksht_id)
|
||||
|
||||
if query != None:
|
||||
query.feed = uri
|
||||
uri = query.ToUri()
|
||||
|
||||
if wksht_id:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)
|
||||
else:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsWorksheetsFeedFromString)
|
||||
|
||||
def AddWorksheet(self, title, row_count, col_count, key):
|
||||
"""Creates a new worksheet in the desired spreadsheet.
|
||||
|
||||
The new worksheet is appended to the end of the list of worksheets. The
|
||||
new worksheet will only have the available number of columns and cells
|
||||
specified.
|
||||
|
||||
Args:
|
||||
title: str The title which will be displayed in the list of worksheets.
|
||||
row_count: int or str The number of rows in the new worksheet.
|
||||
col_count: int or str The number of columns in the new worksheet.
|
||||
key: str The spreadsheet key to the spreadsheet to which the new
|
||||
worksheet should be added.
|
||||
|
||||
Returns:
|
||||
A SpreadsheetsWorksheet if the new worksheet was created succesfully.
|
||||
"""
|
||||
new_worksheet = gdata.spreadsheet.SpreadsheetsWorksheet(
|
||||
title=atom.Title(text=title),
|
||||
row_count=gdata.spreadsheet.RowCount(text=str(row_count)),
|
||||
col_count=gdata.spreadsheet.ColCount(text=str(col_count)))
|
||||
return self.Post(new_worksheet,
|
||||
'https://%s/feeds/worksheets/%s/private/full' % (self.server, key),
|
||||
converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)
|
||||
|
||||
def UpdateWorksheet(self, worksheet_entry, url=None):
|
||||
"""Changes the size and/or title of the desired worksheet.
|
||||
|
||||
Args:
|
||||
worksheet_entry: SpreadsheetWorksheet The new contents of the
|
||||
worksheet.
|
||||
url: str (optional) The URL to which the edited worksheet entry should
|
||||
be sent. If the url is None, the edit URL from the worksheet will
|
||||
be used.
|
||||
|
||||
Returns:
|
||||
A SpreadsheetsWorksheet with the new information about the worksheet.
|
||||
"""
|
||||
target_url = url or worksheet_entry.GetEditLink().href
|
||||
return self.Put(worksheet_entry, target_url,
|
||||
converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)
|
||||
|
||||
def DeleteWorksheet(self, worksheet_entry=None, url=None):
|
||||
"""Removes the desired worksheet from the spreadsheet
|
||||
|
||||
Args:
|
||||
worksheet_entry: SpreadsheetWorksheet (optional) The worksheet to
|
||||
be deleted. If this is none, then the DELETE reqest is sent to
|
||||
the url specified in the url parameter.
|
||||
url: str (optaional) The URL to which the DELETE request should be
|
||||
sent. If left as None, the worksheet's edit URL is used.
|
||||
|
||||
Returns:
|
||||
True if the worksheet was deleted successfully.
|
||||
"""
|
||||
if url:
|
||||
target_url = url
|
||||
else:
|
||||
target_url = worksheet_entry.GetEditLink().href
|
||||
return self.Delete(target_url)
|
||||
|
||||
def GetCellsFeed(self, key, wksht_id='default', cell=None, query=None,
|
||||
visibility='private', projection='full'):
|
||||
"""Gets a cells feed or a specific entry if a cell is defined
|
||||
Args:
|
||||
key: string The spreadsheet key defined in /ccc?key=
|
||||
wksht_id: string The id for a specific worksheet entry
|
||||
cell: string (optional) The R1C1 address of the cell
|
||||
query: DocumentQuery (optional) Query parameters
|
||||
|
||||
Returns:
|
||||
If there is no cell, then a SpreadsheetsCellsFeed.
|
||||
If there is a cell, then a SpreadsheetsCell.
|
||||
"""
|
||||
|
||||
uri = ('https://%s/feeds/cells/%s/%s/%s/%s'
|
||||
% (self.server, key, wksht_id, visibility, projection))
|
||||
|
||||
if cell != None:
|
||||
uri = '%s/%s' % (uri, cell)
|
||||
|
||||
if query != None:
|
||||
query.feed = uri
|
||||
uri = query.ToUri()
|
||||
|
||||
if cell:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsCellFromString)
|
||||
else:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString)
|
||||
|
||||
def GetListFeed(self, key, wksht_id='default', row_id=None, query=None,
|
||||
visibility='private', projection='full'):
|
||||
"""Gets a list feed or a specific entry if a row_id is defined
|
||||
Args:
|
||||
key: string The spreadsheet key defined in /ccc?key=
|
||||
wksht_id: string The id for a specific worksheet entry
|
||||
row_id: string (optional) The row_id of a row in the list
|
||||
query: DocumentQuery (optional) Query parameters
|
||||
|
||||
Returns:
|
||||
If there is no row_id, then a SpreadsheetsListFeed.
|
||||
If there is a row_id, then a SpreadsheetsList.
|
||||
"""
|
||||
|
||||
uri = ('https://%s/feeds/list/%s/%s/%s/%s'
|
||||
% (self.server, key, wksht_id, visibility, projection))
|
||||
|
||||
if row_id is not None:
|
||||
uri = '%s/%s' % (uri, row_id)
|
||||
|
||||
if query is not None:
|
||||
query.feed = uri
|
||||
uri = query.ToUri()
|
||||
|
||||
if row_id:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsListFromString)
|
||||
else:
|
||||
return self.Get(uri,
|
||||
converter=gdata.spreadsheet.SpreadsheetsListFeedFromString)
|
||||
|
||||
def UpdateCell(self, row, col, inputValue, key, wksht_id='default'):
|
||||
"""Updates an existing cell.
|
||||
|
||||
Args:
|
||||
row: int The row the cell to be editted is in
|
||||
col: int The column the cell to be editted is in
|
||||
inputValue: str the new value of the cell
|
||||
key: str The key of the spreadsheet in which this cell resides.
|
||||
wksht_id: str The ID of the worksheet which holds this cell.
|
||||
|
||||
Returns:
|
||||
The updated cell entry
|
||||
"""
|
||||
row = str(row)
|
||||
col = str(col)
|
||||
# make the new cell
|
||||
new_cell = gdata.spreadsheet.Cell(row=row, col=col, inputValue=inputValue)
|
||||
# get the edit uri and PUT
|
||||
cell = 'R%sC%s' % (row, col)
|
||||
entry = self.GetCellsFeed(key, wksht_id, cell)
|
||||
for a_link in entry.link:
|
||||
if a_link.rel == 'edit':
|
||||
entry.cell = new_cell
|
||||
return self.Put(entry, a_link.href,
|
||||
converter=gdata.spreadsheet.SpreadsheetsCellFromString)
|
||||
|
||||
def _GenerateCellsBatchUrl(self, spreadsheet_key, worksheet_id):
|
||||
return ('https://spreadsheets.google.com/feeds/cells/%s/%s/'
|
||||
'private/full/batch' % (spreadsheet_key, worksheet_id))
|
||||
|
||||
def ExecuteBatch(self, batch_feed, url=None, spreadsheet_key=None,
|
||||
worksheet_id=None,
|
||||
converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString):
|
||||
"""Sends a batch request feed to the server.
|
||||
|
||||
The batch request needs to be sent to the batch URL for a particular
|
||||
worksheet. You can specify the worksheet by providing the spreadsheet_key
|
||||
and worksheet_id, or by sending the URL from the cells feed's batch link.
|
||||
|
||||
Args:
|
||||
batch_feed: gdata.spreadsheet.SpreadsheetsCellFeed A feed containing
|
||||
BatchEntry elements which contain the desired CRUD operation and
|
||||
any necessary data to modify a cell.
|
||||
url: str (optional) The batch URL for the cells feed to which these
|
||||
changes should be applied. This can be found by calling
|
||||
cells_feed.GetBatchLink().href.
|
||||
spreadsheet_key: str (optional) Used to generate the batch request URL
|
||||
if the url argument is None. If using the spreadsheet key to
|
||||
generate the URL, the worksheet id is also required.
|
||||
worksheet_id: str (optional) Used if the url is not provided, it is
|
||||
oart of the batch feed target URL. This is used with the spreadsheet
|
||||
key.
|
||||
converter: Function (optional) Function to be executed on the server's
|
||||
response. This function should take one string as a parameter. The
|
||||
default value is SpreadsheetsCellsFeedFromString which will turn the result
|
||||
into a gdata.spreadsheet.SpreadsheetsCellsFeed object.
|
||||
|
||||
Returns:
|
||||
A gdata.BatchFeed containing the results.
|
||||
"""
|
||||
|
||||
if url is None:
|
||||
url = self._GenerateCellsBatchUrl(spreadsheet_key, worksheet_id)
|
||||
return self.Post(batch_feed, url, converter=converter)
|
||||
|
||||
def InsertRow(self, row_data, key, wksht_id='default'):
|
||||
"""Inserts a new row with the provided data
|
||||
|
||||
Args:
|
||||
uri: string The post uri of the list feed
|
||||
row_data: dict A dictionary of column header to row data
|
||||
|
||||
Returns:
|
||||
The inserted row
|
||||
"""
|
||||
new_entry = gdata.spreadsheet.SpreadsheetsList()
|
||||
for k, v in row_data.iteritems():
|
||||
new_custom = gdata.spreadsheet.Custom()
|
||||
new_custom.column = k
|
||||
new_custom.text = v
|
||||
new_entry.custom[new_custom.column] = new_custom
|
||||
# Generate the post URL for the worksheet which will receive the new entry.
|
||||
post_url = 'https://spreadsheets.google.com/feeds/list/%s/%s/private/full'%(
|
||||
key, wksht_id)
|
||||
return self.Post(new_entry, post_url,
|
||||
converter=gdata.spreadsheet.SpreadsheetsListFromString)
|
||||
|
||||
def UpdateRow(self, entry, new_row_data):
|
||||
"""Updates a row with the provided data
|
||||
|
||||
If you want to add additional information to a row, it is often
|
||||
easier to change the values in entry.custom, then use the Put
|
||||
method instead of UpdateRow. This UpdateRow method will replace
|
||||
the contents of the row with new_row_data - it will change all columns
|
||||
not just the columns specified in the new_row_data dict.
|
||||
|
||||
Args:
|
||||
entry: gdata.spreadsheet.SpreadsheetsList The entry to be updated
|
||||
new_row_data: dict A dictionary of column header to row data
|
||||
|
||||
Returns:
|
||||
The updated row
|
||||
"""
|
||||
entry.custom = {}
|
||||
for k, v in new_row_data.iteritems():
|
||||
new_custom = gdata.spreadsheet.Custom()
|
||||
new_custom.column = k
|
||||
new_custom.text = v
|
||||
entry.custom[k] = new_custom
|
||||
for a_link in entry.link:
|
||||
if a_link.rel == 'edit':
|
||||
return self.Put(entry, a_link.href,
|
||||
converter=gdata.spreadsheet.SpreadsheetsListFromString)
|
||||
|
||||
def DeleteRow(self, entry):
|
||||
"""Deletes a row, the provided entry
|
||||
|
||||
Args:
|
||||
entry: gdata.spreadsheet.SpreadsheetsList The row to be deleted
|
||||
|
||||
Returns:
|
||||
The delete response
|
||||
"""
|
||||
for a_link in entry.link:
|
||||
if a_link.rel == 'edit':
|
||||
return self.Delete(a_link.href)
|
||||
|
||||
|
||||
class DocumentQuery(gdata.service.Query):
|
||||
|
||||
def _GetTitleQuery(self):
|
||||
return self['title']
|
||||
|
||||
def _SetTitleQuery(self, document_query):
|
||||
self['title'] = document_query
|
||||
|
||||
title = property(_GetTitleQuery, _SetTitleQuery,
|
||||
doc="""The title query parameter""")
|
||||
|
||||
def _GetTitleExactQuery(self):
|
||||
return self['title-exact']
|
||||
|
||||
def _SetTitleExactQuery(self, document_query):
|
||||
self['title-exact'] = document_query
|
||||
|
||||
title_exact = property(_GetTitleExactQuery, _SetTitleExactQuery,
|
||||
doc="""The title-exact query parameter""")
|
||||
|
||||
|
||||
class CellQuery(gdata.service.Query):
|
||||
|
||||
def _GetMinRowQuery(self):
|
||||
return self['min-row']
|
||||
|
||||
def _SetMinRowQuery(self, cell_query):
|
||||
self['min-row'] = cell_query
|
||||
|
||||
min_row = property(_GetMinRowQuery, _SetMinRowQuery,
|
||||
doc="""The min-row query parameter""")
|
||||
|
||||
def _GetMaxRowQuery(self):
|
||||
return self['max-row']
|
||||
|
||||
def _SetMaxRowQuery(self, cell_query):
|
||||
self['max-row'] = cell_query
|
||||
|
||||
max_row = property(_GetMaxRowQuery, _SetMaxRowQuery,
|
||||
doc="""The max-row query parameter""")
|
||||
|
||||
def _GetMinColQuery(self):
|
||||
return self['min-col']
|
||||
|
||||
def _SetMinColQuery(self, cell_query):
|
||||
self['min-col'] = cell_query
|
||||
|
||||
min_col = property(_GetMinColQuery, _SetMinColQuery,
|
||||
doc="""The min-col query parameter""")
|
||||
|
||||
def _GetMaxColQuery(self):
|
||||
return self['max-col']
|
||||
|
||||
def _SetMaxColQuery(self, cell_query):
|
||||
self['max-col'] = cell_query
|
||||
|
||||
max_col = property(_GetMaxColQuery, _SetMaxColQuery,
|
||||
doc="""The max-col query parameter""")
|
||||
|
||||
def _GetRangeQuery(self):
|
||||
return self['range']
|
||||
|
||||
def _SetRangeQuery(self, cell_query):
|
||||
self['range'] = cell_query
|
||||
|
||||
range = property(_GetRangeQuery, _SetRangeQuery,
|
||||
doc="""The range query parameter""")
|
||||
|
||||
def _GetReturnEmptyQuery(self):
|
||||
return self['return-empty']
|
||||
|
||||
def _SetReturnEmptyQuery(self, cell_query):
|
||||
self['return-empty'] = cell_query
|
||||
|
||||
return_empty = property(_GetReturnEmptyQuery, _SetReturnEmptyQuery,
|
||||
doc="""The return-empty query parameter""")
|
||||
|
||||
|
||||
class ListQuery(gdata.service.Query):
|
||||
|
||||
def _GetSpreadsheetQuery(self):
|
||||
return self['sq']
|
||||
|
||||
def _SetSpreadsheetQuery(self, list_query):
|
||||
self['sq'] = list_query
|
||||
|
||||
sq = property(_GetSpreadsheetQuery, _SetSpreadsheetQuery,
|
||||
doc="""The sq query parameter""")
|
||||
|
||||
def _GetOrderByQuery(self):
|
||||
return self['orderby']
|
||||
|
||||
def _SetOrderByQuery(self, list_query):
|
||||
self['orderby'] = list_query
|
||||
|
||||
orderby = property(_GetOrderByQuery, _SetOrderByQuery,
|
||||
doc="""The orderby query parameter""")
|
||||
|
||||
def _GetReverseQuery(self):
|
||||
return self['reverse']
|
||||
|
||||
def _SetReverseQuery(self, list_query):
|
||||
self['reverse'] = list_query
|
||||
|
||||
reverse = property(_GetReverseQuery, _SetReverseQuery,
|
||||
doc="""The reverse query parameter""")
|
559
python/gdata/spreadsheet/text_db.py
Normal file
559
python/gdata/spreadsheet/text_db.py
Normal file
@@ -0,0 +1,559 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright Google 2007-2008, all rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import StringIO
|
||||
import gdata
|
||||
import gdata.service
|
||||
import gdata.spreadsheet
|
||||
import gdata.spreadsheet.service
|
||||
import gdata.docs
|
||||
import gdata.docs.service
|
||||
|
||||
|
||||
"""Make the Google Documents API feel more like using a database.
|
||||
|
||||
This module contains a client and other classes which make working with the
|
||||
Google Documents List Data API and the Google Spreadsheets Data API look a
|
||||
bit more like working with a heirarchical database. Using the DatabaseClient,
|
||||
you can create or find spreadsheets and use them like a database, with
|
||||
worksheets representing tables and rows representing records.
|
||||
|
||||
Example Usage:
|
||||
# Create a new database, a new table, and add records.
|
||||
client = gdata.spreadsheet.text_db.DatabaseClient(username='jo@example.com',
|
||||
password='12345')
|
||||
database = client.CreateDatabase('My Text Database')
|
||||
table = database.CreateTable('addresses', ['name','email',
|
||||
'phonenumber', 'mailingaddress'])
|
||||
record = table.AddRecord({'name':'Bob', 'email':'bob@example.com',
|
||||
'phonenumber':'555-555-1234', 'mailingaddress':'900 Imaginary St.'})
|
||||
|
||||
# Edit a record
|
||||
record.content['email'] = 'bob2@example.com'
|
||||
record.Push()
|
||||
|
||||
# Delete a table
|
||||
table.Delete
|
||||
|
||||
Warnings:
|
||||
Care should be exercised when using this module on spreadsheets
|
||||
which contain formulas. This module treats all rows as containing text and
|
||||
updating a row will overwrite any formula with the output of the formula.
|
||||
The intended use case is to allow easy storage of text data in a spreadsheet.
|
||||
|
||||
Error: Domain specific extension of Exception.
|
||||
BadCredentials: Error raised is username or password was incorrect.
|
||||
CaptchaRequired: Raised if a login attempt failed and a CAPTCHA challenge
|
||||
was issued.
|
||||
DatabaseClient: Communicates with Google Docs APIs servers.
|
||||
Database: Represents a spreadsheet and interacts with tables.
|
||||
Table: Represents a worksheet and interacts with records.
|
||||
RecordResultSet: A list of records in a table.
|
||||
Record: Represents a row in a worksheet allows manipulation of text data.
|
||||
"""
|
||||
|
||||
|
||||
__author__ = 'api.jscudder (Jeffrey Scudder)'
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BadCredentials(Error):
|
||||
pass
|
||||
|
||||
|
||||
class CaptchaRequired(Error):
|
||||
pass
|
||||
|
||||
|
||||
class DatabaseClient(object):
|
||||
"""Allows creation and finding of Google Spreadsheets databases.
|
||||
|
||||
The DatabaseClient simplifies the process of creating and finding Google
|
||||
Spreadsheets and will talk to both the Google Spreadsheets API and the
|
||||
Google Documents List API.
|
||||
"""
|
||||
|
||||
def __init__(self, username=None, password=None):
|
||||
"""Constructor for a Database Client.
|
||||
|
||||
If the username and password are present, the constructor will contact
|
||||
the Google servers to authenticate.
|
||||
|
||||
Args:
|
||||
username: str (optional) Example: jo@example.com
|
||||
password: str (optional)
|
||||
"""
|
||||
self.__docs_client = gdata.docs.service.DocsService()
|
||||
self.__spreadsheets_client = (
|
||||
gdata.spreadsheet.service.SpreadsheetsService())
|
||||
self.SetCredentials(username, password)
|
||||
|
||||
def SetCredentials(self, username, password):
|
||||
"""Attempts to log in to Google APIs using the provided credentials.
|
||||
|
||||
If the username or password are None, the client will not request auth
|
||||
tokens.
|
||||
|
||||
Args:
|
||||
username: str (optional) Example: jo@example.com
|
||||
password: str (optional)
|
||||
"""
|
||||
self.__docs_client.email = username
|
||||
self.__docs_client.password = password
|
||||
self.__spreadsheets_client.email = username
|
||||
self.__spreadsheets_client.password = password
|
||||
if username and password:
|
||||
try:
|
||||
self.__docs_client.ProgrammaticLogin()
|
||||
self.__spreadsheets_client.ProgrammaticLogin()
|
||||
except gdata.service.CaptchaRequired:
|
||||
raise CaptchaRequired('Please visit https://www.google.com/accounts/'
|
||||
'DisplayUnlockCaptcha to unlock your account.')
|
||||
except gdata.service.BadAuthentication:
|
||||
raise BadCredentials('Username or password incorrect.')
|
||||
|
||||
def CreateDatabase(self, name):
|
||||
"""Creates a new Google Spreadsheet with the desired name.
|
||||
|
||||
Args:
|
||||
name: str The title for the spreadsheet.
|
||||
|
||||
Returns:
|
||||
A Database instance representing the new spreadsheet.
|
||||
"""
|
||||
# Create a Google Spreadsheet to form the foundation of this database.
|
||||
# Spreadsheet is created by uploading a file to the Google Documents
|
||||
# List API.
|
||||
virtual_csv_file = StringIO.StringIO(',,,')
|
||||
virtual_media_source = gdata.MediaSource(file_handle=virtual_csv_file, content_type='text/csv', content_length=3)
|
||||
db_entry = self.__docs_client.UploadSpreadsheet(virtual_media_source, name)
|
||||
return Database(spreadsheet_entry=db_entry, database_client=self)
|
||||
|
||||
def GetDatabases(self, spreadsheet_key=None, name=None):
|
||||
"""Finds spreadsheets which have the unique key or title.
|
||||
|
||||
If querying on the spreadsheet_key there will be at most one result, but
|
||||
searching by name could yield multiple results.
|
||||
|
||||
Args:
|
||||
spreadsheet_key: str The unique key for the spreadsheet, this
|
||||
usually in the the form 'pk23...We' or 'o23...423.12,,,3'.
|
||||
name: str The title of the spreadsheets.
|
||||
|
||||
Returns:
|
||||
A list of Database objects representing the desired spreadsheets.
|
||||
"""
|
||||
if spreadsheet_key:
|
||||
db_entry = self.__docs_client.GetDocumentListEntry(
|
||||
r'/feeds/documents/private/full/spreadsheet%3A' + spreadsheet_key)
|
||||
return [Database(spreadsheet_entry=db_entry, database_client=self)]
|
||||
else:
|
||||
title_query = gdata.docs.service.DocumentQuery()
|
||||
title_query['title'] = name
|
||||
db_feed = self.__docs_client.QueryDocumentListFeed(title_query.ToUri())
|
||||
matching_databases = []
|
||||
for entry in db_feed.entry:
|
||||
matching_databases.append(Database(spreadsheet_entry=entry,
|
||||
database_client=self))
|
||||
return matching_databases
|
||||
|
||||
def _GetDocsClient(self):
|
||||
return self.__docs_client
|
||||
|
||||
def _GetSpreadsheetsClient(self):
|
||||
return self.__spreadsheets_client
|
||||
|
||||
|
||||
class Database(object):
|
||||
"""Provides interface to find and create tables.
|
||||
|
||||
The database represents a Google Spreadsheet.
|
||||
"""
|
||||
|
||||
def __init__(self, spreadsheet_entry=None, database_client=None):
|
||||
"""Constructor for a database object.
|
||||
|
||||
Args:
|
||||
spreadsheet_entry: gdata.docs.DocumentListEntry The
|
||||
Atom entry which represents the Google Spreadsheet. The
|
||||
spreadsheet's key is extracted from the entry and stored as a
|
||||
member.
|
||||
database_client: DatabaseClient A client which can talk to the
|
||||
Google Spreadsheets servers to perform operations on worksheets
|
||||
within this spreadsheet.
|
||||
"""
|
||||
self.entry = spreadsheet_entry
|
||||
if self.entry:
|
||||
id_parts = spreadsheet_entry.id.text.split('/')
|
||||
self.spreadsheet_key = id_parts[-1].replace('spreadsheet%3A', '')
|
||||
self.client = database_client
|
||||
|
||||
def CreateTable(self, name, fields=None):
|
||||
"""Add a new worksheet to this spreadsheet and fill in column names.
|
||||
|
||||
Args:
|
||||
name: str The title of the new worksheet.
|
||||
fields: list of strings The column names which are placed in the
|
||||
first row of this worksheet. These names are converted into XML
|
||||
tags by the server. To avoid changes during the translation
|
||||
process I recommend using all lowercase alphabetic names. For
|
||||
example ['somelongname', 'theothername']
|
||||
|
||||
Returns:
|
||||
Table representing the newly created worksheet.
|
||||
"""
|
||||
worksheet = self.client._GetSpreadsheetsClient().AddWorksheet(title=name,
|
||||
row_count=1, col_count=len(fields), key=self.spreadsheet_key)
|
||||
return Table(name=name, worksheet_entry=worksheet,
|
||||
database_client=self.client,
|
||||
spreadsheet_key=self.spreadsheet_key, fields=fields)
|
||||
|
||||
def GetTables(self, worksheet_id=None, name=None):
|
||||
"""Searches for a worksheet with the specified ID or name.
|
||||
|
||||
The list of results should have one table at most, or no results
|
||||
if the id or name were not found.
|
||||
|
||||
Args:
|
||||
worksheet_id: str The ID of the worksheet, example: 'od6'
|
||||
name: str The title of the worksheet.
|
||||
|
||||
Returns:
|
||||
A list of length 0 or 1 containing the desired Table. A list is returned
|
||||
to make this method feel like GetDatabases and GetRecords.
|
||||
"""
|
||||
if worksheet_id:
|
||||
worksheet_entry = self.client._GetSpreadsheetsClient().GetWorksheetsFeed(
|
||||
self.spreadsheet_key, wksht_id=worksheet_id)
|
||||
return [Table(name=worksheet_entry.title.text,
|
||||
worksheet_entry=worksheet_entry, database_client=self.client,
|
||||
spreadsheet_key=self.spreadsheet_key)]
|
||||
else:
|
||||
matching_tables = []
|
||||
query = None
|
||||
if name:
|
||||
query = gdata.spreadsheet.service.DocumentQuery()
|
||||
query.title = name
|
||||
|
||||
worksheet_feed = self.client._GetSpreadsheetsClient().GetWorksheetsFeed(
|
||||
self.spreadsheet_key, query=query)
|
||||
for entry in worksheet_feed.entry:
|
||||
matching_tables.append(Table(name=entry.title.text,
|
||||
worksheet_entry=entry, database_client=self.client,
|
||||
spreadsheet_key=self.spreadsheet_key))
|
||||
return matching_tables
|
||||
|
||||
def Delete(self):
|
||||
"""Deletes the entire database spreadsheet from Google Spreadsheets."""
|
||||
entry = self.client._GetDocsClient().Get(
|
||||
r'http://docs.google.com/feeds/documents/private/full/spreadsheet%3A' +
|
||||
self.spreadsheet_key)
|
||||
self.client._GetDocsClient().Delete(entry.GetEditLink().href)
|
||||
|
||||
|
||||
class Table(object):
|
||||
|
||||
def __init__(self, name=None, worksheet_entry=None, database_client=None,
|
||||
spreadsheet_key=None, fields=None):
|
||||
self.name = name
|
||||
self.entry = worksheet_entry
|
||||
id_parts = worksheet_entry.id.text.split('/')
|
||||
self.worksheet_id = id_parts[-1]
|
||||
self.spreadsheet_key = spreadsheet_key
|
||||
self.client = database_client
|
||||
self.fields = fields or []
|
||||
if fields:
|
||||
self.SetFields(fields)
|
||||
|
||||
def LookupFields(self):
|
||||
"""Queries to find the column names in the first row of the worksheet.
|
||||
|
||||
Useful when you have retrieved the table from the server and you don't
|
||||
know the column names.
|
||||
"""
|
||||
if self.entry:
|
||||
first_row_contents = []
|
||||
query = gdata.spreadsheet.service.CellQuery()
|
||||
query.max_row = '1'
|
||||
query.min_row = '1'
|
||||
feed = self.client._GetSpreadsheetsClient().GetCellsFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, query=query)
|
||||
for entry in feed.entry:
|
||||
first_row_contents.append(entry.content.text)
|
||||
# Get the next set of cells if needed.
|
||||
next_link = feed.GetNextLink()
|
||||
while next_link:
|
||||
feed = self.client._GetSpreadsheetsClient().Get(next_link.href,
|
||||
converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString)
|
||||
for entry in feed.entry:
|
||||
first_row_contents.append(entry.content.text)
|
||||
next_link = feed.GetNextLink()
|
||||
# Convert the contents of the cells to valid headers.
|
||||
self.fields = ConvertStringsToColumnHeaders(first_row_contents)
|
||||
|
||||
def SetFields(self, fields):
|
||||
"""Changes the contents of the cells in the first row of this worksheet.
|
||||
|
||||
Args:
|
||||
fields: list of strings The names in the list comprise the
|
||||
first row of the worksheet. These names are converted into XML
|
||||
tags by the server. To avoid changes during the translation
|
||||
process I recommend using all lowercase alphabetic names. For
|
||||
example ['somelongname', 'theothername']
|
||||
"""
|
||||
# TODO: If the table already had fields, we might want to clear out the,
|
||||
# current column headers.
|
||||
self.fields = fields
|
||||
i = 0
|
||||
for column_name in fields:
|
||||
i = i + 1
|
||||
# TODO: speed this up by using a batch request to update cells.
|
||||
self.client._GetSpreadsheetsClient().UpdateCell(1, i, column_name,
|
||||
self.spreadsheet_key, self.worksheet_id)
|
||||
|
||||
def Delete(self):
|
||||
"""Deletes this worksheet from the spreadsheet."""
|
||||
worksheet = self.client._GetSpreadsheetsClient().GetWorksheetsFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id)
|
||||
self.client._GetSpreadsheetsClient().DeleteWorksheet(
|
||||
worksheet_entry=worksheet)
|
||||
|
||||
def AddRecord(self, data):
|
||||
"""Adds a new row to this worksheet.
|
||||
|
||||
Args:
|
||||
data: dict of strings Mapping of string values to column names.
|
||||
|
||||
Returns:
|
||||
Record which represents this row of the spreadsheet.
|
||||
"""
|
||||
new_row = self.client._GetSpreadsheetsClient().InsertRow(data,
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id)
|
||||
return Record(content=data, row_entry=new_row,
|
||||
spreadsheet_key=self.spreadsheet_key, worksheet_id=self.worksheet_id,
|
||||
database_client=self.client)
|
||||
|
||||
def GetRecord(self, row_id=None, row_number=None):
|
||||
"""Gets a single record from the worksheet based on row ID or number.
|
||||
|
||||
Args:
|
||||
row_id: The ID for the individual row.
|
||||
row_number: str or int The position of the desired row. Numbering
|
||||
begins at 1, which refers to the second row in the worksheet since
|
||||
the first row is used for column names.
|
||||
|
||||
Returns:
|
||||
Record for the desired row.
|
||||
"""
|
||||
if row_id:
|
||||
row_entry = self.client._GetSpreadsheetsClient().GetListFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=row_id)
|
||||
return Record(content=None, row_entry=row_entry,
|
||||
spreadsheet_key=self.spreadsheet_key,
|
||||
worksheet_id=self.worksheet_id, database_client=self.client)
|
||||
else:
|
||||
row_query = gdata.spreadsheet.service.ListQuery()
|
||||
row_query.start_index = str(row_number)
|
||||
row_query.max_results = '1'
|
||||
row_feed = self.client._GetSpreadsheetsClient().GetListFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query)
|
||||
if len(row_feed.entry) >= 1:
|
||||
return Record(content=None, row_entry=row_feed.entry[0],
|
||||
spreadsheet_key=self.spreadsheet_key,
|
||||
worksheet_id=self.worksheet_id, database_client=self.client)
|
||||
else:
|
||||
return None
|
||||
|
||||
def GetRecords(self, start_row, end_row):
|
||||
"""Gets all rows between the start and end row numbers inclusive.
|
||||
|
||||
Args:
|
||||
start_row: str or int
|
||||
end_row: str or int
|
||||
|
||||
Returns:
|
||||
RecordResultSet for the desired rows.
|
||||
"""
|
||||
start_row = int(start_row)
|
||||
end_row = int(end_row)
|
||||
max_rows = end_row - start_row + 1
|
||||
row_query = gdata.spreadsheet.service.ListQuery()
|
||||
row_query.start_index = str(start_row)
|
||||
row_query.max_results = str(max_rows)
|
||||
rows_feed = self.client._GetSpreadsheetsClient().GetListFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query)
|
||||
return RecordResultSet(rows_feed, self.client, self.spreadsheet_key,
|
||||
self.worksheet_id)
|
||||
|
||||
def FindRecords(self, query_string):
|
||||
"""Performs a query against the worksheet to find rows which match.
|
||||
|
||||
For details on query string syntax see the section on sq under
|
||||
http://code.google.com/apis/spreadsheets/reference.html#list_Parameters
|
||||
|
||||
Args:
|
||||
query_string: str Examples: 'name == john' to find all rows with john
|
||||
in the name column, '(cost < 19.50 and name != toy) or cost > 500'
|
||||
|
||||
Returns:
|
||||
RecordResultSet with the first group of matches.
|
||||
"""
|
||||
row_query = gdata.spreadsheet.service.ListQuery()
|
||||
row_query.sq = query_string
|
||||
matching_feed = self.client._GetSpreadsheetsClient().GetListFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query)
|
||||
return RecordResultSet(matching_feed, self.client,
|
||||
self.spreadsheet_key, self.worksheet_id)
|
||||
|
||||
|
||||
class RecordResultSet(list):
|
||||
"""A collection of rows which allows fetching of the next set of results.
|
||||
|
||||
The server may not send all rows in the requested range because there are
|
||||
too many. Using this result set you can access the first set of results
|
||||
as if it is a list, then get the next batch (if there are more results) by
|
||||
calling GetNext().
|
||||
"""
|
||||
|
||||
def __init__(self, feed, client, spreadsheet_key, worksheet_id):
|
||||
self.client = client
|
||||
self.spreadsheet_key = spreadsheet_key
|
||||
self.worksheet_id = worksheet_id
|
||||
self.feed = feed
|
||||
list(self)
|
||||
for entry in self.feed.entry:
|
||||
self.append(Record(content=None, row_entry=entry,
|
||||
spreadsheet_key=spreadsheet_key, worksheet_id=worksheet_id,
|
||||
database_client=client))
|
||||
|
||||
def GetNext(self):
|
||||
"""Fetches the next batch of rows in the result set.
|
||||
|
||||
Returns:
|
||||
A new RecordResultSet.
|
||||
"""
|
||||
next_link = self.feed.GetNextLink()
|
||||
if next_link and next_link.href:
|
||||
new_feed = self.client._GetSpreadsheetsClient().Get(next_link.href,
|
||||
converter=gdata.spreadsheet.SpreadsheetsListFeedFromString)
|
||||
return RecordResultSet(new_feed, self.client, self.spreadsheet_key,
|
||||
self.worksheet_id)
|
||||
|
||||
|
||||
class Record(object):
|
||||
"""Represents one row in a worksheet and provides a dictionary of values.
|
||||
|
||||
Attributes:
|
||||
custom: dict Represents the contents of the row with cell values mapped
|
||||
to column headers.
|
||||
"""
|
||||
|
||||
def __init__(self, content=None, row_entry=None, spreadsheet_key=None,
|
||||
worksheet_id=None, database_client=None):
|
||||
"""Constructor for a record.
|
||||
|
||||
Args:
|
||||
content: dict of strings Mapping of string values to column names.
|
||||
row_entry: gdata.spreadsheet.SpreadsheetsList The Atom entry
|
||||
representing this row in the worksheet.
|
||||
spreadsheet_key: str The ID of the spreadsheet in which this row
|
||||
belongs.
|
||||
worksheet_id: str The ID of the worksheet in which this row belongs.
|
||||
database_client: DatabaseClient The client which can be used to talk
|
||||
the Google Spreadsheets server to edit this row.
|
||||
"""
|
||||
self.entry = row_entry
|
||||
self.spreadsheet_key = spreadsheet_key
|
||||
self.worksheet_id = worksheet_id
|
||||
if row_entry:
|
||||
self.row_id = row_entry.id.text.split('/')[-1]
|
||||
else:
|
||||
self.row_id = None
|
||||
self.client = database_client
|
||||
self.content = content or {}
|
||||
if not content:
|
||||
self.ExtractContentFromEntry(row_entry)
|
||||
|
||||
def ExtractContentFromEntry(self, entry):
|
||||
"""Populates the content and row_id based on content of the entry.
|
||||
|
||||
This method is used in the Record's contructor.
|
||||
|
||||
Args:
|
||||
entry: gdata.spreadsheet.SpreadsheetsList The Atom entry
|
||||
representing this row in the worksheet.
|
||||
"""
|
||||
self.content = {}
|
||||
if entry:
|
||||
self.row_id = entry.id.text.split('/')[-1]
|
||||
for label, custom in entry.custom.iteritems():
|
||||
self.content[label] = custom.text
|
||||
|
||||
def Push(self):
|
||||
"""Send the content of the record to spreadsheets to edit the row.
|
||||
|
||||
All items in the content dictionary will be sent. Items which have been
|
||||
removed from the content may remain in the row. The content member
|
||||
of the record will not be modified so additional fields in the row
|
||||
might be absent from this local copy.
|
||||
"""
|
||||
self.entry = self.client._GetSpreadsheetsClient().UpdateRow(self.entry, self.content)
|
||||
|
||||
def Pull(self):
|
||||
"""Query Google Spreadsheets to get the latest data from the server.
|
||||
|
||||
Fetches the entry for this row and repopulates the content dictionary
|
||||
with the data found in the row.
|
||||
"""
|
||||
if self.row_id:
|
||||
self.entry = self.client._GetSpreadsheetsClient().GetListFeed(
|
||||
self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=self.row_id)
|
||||
self.ExtractContentFromEntry(self.entry)
|
||||
|
||||
def Delete(self):
|
||||
self.client._GetSpreadsheetsClient().DeleteRow(self.entry)
|
||||
|
||||
|
||||
def ConvertStringsToColumnHeaders(proposed_headers):
|
||||
"""Converts a list of strings to column names which spreadsheets accepts.
|
||||
|
||||
When setting values in a record, the keys which represent column names must
|
||||
fit certain rules. They are all lower case, contain no spaces or special
|
||||
characters. If two columns have the same name after being sanitized, the
|
||||
columns further to the right have _2, _3 _4, etc. appended to them.
|
||||
|
||||
If there are column names which consist of all special characters, or if
|
||||
the column header is blank, an obfuscated value will be used for a column
|
||||
name. This method does not handle blank column names or column names with
|
||||
only special characters.
|
||||
"""
|
||||
headers = []
|
||||
for input_string in proposed_headers:
|
||||
# TODO: probably a more efficient way to do this. Perhaps regex.
|
||||
sanitized = input_string.lower().replace('_', '').replace(
|
||||
':', '').replace(' ', '')
|
||||
# When the same sanitized header appears multiple times in the first row
|
||||
# of a spreadsheet, _n is appended to the name to make it unique.
|
||||
header_count = headers.count(sanitized)
|
||||
if header_count > 0:
|
||||
headers.append('%s_%i' % (sanitized, header_count+1))
|
||||
else:
|
||||
headers.append(sanitized)
|
||||
return headers
|
Reference in New Issue
Block a user