Skip to content

Commit 57302a8

Browse files
author
Vic Fryzel
committed
Merging new docslist client to default branch.
2 parents c17260f + 9cc1d93 commit 57302a8

7 files changed

Lines changed: 282 additions & 10 deletions

File tree

samples/sites/sites_example.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
class SitesExample(object):
4949
"""Wrapper around the Sites API functionality."""
5050

51-
def __init__(self, site_name=None, site_domain=None, ssl=False, debug=False):
51+
def __init__(self, site_name=None, site_domain=None, debug=False):
5252
if site_domain is None:
5353
site_domain = self.PromptDomain()
5454

@@ -60,7 +60,6 @@ def __init__(self, site_name=None, site_domain=None, ssl=False, debug=False):
6060
self.client = gdata.sites.client.SitesClient(
6161
source=SOURCE_APP_NAME, site=site_name, domain=site_domain)
6262
self.client.http_client.debug = debug
63-
self.client.ssl = ssl
6463

6564
try:
6665
gdata.sample_util.authorize_client(
@@ -412,30 +411,26 @@ def main():
412411

413412
try:
414413
opts, args = getopt.getopt(sys.argv[1:], '',
415-
['site=', 'domain=', 'ssl', 'debug'])
414+
['site=', 'domain=', 'debug'])
416415
except getopt.error, msg:
417416
print """python sites_sample.py --site [sitename]
418417
--domain [domain or "site"]
419-
--ssl [enables https if set]
420418
--debug [prints debug info if set]"""
421419
sys.exit(2)
422420

423421
site = None
424422
domain = None
425423
debug = False
426-
ssl = False
427424

428425
for option, arg in opts:
429426
if option == '--site':
430427
site = arg
431428
elif option == '--domain':
432429
domain = arg
433-
elif option == '--ssl':
434-
ssl = True
435430
elif option == '--debug':
436431
debug = True
437432

438-
sample = SitesExample(site, domain, ssl=ssl, debug=debug)
433+
sample = SitesExample(site, domain, debug=debug)
439434
sample.Run()
440435

441436

