Skip to content

Commit 496a2bd

Browse files
author
j.s@google.com
committed
Adding data model classes for v2 Spreadsheets API support.
1 parent 6d6fe22 commit 496a2bd

8 files changed

Lines changed: 804 additions & 12 deletions

File tree

‎setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
'gdata.Crypto', 'gdata.Crypto.Cipher', 'gdata.Crypto.Hash',
6969
'gdata.Crypto.Protocol', 'gdata.Crypto.PublicKey', 'gdata.Crypto.Util',
7070
'gdata.tlslite.utils', 'gdata.tlslite.integration', 'gdata.health',
71-
'gdata.analytics', 'gdata.finance'],
71+
'gdata.analytics', 'gdata.finance', 'gdata.spreadsheets', 'gdata.maps'],
7272
package_dir = {'gdata':'src/gdata', 'atom':'src/atom'},
7373
install_requires=required
7474
)

‎src/gdata/data.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333

3434

3535
GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s'
36-
OPENSEARCH_TEMPLATE = '{http://a9.com/-/spec/opensearchrss/1.0/}%s'
36+
OPENSEARCH_TEMPLATE_V1 = '{http://a9.com/-/spec/opensearchrss/1.0/}%s'
37+
OPENSEARCH_TEMPLATE_V2 = '{http://a9.com/-/spec/opensearch/1.1/}%s'
3738
BATCH_TEMPLATE = '{http://schemas.google.com/gdata/batch}%s'
3839

3940

@@ -128,6 +129,13 @@
128129
ATTENDEE_INVITED = 'http://schemas.google.com/g/2005#event.invited'
129130
ATTENDEE_TENTATIVE = 'http://schemas.google.com/g/2005#event.tentative'
130131

132+
FULL_PROJECTION = 'full'
133+
VALUES_PROJECTION = 'values'
134+
BASIC_PROJECTION = 'basic'
135+
136+
PRIVATE_VISIBILITY = 'private'
137+
PUBLIC_VISIBILITY = 'public'
138+
131139

132140
class Error(Exception):
133141
pass
@@ -213,17 +221,20 @@ def get_previous_link(self):
213221

214222
class TotalResults(atom.core.XmlElement):
215223
"""opensearch:TotalResults for a GData feed."""
216-
_qname = OPENSEARCH_TEMPLATE % 'totalResults'
224+
_qname = (OPENSEARCH_TEMPLATE_V1 % 'totalResults',
225+
OPENSEARCH_TEMPLATE_V2 % 'totalResults')
217226

218227

219228
class StartIndex(atom.core.XmlElement):
220229
"""The opensearch:startIndex element in GData feed."""
221-
_qname = OPENSEARCH_TEMPLATE % 'startIndex'
230+
_qname = (OPENSEARCH_TEMPLATE_V1 % 'startIndex',
231+
OPENSEARCH_TEMPLATE_V2 % 'startIndex')
222232

223233

224234
class ItemsPerPage(atom.core.XmlElement):
225235
"""The opensearch:itemsPerPage element in GData feed."""
226-
_qname = OPENSEARCH_TEMPLATE % 'itemsPerPage'
236+
_qname = (OPENSEARCH_TEMPLATE_V1 % 'itemsPerPage',
237+
OPENSEARCH_TEMPLATE_V2 % 'itemsPerPage')
227238

228239

229240
class ExtendedProperty(atom.core.XmlElement):

‎src/gdata/spreadsheets/__init__.py‎

Whitespace-only changes.

