#!/usr/bin/env python # # Copyright (C) 2009 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 a client to communicate with the Google Spreadsheets servers. For documentation on the Spreadsheets API, see: http://code.google.com/apis/spreadsheets/ """ __author__ = 'j.s@google.com (Jeff Scudder)' import gdata.client import gdata.gauth import gdata.spreadsheets.data import atom.data import atom.http_core SPREADSHEETS_URL = ('https://spreadsheets.google.com/feeds/spreadsheets' '/private/full') WORKSHEETS_URL = ('https://spreadsheets.google.com/feeds/worksheets/' '%s/private/full') WORKSHEET_URL = ('https://spreadsheets.google.com/feeds/worksheets/' '%s/private/full/%s') TABLES_URL = 'https://spreadsheets.google.com/feeds/%s/tables' RECORDS_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s' RECORD_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s/%s' class SpreadsheetsClient(gdata.client.GDClient): api_version = '3' auth_service = 'wise' auth_scopes = gdata.gauth.AUTH_SCOPES['wise'] ssl = True def get_spreadsheets(self, auth_token=None, desired_class=gdata.spreadsheets.data.SpreadsheetsFeed, **kwargs): """Obtains a feed with the spreadsheets belonging to the current user. Args: auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.SpreadsheetsFeed. """ return self.get_feed(SPREADSHEETS_URL, auth_token=auth_token, desired_class=desired_class, **kwargs) GetSpreadsheets = get_spreadsheets def get_worksheets(self, spreadsheet_key, auth_token=None, desired_class=gdata.spreadsheets.data.WorksheetsFeed, **kwargs): """Finds the worksheets within a given spreadsheet. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.WorksheetsFeed. """ return self.get_feed(WORKSHEETS_URL % spreadsheet_key, auth_token=auth_token, desired_class=desired_class, **kwargs) GetWorksheets = get_worksheets def add_worksheet(self, spreadsheet_key, title, rows, cols, auth_token=None, **kwargs): """Creates a new worksheet entry in the spreadsheet. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. title: str, The title to be used in for the worksheet. rows: str or int, The number of rows this worksheet should start with. cols: str or int, The number of columns this worksheet should start with. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. """ new_worksheet = gdata.spreadsheets.data.WorksheetEntry( title=atom.data.Title(text=title), row_count=gdata.spreadsheets.data.RowCount(text=str(rows)), col_count=gdata.spreadsheets.data.ColCount(text=str(cols))) return self.post(new_worksheet, WORKSHEETS_URL % spreadsheet_key, auth_token=auth_token, **kwargs) AddWorksheet = add_worksheet def get_worksheet(self, spreadsheet_key, worksheet_id, desired_class=gdata.spreadsheets.data.WorksheetEntry, auth_token=None, **kwargs): """Retrieves a single worksheet. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. worksheet_id: str, The unique ID for the worksheet withing the desired spreadsheet. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.WorksheetEntry. """ return self.get_entry(WORKSHEET_URL % (spreadsheet_key, worksheet_id,), auth_token=auth_token, desired_class=desired_class, **kwargs) GetWorksheet = get_worksheet def add_table(self, spreadsheet_key, title, summary, worksheet_name, header_row, num_rows, start_row, insertion_mode, column_headers, auth_token=None, **kwargs): """Creates a new table within the worksheet. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. title: str, The title for the new table within a worksheet. summary: str, A description of the table. worksheet_name: str The name of the worksheet in which this table should live. header_row: int or str, The number of the row in the worksheet which will contain the column names for the data in this table. num_rows: int or str, The number of adjacent rows in this table. start_row: int or str, The number of the row at which the data begins. insertion_mode: str column_headers: dict of strings, maps the column letters (A, B, C) to the desired name which will be viewable in the worksheet. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. """ data = gdata.spreadsheets.data.Data( insertion_mode=insertion_mode, num_rows=str(num_rows), start_row=str(start_row)) for index, name in column_headers.iteritems(): data.column.append(gdata.spreadsheets.data.Column( index=index, name=name)) new_table = gdata.spreadsheets.data.Table( title=atom.data.Title(text=title), summary=atom.data.Summary(summary), worksheet=gdata.spreadsheets.data.Worksheet(name=worksheet_name), header=gdata.spreadsheets.data.Header(row=str(header_row)), data=data) return self.post(new_table, TABLES_URL % spreadsheet_key, auth_token=auth_token, **kwargs) AddTable = add_table def get_tables(self, spreadsheet_key, desired_class=gdata.spreadsheets.data.TablesFeed, auth_token=None, **kwargs): """Retrieves a feed listing the tables in this spreadsheet. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.TablesFeed. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. """ return self.get_feed(TABLES_URL % spreadsheet_key, desired_class=desired_class, auth_token=auth_token, **kwargs) GetTables = get_tables def add_record(self, spreadsheet_key, table_id, fields, title=None, auth_token=None, **kwargs): """Adds a new row to the table. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. table_id: str, The ID of the table within the worksheet which should receive this new record. The table ID can be found using the get_table_id method of a gdata.spreadsheets.data.Table. fields: dict of strings mapping column names to values. title: str, optional The title for this row. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. """ new_record = gdata.spreadsheets.data.Record() if title is not None: new_record.title = atom.data.Title(text=title) for name, value in fields.iteritems(): new_record.field.append(gdata.spreadsheets.data.Field( name=name, text=value)) return self.post(new_record, RECORDS_URL % (spreadsheet_key, table_id), auth_token=auth_token, **kwargs) AddRecord = add_record def get_records(self, spreadsheet_key, table_id, desired_class=gdata.spreadsheets.data.RecordsFeed, auth_token=None, **kwargs): """Retrieves the records in a table. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. table_id: str, The ID of the table within the worksheet whose records we would like to fetch. The table ID can be found using the get_table_id method of a gdata.spreadsheets.data.Table. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.RecordsFeed. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient. """ return self.get_feed(RECORDS_URL % (spreadsheet_key, table_id), desired_class=desired_class, auth_token=auth_token, **kwargs) GetRecords = get_records def get_record(self, spreadsheet_key, table_id, record_id, desired_class=gdata.spreadsheets.data.Record, auth_token=None, **kwargs): """Retrieves a single record from the table. Args: spreadsheet_key: str, The unique ID of this containing spreadsheet. This can be the ID from the URL or as provided in a Spreadsheet entry. table_id: str, The ID of the table within the worksheet whose records we would like to fetch. The table ID can be found using the get_table_id method of a gdata.spreadsheets.data.Table. record_id: str, The ID of the record within this table which we want to fetch. You can find the record ID using get_record_id() on an instance of the gdata.spreadsheets.data.Record class. desired_class: class descended from atom.core.XmlElement to which a successful response should be converted. If there is no converter function specified (converter=None) then the desired_class will be used in calling the atom.core.parse function. If neither the desired_class nor the converter is specified, an HTTP reponse object will be returned. Defaults to gdata.spreadsheets.data.RecordsFeed. auth_token: An object which sets the Authorization HTTP header in its modify_request method. Recommended classes include gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken among others. Represents the current user. Defaults to None and if None, this method will look for a value in the auth_token member of SpreadsheetsClient.""" return self.get_entry(RECORD_URL % (spreadsheet_key, table_id, record_id), desired_class=desired_class, auth_token=auth_token, **kwargs) GetRecord = get_record class SpreadsheetQuery(gdata.client.Query): def __init__(self, title=None, title_exact=None, **kwargs): """Adds Spreadsheets feed query parameters to a request. Args: title: str Specifies the search terms for the title of a document. This parameter used without title-exact will only submit partial queries, not exact queries. title_exact: str Specifies whether the title query should be taken as an exact string. Meaningless without title. Possible values are 'true' and 'false'. """ gdata.client.Query.__init__(self, **kwargs) self.title = title self.title_exact = title_exact def modify_request(self, http_request): gdata.client._add_query_param('title', self.title, http_request) gdata.client._add_query_param('title-exact', self.title_exact, http_request) gdata.client.Query.modify_request(self, http_request) ModifyRequest = modify_request class WorksheetQuery(SpreadsheetQuery): pass class ListQuery(gdata.client.Query): def __init__(self, order_by=None, reverse=None, sq=None, **kwargs): """Adds List-feed specific query parameters to a request. Args: order_by: str Specifies what column to use in ordering the entries in the feed. By position (the default): 'position' returns rows in the order in which they appear in the GUI. Row 1, then row 2, then row 3, and so on. By column: 'column:columnName' sorts rows in ascending order based on the values in the column with the given columnName, where columnName is the value in the header row for that column. reverse: str Specifies whether to sort in descending or ascending order. Reverses default sort order: 'true' results in a descending sort; 'false' (the default) results in an ascending sort. sq: str Structured query on the full text in the worksheet. [columnName][binaryOperator][value] Supported binaryOperators are: - (), for overriding order of operations - = or ==, for strict equality - <> or !=, for strict inequality - and or &&, for boolean and - or or ||, for boolean or """ gdata.client.Query.__init__(self, **kwargs) self.order_by = order_by self.reverse = reverse self.sq = sq def modify_request(self, http_request): gdata.client._add_query_param('orderby', self.order_by, http_request) gdata.client._add_query_param('reverse', self.reverse, http_request) gdata.client._add_query_param('sq', self.sq, http_request) gdata.client.Query.modify_request(self, http_request) ModifyRequest = modify_request class TableQuery(ListQuery): pass class CellQuery(gdata.client.Query): def __init__(self, min_row=None, max_row=None, min_col=None, max_col=None, range=None, return_empty=None, **kwargs): """Adds Cells-feed specific query parameters to a request. Args: min_row: str or int Positional number of minimum row returned in query. max_row: str or int Positional number of maximum row returned in query. min_col: str or int Positional number of minimum column returned in query. max_col: str or int Positional number of maximum column returned in query. range: str A single cell or a range of cells. Use standard spreadsheet cell-range notations, using a colon to separate start and end of range. Examples: - 'A1' and 'R1C1' both specify only cell A1. - 'D1:F3' and 'R1C4:R3C6' both specify the rectangle of cells with corners at D1 and F3. return_empty: str If 'true' then empty cells will be returned in the feed. If omitted, the default is 'false'. """ gdata.client.Query.__init__(self, **kwargs) self.min_row = min_row self.max_row = max_row self.min_col = min_col self.max_col = max_col self.range = range self.return_empty = return_empty def modify_request(self, http_request): gdata.client._add_query_param('min-row', self.min_row, http_request) gdata.client._add_query_param('max-row', self.max_row, http_request) gdata.client._add_query_param('min-col', self.min_col, http_request) gdata.client._add_query_param('max-col', self.max_col, http_request) gdata.client._add_query_param('range', self.range, http_request) gdata.client._add_query_param('return-empty', self.return_empty, http_request) gdata.client.Query.modify_request(self, http_request) ModifyRequest = modify_request