src/atom/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ class LinkFinder(object):
185185
"""
186186

187187
def find_url(self, rel):
188-
"""Returns the URL in a link with the desired rel value."""
188+
"""Returns the URL (as a string) in a link with the desired rel value."""
189189
for link in self.link:
190190
if link.rel == rel and link.href:
191191
return link.href

src/gdata/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ class ResumableUploader(object):
875875
"""Resumable upload helper for the Google Data protocol."""
876876

877877
DEFAULT_CHUNK_SIZE = 5242880 # 5MB
878+
# Initial chunks which are smaller than 256KB might be dropped. The last
879+
# chunk for a file can be smaller tan this.
880+
MIN_CHUNK_SIZE = 262144 # 256KB
878881

879882
def __init__(self, client, file_handle, content_type, total_file_size,
880883
chunk_size=None, desired_class=None):
@@ -895,6 +898,8 @@ def __init__(self, client, file_handle, content_type, total_file_size,
895898
self.content_type = content_type
896899
self.total_file_size = total_file_size
897900
self.chunk_size = chunk_size or self.DEFAULT_CHUNK_SIZE
901+
if self.chunk_size < self.MIN_CHUNK_SIZE:
902+
self.chunk_size = self.MIN_CHUNK_SIZE
898903
self.desired_class = desired_class or gdata.data.GDEntry
899904
self.upload_uri = None
900905

src/gdata/spreadsheets/client.py

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
TABLES_URL = 'https://spreadsheets.google.com/feeds/%s/tables'
4242
RECORDS_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s'
4343
RECORD_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s/%s'
44+
CELLS_URL = 'https://spreadsheets.google.com/feeds/cells/%s/%s/private/full'
45+
CELL_URL = ('https://spreadsheets.google.com/feeds/cells/%s/%s/private/full/'
46+
'R%sC%s')
47+
LISTS_URL = 'https://spreadsheets.google.com/feeds/list/%s/%s/private/full'
4448

4549

4650
class SpreadsheetsClient(gdata.client.GDClient):
@@ -330,13 +334,149 @@ def get_record(self, spreadsheet_key, table_id, record_id,
330334
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
331335
among others. Represents the current user. Defaults to None
332336
and if None, this method will look for a value in the
333-
auth_token member of SpreadsheetsClient."""
337+
auth_token member of SpreadsheetsClient.
338+
"""
334339
return self.get_entry(RECORD_URL % (spreadsheet_key, table_id, record_id),
335340
desired_class=desired_class, auth_token=auth_token,
336341
**kwargs)
337342

338343
GetRecord = get_record
339344

345+
def get_cells(self, spreadsheet_key, worksheet_id,
346+
desired_class=gdata.spreadsheets.data.CellsFeed,
347+
auth_token=None, **kwargs):
348+
"""Retrieves the cells which have values in this spreadsheet.
349+
350+
Blank cells are not included.
351+
352+
Args:
353+
spreadsheet_key: str, The unique ID of this containing spreadsheet. This
354+
can be the ID from the URL or as provided in a
355+
Spreadsheet entry.
356+
worksheet_id: str, The unique ID of the worksheet in this spreadsheet
357+
whose cells we want. This can be obtained using
358+
WorksheetEntry's get_worksheet_id method.
359+
desired_class: class descended from atom.core.XmlElement to which a
360+
successful response should be converted. If there is no
361+
converter function specified (converter=None) then the
362+
desired_class will be used in calling the
363+
atom.core.parse function. If neither
364+
the desired_class nor the converter is specified, an
365+
HTTP reponse object will be returned. Defaults to
366+
gdata.spreadsheets.data.CellsFeed.
367+
auth_token: An object which sets the Authorization HTTP header in its
368+
modify_request method. Recommended classes include
369+
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
370+
among others. Represents the current user. Defaults to None
371+
and if None, this method will look for a value in the
372+
auth_token member of SpreadsheetsClient.
373+
"""
374+
return self.get_feed(CELLS_URL % (spreadsheet_key, worksheet_id),
375+
auth_token=auth_token, desired_class=desired_class,
376+
**kwargs)
377+
378+
GetCells = get_cells
379+
380+
def get_cell(self, spreadsheet_key, worksheet_id, row_num, col_num,
381+
desired_class=gdata.spreadsheets.data.CellEntry,
382+
auth_token=None, **kwargs):
383+
"""Retrieves a single cell from the worksheet.
384+
385+
Indexes are 1 based so the first cell in the worksheet is 1, 1.
386+
387+
Args:
388+
spreadsheet_key: str, The unique ID of this containing spreadsheet. This
389+
can be the ID from the URL or as provided in a
390+
Spreadsheet entry.
391+
worksheet_id: str, The unique ID of the worksheet in this spreadsheet
392+
whose cells we want. This can be obtained using
393+
WorksheetEntry's get_worksheet_id method.
394+
row_num: int, The row of the cell that we want. Numbering starts with 1.
395+
col_num: int, The column of the cell we want. Numbering starts with 1.
396+
desired_class: class descended from atom.core.XmlElement to which a
397+
successful response should be converted. If there is no
398+
converter function specified (converter=None) then the
399+
desired_class will be used in calling the
400+
atom.core.parse function. If neither
401+
the desired_class nor the converter is specified, an
402+
HTTP reponse object will be returned. Defaults to
403+
gdata.spreadsheets.data.CellEntry.
404+
auth_token: An object which sets the Authorization HTTP header in its
405+
modify_request method. Recommended classes include
406+
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
407+
among others. Represents the current user. Defaults to None
408+
and if None, this method will look for a value in the
409+
auth_token member of SpreadsheetsClient.
410+
"""
411+
return self.get_entry(
412+
CELL_URL % (spreadsheet_key, worksheet_id, row_num, col_num),
413+
auth_token=auth_token, desired_class=desired_class, **kwargs)
414+
415+
GetCell = get_cell
416+
417+
def get_list_feed(self, spreadsheet_key, worksheet_id,
418+
desired_class=gdata.spreadsheets.data.ListsFeed,
419+
auth_token=None, **kwargs):
420+
"""Retrieves the value rows from the worksheet's list feed.
421+
422+
The list feed is a view of the spreadsheet in which the first row is used
423+
for column names and subsequent rows up to the first blank line are
424+
records.
425+
426+
Args:
427+
spreadsheet_key: str, The unique ID of this containing spreadsheet. This
428+
can be the ID from the URL or as provided in a
429+
Spreadsheet entry.
430+
worksheet_id: str, The unique ID of the worksheet in this spreadsheet
431+
whose cells we want. This can be obtained using
432+
WorksheetEntry's get_worksheet_id method.
433+
desired_class: class descended from atom.core.XmlElement to which a
434+
successful response should be converted. If there is no
435+
converter function specified (converter=None) then the
436+
desired_class will be used in calling the
437+
atom.core.parse function. If neither
438+
the desired_class nor the converter is specified, an
439+
HTTP reponse object will be returned. Defaults to
440+
gdata.spreadsheets.data.ListsFeed.
441+
auth_token: An object which sets the Authorization HTTP header in its
442+
modify_request method. Recommended classes include
443+
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
444+
among others. Represents the current user. Defaults to None
445+
and if None, this method will look for a value in the
446+
auth_token member of SpreadsheetsClient.
447+
"""
448+
return self.get_feed(LISTS_URL % (spreadsheet_key, worksheet_id),
449+
auth_token=auth_token, desired_class=desired_class,
450+
**kwargs)
451+
452+
GetListFeed = get_list_feed
453+
454+
def add_list_entry(self, list_entry, spreadsheet_key, worksheet_id,
455+
auth_token=None, **kwargs):
456+
"""Adds a new row to the worksheet's list feed.
457+
458+
Args:
459+
list_entry: gdata.spreadsheets.data.ListsEntry An entry which contains
460+
the values which should be set for the columns in this
461+
record.
462+
spreadsheet_key: str, The unique ID of this containing spreadsheet. This
463+
can be the ID from the URL or as provided in a
464+
Spreadsheet entry.
465+
worksheet_id: str, The unique ID of the worksheet in this spreadsheet
466+
whose cells we want. This can be obtained using
467+
WorksheetEntry's get_worksheet_id method.
468+
auth_token: An object which sets the Authorization HTTP header in its
469+
modify_request method. Recommended classes include
470+
gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken
471+
among others. Represents the current user. Defaults to None
472+
and if None, this method will look for a value in the
473+
auth_token member of SpreadsheetsClient.
474+
"""
475+
return self.post(list_entry, LISTS_URL % (spreadsheet_key, worksheet_id),
476+
auth_token=auth_token, **kwargs)
477+
478+
AddListEntry = add_list_entry
479+
340480

341481
class SpreadsheetQuery(gdata.client.Query):
342482

src/gdata/spreadsheets/data.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ def find_worksheets_feed(self):
141141

142142
FindWorksheetsFeed = find_worksheets_feed
143143

144+
def get_spreadsheet_key(self):
145+
"""Extracts the spreadsheet key unique to this spreadsheet."""
146+
return self.get_id().split('/')[-1]
147+
148+
GetSpreadsheetKey = get_spreadsheet_key
149+
144150

145151
class SpreadsheetsFeed(gdata.data.GDFeed):
146152
"""An Atom feed listing a user's Google Spreadsheets."""
@@ -152,6 +158,12 @@ class WorksheetEntry(gdata.data.GDEntry):
152158
row_count = RowCount
153159
col_count = ColCount
154160