‎src/gdata/spreadsheets/data.py‎

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright (C) 2009 Google Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
18+
# This module is used for version 2 of the Google Data APIs.
19+
20+
21+
"""Provides classes and constants for the XML in the Google Spreadsheets API.
22+
23+
Documentation for the raw XML which these classes represent can be found here:
24+
http://code.google.com/apis/spreadsheets/docs/3.0/reference.html#Elements
25+
"""
26+
27+
28+
__author__ = 'j.s@google.com (Jeff Scudder)'
29+
30+
31+
import atom.core
32+
import gdata.data
33+
34+
35+
GS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s'
36+
GSX_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006/extended'
37+
38+
39+
INSERT_MODE = 'insert'
40+
OVERWRITE_MODE = 'overwrite'
41+
42+
43+
WORKSHEETS_REL = 'http://schemas.google.com/spreadsheets/2006#worksheetsfeed'
44+
45+
46+
class Error(object):
47+
pass
48+
49+
50+
class HeaderNotSet(Error):
51+
"""The desired column header had no value for the row in the list feed."""
52+
53+
54+
class Cell(atom.core.XmlElement):
55+
"""The gs:cell element.
56+
57+
A cell in the worksheet. The <gs:cell> element can appear only as a child
58+
of <atom:entry>.
59+
"""
60+
_qname = GS_TEMPLATE % 'cell'
61+
col = 'col'
62+
input_value = 'inputValue'
63+
numeric_value = 'numericValue'
64+
row = 'row'
65+
66+
67+
class ColCount(atom.core.XmlElement):
68+
"""The gs:colCount element.
69+
70+
Indicates the number of columns in the worksheet, including columns that
71+
contain only empty cells. The <gs:colCount> element can appear as a child
72+
of <atom:entry> or <atom:feed>
73+
"""
74+
_qname = GS_TEMPLATE % 'colCount'
75+
76+
77+
class Field(atom.core.XmlElement):
78+
"""The gs:field element.
79+
80+
A field—a single cell within a record. Contained in an <atom:entry>.
81+
"""
82+
_qname = GS_TEMPLATE % 'field'
83+
index = 'index'
84+
name = 'name'
85+
86+
87+
class Column(Field):
88+
"""The gs:column element."""
89+
_qname = GS_TEMPLATE % 'column'
90+
91+
92+
class Data(atom.core.XmlElement):
93+
"""The gs:data element.
94+
95+
A data region of a table. Contained in an <atom:entry> element.
96+
"""
97+
_qname = GS_TEMPLATE % 'data'
98+
column = [Column]
99+
insertion_mode = 'insertionMode'
100+
num_rows = 'numRows'
101+
start_row = 'startRow'
102+
103+
104+
class Header(atom.core.XmlElement):
105+
"""The gs:header element.
106+
107+
Indicates which row is the header row. Contained in an <atom:entry>.
108+
"""
109+
_qname = GS_TEMPLATE % 'header'
110+
row = 'row'
111+
112+
113+
class RowCount(atom.core.XmlElement):
114+
"""The gs:rowCount element.
115+
116+
Indicates the number of total rows in the worksheet, including rows that
117+
contain only empty cells. The <gs:rowCount> element can appear as a
118+
child of <atom:entry> or <atom:feed>.
119+
"""
120+
_qname = GS_TEMPLATE % 'rowCount'
121+
122+
123+
class Worksheet(atom.core.XmlElement):
124+
"""The gs:worksheet element.
125+
126+
The worksheet where the table lives.Contained in an <atom:entry>.
127+
"""
128+
_qname = GS_TEMPLATE % 'worksheet'
129+
name = 'name'
130+
131+
132+
class Spreadsheet(gdata.data.GDEntry):
133+
"""An Atom entry which represents a Google Spreadsheet."""
134+
135+
def find_worksheets_feed(self):
136+
return self.find_url(WORKSHEETS_REL)
137+
138+
FindWorksheetsFeed = find_worksheets_feed
139+
140+
141+
class SpreadsheetsFeed(gdata.data.GDFeed):
142+
"""An Atom feed listing a user's Google Spreadsheets."""
143+
entry = [Spreadsheet]
144+
145+
146+
class WorksheetEntry(gdata.data.GDEntry):
147+
"""An Atom entry representing a single worksheet in a spreadsheet."""
148+
row_count = RowCount
149+
col_count = ColCount
150+
151+
152+
class WorksheetsFeed(gdata.data.GDFeed):
153+
"""A feed containing the worksheets in a single spreadsheet."""
154+
entry = [Worksheet]
155+
156+
157+
class Table(gdata.data.GDEntry):
158+
"""An Atom entry that represents a subsection of a worksheet.
159+
160+
A table allows you to treat part or all of a worksheet somewhat like a
161+
table in a database—that is, as a set of structured data items. Tables
162+
don't exist until you explicitly create them—before you can use a table
163+
feed, you have to explicitly define where the table data comes from.
164+
"""
165+
data = Data
166+
header = Header
167+
168+
169+
class TablesFeed(gdata.data.GDFeed):
170+
"""An Atom feed containing the tables defined within a worksheet."""
171+
entry = [Table]
172+
173+
174+
class Record(gdata.data.GDEntry):
175+
"""An Atom entry representing a single record in a table.
176+
177+
Note that the order of items in each record is the same as the order of
178+
columns in the table definition, which may not match the order of
179+
columns in the GUI.
180+
"""
181+
field = [Field]
182+
183+
184+
class RecordsFeed(gdata.data.GDFeed):
185+
"""An Atom feed containing the individuals records in a table."""
186+
entry = [Record]
187+
188+
189+
class ListEntry(gdata.data.GDEntry):
190+
"""An Atom entry representing a worksheet row in the list feed.
191+
192+
The values for a particular column can be get and set using
193+
x.get_value('columnheader') and x.set_value('columnheader', 'value').
194+
See also the explanation of column names in the ListFeed class.
195+
"""
196+
197+
def get_value(self, column_name):
198+
#TODO: implement search through row fields for column name
199+
pass
200+
201+
def set_value(self, column_name):
202+
#TODO: implement find and set field in this row.
203+
pass
204+
205+
206+
class ListsFeed(gdata.data.GDFeed):
207+
"""An Atom feed in which each entry represents a row in a worksheet.
208+
209+
The first row in the worksheet is used as the column names for the values
210+
in each row. If a header cell is empty, then a unique column ID is used
211+
for the gsx element name.
212+
213+
Spaces in a column name are removed from the name of the corresponding
214+
gsx element.
215+
216+
Caution: The columnNames are case-insensitive. For example, if you see
217+
a <gsx:e-mail> element in a feed, you can't know whether the column
218+
heading in the original worksheet was "e-mail" or "E-Mail".
219+
220+
Note: If two or more columns have the same name, then subsequent columns
221+
of the same name have _n appended to the columnName. For example, if the
222+
first column name is "e-mail", followed by columns named "E-Mail" and
223+
"E-mail", then the columnNames will be gsx:e-mail, gsx:e-mail_2, and
224+
gsx:e-mail_3 respectively.
225+
"""
226+
entry = [ListEntry]
227+
228+
229+
class CellEntry(gdata.data.BatchEntry):
230+
"""An Atom entry representing a single cell in a worksheet."""
231+
cell = Cell
232+
233+
234+
class CellsFeed(gdata.data.BatchFeed):
235+
"""An Atom feed contains one entry per cell in a worksheet.
236+
237+
The cell feed supports batch operations, you can send multiple cell
238+
operations in one HTTP request.
239+
"""
240+
entry = [CellEntry]

