From a0cb44cc436249d97ee5bca9623de388ff13f6ff Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Mon, 13 Sep 2010 20:03:46 -0400 Subject: [PATCH 1/5] Made Struct really powerful. --- tablib/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tablib/helpers.py b/tablib/helpers.py index a12c4dd..b64d4b6 100644 --- a/tablib/helpers.py +++ b/tablib/helpers.py @@ -10,7 +10,7 @@ class Struct(object): self.__dict__.update(entries) def __getitem__(self, key): - return getattr(self, key) + return getattr(self, key, None) def piped(): From 68d7204b2dacb157a3c0dc33b40ec3a6957f7471 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Mon, 13 Sep 2010 23:25:49 -0400 Subject: [PATCH 2/5] Added data.append(col=[]) support. --- tablib/core.py | 62 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/tablib/core.py b/tablib/core.py index ac61315..4a0b55f 100644 --- a/tablib/core.py +++ b/tablib/core.py @@ -15,6 +15,7 @@ import simplejson as json import xlwt import yaml +import showme from helpers import * @@ -80,10 +81,15 @@ class Dataset(object): return '' - def _validate(self, row=None, safety=False): + def _validate(self, row=None, col=None, safety=False): """Assures size of every row in dataset is of proper proportions.""" if row: is_valid = (len(row) == self.width) if self.width else True + elif col: + if self.headers: + is_valid = (len(col) - 1) == self.height + else: + is_valid = (len(col) == self.height) if self.height else True else: is_valid = all((len(x)== self.width for x in self._data)) @@ -130,17 +136,20 @@ class Dataset(object): """Headers property.""" return self.__headers + @headers.setter def headers(self, collection): """Validating headers setter.""" self._validate(collection) self.__headers = collection - + + @property def dict(self): """Returns python dict of Dataset.""" return self._package() + @property def json(self): """Returns JSON representation of Dataset.""" @@ -179,17 +188,37 @@ class Dataset(object): wb.save(stream) return stream.getvalue() - - def append(self, row): +# @showme.trace + def append(self, row=None, col=None): """Adds a row to the end of Dataset""" - self._validate(row) - self._data.append(tuple(row)) + if row: + self._validate(row) + self._data.append(tuple(row)) + elif col: + self._validate(col=col) + + if self.headers: + # pop the first item off, add to headers + self.headers.append(col[0]) + col = col[1:] + + if self.height and self.width: + + for i, row in enumerate(self._data): + _row = list(row) + _row.append(col[i]) + self._data[i] = tuple(_row) + else: + self._data = [tuple([row]) for row in col] - def index(self, i, row): + def insert(self, i, row=None, col=None): """Inserts a row at given position in Dataset""" - self._validate(row) - self._data.insert(i, tuple(row)) + if row: + self._validate(row) + self._data.insert(i, tuple(row)) + elif col: + pass class DataBook(object): @@ -200,12 +229,14 @@ class DataBook(object): def __init__(self, sets=[]): self._datasets = sets + def __repr__(self): try: return '<%s databook>' % (self.title.lower()) except AttributeError: return '' + def add_sheet(self, dataset): """Add given dataset .""" if type(dataset) is Dataset: @@ -213,6 +244,7 @@ class DataBook(object): else: raise InvalidDatasetType + def _package(self): collector = [] for dset in self._datasets: @@ -222,6 +254,7 @@ class DataBook(object): )) return collector + @property def size(self): """The number of the Datasets within DataBook.""" @@ -235,8 +268,8 @@ class DataBook(object): stream = cStringIO.StringIO() wb = xlwt.Workbook() - for dset in self._datasets: - ws = wb.add_sheet(dset.title if dset.title else 'Tabbed Dataset %s' % (int(random.random() * 100000000))) + for i, dset in enumerate(self._datasets): + ws = wb.add_sheet(dset.title if dset.title else 'Sheet%s' % (i)) #for row in self._package(dicts=False): for i, row in enumerate(dset._package(dicts=False)): @@ -246,24 +279,29 @@ class DataBook(object): wb.save(stream) return stream.getvalue() + @property def json(self): """Returns JSON representation of Databook.""" return json.dumps(self._package()) + @property def yaml(self): """Returns YAML representation of Databook.""" return yaml.dump(self._package()) - + + class InvalidDatasetType(Exception): "Only Datasets can be added to a DataBook" + class InvalidDimensions(Exception): "Invalid size" + class UnsupportedFormat(NotImplementedError): "Format is not supported" From f31ec562b4834089ac0d4a9510da6a80a5ba9a0a Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Mon, 13 Sep 2010 23:49:16 -0400 Subject: [PATCH 3/5] Extensively testing --- tablib/core.py | 12 +++++++--- test_tablib.py | 63 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/tablib/core.py b/tablib/core.py index 4a0b55f..d359055 100644 --- a/tablib/core.py +++ b/tablib/core.py @@ -15,7 +15,6 @@ import simplejson as json import xlwt import yaml -import showme from helpers import * @@ -141,7 +140,14 @@ class Dataset(object): def headers(self, collection): """Validating headers setter.""" self._validate(collection) - self.__headers = collection + if collection: + try: + self.__headers = list(collection) + except TypeError, why: + raise TypeError + else: + self.__headers = None + @property @@ -188,7 +194,7 @@ class Dataset(object): wb.save(stream) return stream.getvalue() -# @showme.trace + def append(self, row=None, col=None): """Adds a row to the end of Dataset""" if row: diff --git a/test_tablib.py b/test_tablib.py index cea6d24..93ff252 100644 --- a/test_tablib.py +++ b/test_tablib.py @@ -7,15 +7,14 @@ import tablib class TablibTestCase(unittest.TestCase): def setUp(self): - pass + global data + data = tablib.Dataset() def tearDown(self): pass + def test_empty_append(self): - - data = tablib.Dataset() - new_row = (1,2,3) data.append(new_row) @@ -24,17 +23,65 @@ class TablibTestCase(unittest.TestCase): def test_empty_append_with_headers(self): - data = tablib.Dataset() - data.headers = ['first', 'second'] new_row = (1,2,3,4) self.assertRaises(tablib.InvalidDimensions, data.append, new_row) + + + def test_add_column(self): + # No Headers + + data.append(['kenneth']) + data.append(['bessie']) + + new_col = ['reitz', 'monke'] + + data.append(col=new_col) + + self.assertEquals(data[0], ('kenneth', 'reitz')) + self.assertEquals(data.width, 2) + + # With Headers + data.headers = ('fname', 'lname') + new_col = ['age', 21, 22] + data.append(col=new_col) + + self.assertEquals(data[new_col[0]], new_col[1:]) - # def test_adding_header with (self): - + + + def test_add_column_no_data_no_headers(self): + + # no headers + + new_col = ('reitz', 'monke') + + data.append(col=new_col) + + self.assertEquals(data[0], tuple([new_col[0]])) + self.assertEquals(data.width, 1) + self.assertEquals(data.height, len(new_col)) + def test_add_column_no_data_with_headers(self): + # no headers + + data.headers = ('first', 'last') + + new_col = ('age',) + data.append(col=new_col) + + self.assertEquals(len(data.headers), 3) + self.assertEquals(data.width, 3) + + new_col = ('foo', 'bar') + + self.assertRaises(tablib.InvalidDimensions, data.append, col=new_col) + + def tuple_check(self): + data.append(col=(1,2,3)) + if __name__ == '__main__': unittest.main() \ No newline at end of file From bb4e97f8aae83f3f98f265c3cf2c98e0418c83d0 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 14 Sep 2010 00:01:59 -0400 Subject: [PATCH 4/5] Updated readme for column additions. --- README.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 86da367..32d477f 100644 --- a/README.rst +++ b/README.rst @@ -31,11 +31,11 @@ Usage Populate fresh data files: :: - headers = ('first_name', 'last_name', 'gpa') + headers = ('first_name', 'last_name') data = [ - ('John', 'Adams', 90), - ('George', 'Washington', 67) + ('John', 'Adams'), + ('George', 'Washington') ] data = tablib.Dataset(*data, headers=headers) @@ -43,7 +43,11 @@ Populate fresh data files: :: Intelligently add new rows: :: - >>> data.append(('Henry', 'Ford', 83)) + >>> data.append(('Henry', 'Ford')) + +Intelligently add new columns: :: + + >>> data.append(col=('age', 90, 67, 83)) Slice rows: :: @@ -122,7 +126,7 @@ Or, if you absolutely must: :: Contribute ---------- -If you'd like to contribute, simply fork `the repository`_, commit your changes, and send a pull request. Make sure you add yourself to AUTHORS_. +If you'd like to contribute, simply fork `the repository`_, commit your changes to the **develop** branch (or branch off of it), and send a pull request. Make sure you add yourself to AUTHORS_. Roadmap From d0c8df95a3f7234414fe6b7df72cae52b55f6af8 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 14 Sep 2010 00:02:14 -0400 Subject: [PATCH 5/5] Version bump. v0.6.3. --- setup.py | 2 +- tablib/core.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 5670078..02c84d3 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ if sys.argv[-1] == "publish": setup( name='tablib', - version='0.6.2', + version='0.6.3', description='Format agnostic tabular data library (XLS, JSON, YAML, CSV)', long_description=open('README.rst').read() + '\n\n' + open('HISTORY.rst').read(), diff --git a/tablib/core.py b/tablib/core.py index d359055..e4ceee1 100644 --- a/tablib/core.py +++ b/tablib/core.py @@ -21,8 +21,8 @@ from helpers import * # __all__ = ['Dataset', 'DataBook'] __name__ = 'tablib' -__version__ = '0.6.2' -__build__ = 0x000602 +__version__ = '0.6.3' +__build__ = 0x000603 __author__ = 'Kenneth Reitz' __license__ = 'MIT' __copyright__ = 'Copyright 2010 Kenneth Reitz'