161+
def get_worksheet_id(self):
162+
"""The worksheet ID identifies this worksheet in its spreadsheet."""
163+
return self.get_id().split('/')[-1]
164+
165+
GetWorksheetId = get_worksheet_id
166+
155167

156168
class WorksheetsFeed(gdata.data.GDFeed):
157169
"""A feed containing the worksheets in a single spreadsheet."""
@@ -275,6 +287,23 @@ def set_value(self, column_name, value):
275287
new_value._qname = new_value._qname % (column_name,)
276288
self._other_elements.append(new_value)
277289

290+
def to_dict(self):
291+
"""Converts this row to a mapping of column names to their values."""
292+
result = {}
293+
values = self.get_elements(namespace=GSX_NAMESPACE)
294+
for item in values:
295+
result[item._get_tag()] = item.text
296+
return result
297+
298+
def from_dict(self, values):
299+
"""Sets values for this row from the dictionary.
300+
301+
Old values which are already in the entry will not be removed unless
302+
they are overwritten with new values from the dict.
303+
"""
304+
for column, value in values.iteritems():
305+
self.set_value(column, value)
306+
278307

279308
class ListsFeed(gdata.data.GDFeed):
280309
"""An Atom feed in which each entry represents a row in a worksheet.

tests/gdata_tests/spreadsheets/data_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,30 @@ def test_check_parsing(self):
527527
'http://spreadsheets.google.com/feeds/spreadsheets'
528528
'/private/full/key')
529529

530+
def test_get_spreadsheet_key(self):
531+
self.assertEqual(self.spreadsheet.get_spreadsheet_key(), 'key')
532+
# Change the value of the self link.
533+
self.spreadsheet.id.text = '42'
534+
self.assertEqual(self.spreadsheet.GetSpreadsheetKey(), '42')
535+
536+
537+
class WorksheetEntryTest(unittest.TestCase):
538+
539+
def setUp(self):
540+
self.worksheets = atom.core.parse(
541+
WORKSHEETS_FEED, gdata.spreadsheets.data.WorksheetsFeed)
542+
543+
def test_check_parsing(self):
544+
self.assertEqual(len(self.worksheets.entry), 1)
545+
self.assertEqual(self.worksheets.entry[0].get_id(),
546+
'http://spreadsheets.google.com/feeds/worksheets/0/private/full/1')
547+
548+
def test_get_worksheet_id(self):
549+
self.assertEqual(self.worksheets.entry[0].get_worksheet_id(), '1')
550+
self.worksheets.entry[0].id.text = '////spam'
551+
self.assertEqual(self.worksheets.entry[0].GetWorksheetId(), 'spam')
552+
553+
530554

531555
class ListEntryTest(unittest.TestCase):
532556

0 commit comments

Comments
 (0)