‎src/gdata/test_config.py‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,15 @@ def check_data_classes(test, classes):
177177
test.assertTrue(data_class.__doc__ is not None,
178178
'The class %s should have a docstring' % data_class)
179179
if hasattr(data_class, '_qname'):
180-
test.assertTrue(isinstance(data_class._qname, str),
181-
'The class %s has a non-string _qname' % data_class)
182-
test.assertFalse(data_class._qname.endswith('}'),
183-
'The _qname for class %s is only a namespace' % (
184-
data_class))
180+
qname_versions = (data_class._qname
181+
if isinstance(data_class._qname, tuple)
182+
else (data_class._qname,))
183+
for versioned_qname in data_class._qname:
184+
test.assertTrue(isinstance(versioned_qname, str),
185+
'The class %s has a non-string _qname' % data_class)
186+
test.assertFalse(versioned_qname.endswith('}'),
187+
'The _qname for class %s is only a namespace' % (
188+
data_class))
185189

186190
for attribute_name, value in data_class.__dict__.iteritems():
187191
# Ignore all elements that start with _ (private members)

‎tests/gdata_tests/data_test.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,8 @@ def test_basic_element_structure(self):
481481
def test_member_values(self):
482482
self.assertEqual(
483483
gdata.data.TotalResults._qname,
484-
'{http://a9.com/-/spec/opensearch/1.1/}totalResults')
484+
('{http://a9.com/-/spec/opensearchrss/1.0/}totalResults',
485+
'{http://a9.com/-/spec/opensearch/1.1/}totalResults'))
485486
self.assertEqual(
486487
gdata.data.RecurrenceException._qname,
487488
'{http://schemas.google.com/g/2005}recurrenceException')
@@ -492,7 +493,8 @@ def test_member_values(self):
492493
def suite():
493494
return conf.build_suite([StartIndexTest, StartIndexTest, GDataEntryTest,
494495
LinkFinderTest, GDataFeedTest, BatchEntryTest, BatchFeedTest,
495-
ExtendedPropertyTest, FeedLinkTest, SimpleV2FeedTest])
496+
ExtendedPropertyTest, FeedLinkTest, SimpleV2FeedTest,
497+
DataClassSanityTest])
496498

497499

498500
if __name__ == '__main__':

‎tests/gdata_tests/spreadsheets/__init__.py‎

Whitespace-only changes.

0 commit comments

Comments
 (0)