From 9fdb72cc5c69138d19c969d9cef91c5f60cd6bd0 Mon Sep 17 00:00:00 2001 From: Brian Painter Date: Wed, 23 Mar 2016 08:21:36 -0400 Subject: if the object is a decimal, return the string representation of it. --- tablib/formats/_json.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py index 777040a..d063d10 100644 --- a/tablib/formats/_json.py +++ b/tablib/formats/_json.py @@ -2,6 +2,7 @@ """ Tablib - JSON Support """ +import decimal import tablib @@ -14,7 +15,13 @@ extensions = ('json', 'jsn') def date_handler(obj): - return obj.isoformat() if hasattr(obj, 'isoformat') else obj + if isinstance(obj, decimal.Decimal): + return str(obj) + elif hasattr(obj, 'isoformat'): + return obj.isoformat() + else: + return obj + # return obj.isoformat() if hasattr(obj, 'isoformat') else obj def export_set(dataset): -- cgit v1.2.1 From 91d3299280a57a6780c446c21a506f6259552c0a Mon Sep 17 00:00:00 2001 From: Dylan Verheul Date: Wed, 30 Nov 2016 12:32:47 +0100 Subject: Fix date and datetime export to JSON in Python versions with a json package Python without a json package will use omnijson and fail on date and datetime objects. Added unit tests. --- tablib/formats/_json.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py index 777040a..fb66745 100644 --- a/tablib/formats/_json.py +++ b/tablib/formats/_json.py @@ -5,8 +5,10 @@ import tablib -import sys -from tablib.packages import omnijson as json +try: + import json +except ImportError: + from tablib.packages import omnijson as json title = 'json' -- cgit v1.2.1 From bb1354b61f10cf82bcd7cccf25de3d3fa2dab4ce Mon Sep 17 00:00:00 2001 From: tomwen Date: Fri, 30 Dec 2016 10:21:43 +0800 Subject: remove file must be close it. in windows if you don't close template file, remove it will raise WindowsError: [Error 32] --- tablib/formats/_dbf.py | 1 + 1 file changed, 1 insertion(+) (limited to 'tablib/formats') diff --git a/tablib/formats/_dbf.py b/tablib/formats/_dbf.py index 41c2ef4..a0c183d 100644 --- a/tablib/formats/_dbf.py +++ b/tablib/formats/_dbf.py @@ -55,6 +55,7 @@ def export_set(dataset): else: stream = StringIO(dbf_stream.read()) dbf_stream.close() + os.close(temp_file) os.remove(temp_uri) return stream.getvalue() -- cgit v1.2.1 From e66eb4a18967dd4d35e6e9da265622e9e4015103 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 20 Feb 2017 16:41:33 +0100 Subject: Replaced vendored openpyxl by a dependency (#221) It is time to make it happen. * Dropped Python 3.2 support Recent dependencies are dropping Python 3.2 too. * Replaced vendored openpyxl by a dependency Thanks Tommy Anthony for the initial patch. --- tablib/formats/_xlsx.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index 411e0fc..20f55df 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -11,12 +11,12 @@ if sys.version_info[0] > 2: else: from cStringIO import StringIO as BytesIO -from tablib.compat import openpyxl +import openpyxl import tablib Workbook = openpyxl.workbook.Workbook ExcelWriter = openpyxl.writer.excel.ExcelWriter -get_column_letter = openpyxl.cell.get_column_letter +get_column_letter = openpyxl.utils.get_column_letter from tablib.compat import unicode @@ -51,7 +51,8 @@ def export_book(databook, freeze_panes=True): """Returns XLSX representation of DataBook.""" wb = Workbook() - wb.worksheets = [] + for sheet in wb.worksheets: + wb.remove_sheet(sheet) for i, dset in enumerate(databook._datasets): ws = wb.create_sheet() ws.title = dset.title if dset.title else 'Sheet%s' % (i) @@ -111,42 +112,38 @@ def dset_sheet(dataset, ws, freeze_panes=True): _offset = i _package.insert((sep[0] + _offset), (sep[1],)) + bold = openpyxl.styles.Font(bold=True) + wrap_text = openpyxl.styles.Alignment(wrap_text=True) + for i, row in enumerate(_package): row_number = i + 1 for j, col in enumerate(row): col_idx = get_column_letter(j + 1) + cell = ws.cell('%s%s' % (col_idx, row_number)) # bold headers if (row_number == 1) and dataset.headers: - # ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - # '%s' % col, errors='ignore') - ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col) - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.font.bold = True + # cell.value = unicode('%s' % col, errors='ignore') + cell.value = unicode(col) + cell.font = bold if freeze_panes: - # As already done in #53, but after Merge lost: # Export Freeze only after first Line ws.freeze_panes = 'A2' # bold separators elif len(row) < dataset.width: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.font.bold = True + cell.value = unicode('%s' % col, errors='ignore') + cell.font = bold # wrap the rest else: try: if '\n' in col: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') - style = ws.get_style('%s%s' % (col_idx, row_number)) - style.alignment.wrap_text + cell.value = unicode('%s' % col, errors='ignore') + cell.alignment = wrap_text else: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode( - '%s' % col, errors='ignore') + cell.value = unicode('%s' % col, errors='ignore') except TypeError: - ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col) + cell.value = unicode(col) -- cgit v1.2.1 From 0ca5520bbc1474dbd872a3f330c681a5201336fd Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 20 Feb 2017 21:29:22 +0100 Subject: Replaced vendored xlrd/xlwt by dependencies (#277) Refs #273. --- tablib/formats/_xls.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_xls.py b/tablib/formats/_xls.py index 787907a..77e2bb1 100644 --- a/tablib/formats/_xls.py +++ b/tablib/formats/_xls.py @@ -5,8 +5,11 @@ import sys -from tablib.compat import BytesIO, xlwt, xlrd, XLRDError, xrange +from tablib.compat import BytesIO, xrange import tablib +import xlrd +import xlwt +from xlrd.biffh import XLRDError title = 'xls' extensions = ('xls',) -- cgit v1.2.1 From 44e9e24fec35bf7697e22aaa94feccdad23e5b79 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 20 Feb 2017 23:41:38 +0100 Subject: Replaced vendored pyyaml by a dependency (#278) --- tablib/formats/_yaml.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_yaml.py b/tablib/formats/_yaml.py index f2359cf..5aecb42 100644 --- a/tablib/formats/_yaml.py +++ b/tablib/formats/_yaml.py @@ -3,17 +3,8 @@ """ Tablib - YAML Support. """ -import sys - -try: - import yaml -except ImportError: - if sys.version_info[0] > 2: - import tablib.packages.yaml3 as yaml - else: - import tablib.packages.yaml as yaml - import tablib +import yaml title = 'yaml' extensions = ('yaml', 'yml') -- cgit v1.2.1 From 46102d4be718ee6fd6c003c1387df7c940e65b2c Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Fri, 24 Feb 2017 13:38:07 +0100 Subject: Replaced vendored omnijson by the standard lib version (#279) Refs #273. --- tablib/formats/_json.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py index 72c3d02..a3d6cc3 100644 --- a/tablib/formats/_json.py +++ b/tablib/formats/_json.py @@ -7,10 +7,9 @@ import decimal import tablib try: - import json + import ujson as json except ImportError: - from tablib.packages import omnijson as json - + import json title = 'json' extensions = ('json', 'jsn') -- cgit v1.2.1 From 62807734bd6f7f4ade5befb9a22b091db4182010 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 26 Feb 2017 23:05:01 +0100 Subject: Replaced vendored odfpy by a dependency (#280) Refs #273. --- tablib/formats/_ods.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_ods.py b/tablib/formats/_ods.py index f43a26c..c7af891 100644 --- a/tablib/formats/_ods.py +++ b/tablib/formats/_ods.py @@ -3,15 +3,8 @@ """ Tablib - ODF Support. """ -import sys - - -if sys.version_info[0] > 2: - from io import BytesIO -else: - from cStringIO import StringIO as BytesIO - -from tablib.compat import opendocument, style, table, text, unicode +from odf import opendocument, style, table, text +from tablib.compat import BytesIO, unicode title = 'ods' extensions = ('ods',) @@ -97,4 +90,4 @@ def dset_sheet(dataset, ws): ws.addElement(odf_row) cell = table.TableCell() cell.addElement(text.P(text=col)) - odf_row.addElement(cell) \ No newline at end of file + odf_row.addElement(cell) -- cgit v1.2.1 From 80e72cfa27264efb9f525bd92ce6476c5eadb3e9 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Thu, 12 Jan 2017 09:49:45 +0000 Subject: Fix unicode encode errors on Python 2 -- Fixes #215 Switch csv library to backports.csv as the implementation is closer to the python 3 one. Add a test case covering the problem. Run tests with unicode_literals from future Fix unicode encode errors with unicode characters - Use `backports.csv` instead of `unicodecsv` - Use StringIO instead of cStringIO - Clean-up some Python 2 specific code --- tablib/formats/_csv.py | 9 ++------- tablib/formats/_tsv.py | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_csv.py b/tablib/formats/_csv.py index 994b23b..b74afd7 100644 --- a/tablib/formats/_csv.py +++ b/tablib/formats/_csv.py @@ -3,15 +3,14 @@ """ Tablib - *SV Support. """ -from tablib.compat import is_py3, csv, StringIO +from tablib.compat import csv, StringIO, unicode title = 'csv' extensions = ('csv',) -DEFAULT_ENCODING = 'utf-8' -DEFAULT_DELIMITER = ',' +DEFAULT_DELIMITER = unicode(',') def export_set(dataset, **kwargs): @@ -19,8 +18,6 @@ def export_set(dataset, **kwargs): stream = StringIO() kwargs.setdefault('delimiter', DEFAULT_DELIMITER) - if not is_py3: - kwargs.setdefault('encoding', DEFAULT_ENCODING) _csv = csv.writer(stream, **kwargs) @@ -36,8 +33,6 @@ def import_set(dset, in_stream, headers=True, **kwargs): dset.wipe() kwargs.setdefault('delimiter', DEFAULT_DELIMITER) - if not is_py3: - kwargs.setdefault('encoding', DEFAULT_ENCODING) rows = csv.reader(StringIO(in_stream), **kwargs) for i, row in enumerate(rows): diff --git a/tablib/formats/_tsv.py b/tablib/formats/_tsv.py index 9380b3b..1c6d6a1 100644 --- a/tablib/formats/_tsv.py +++ b/tablib/formats/_tsv.py @@ -3,6 +3,7 @@ """ Tablib - TSV (Tab Separated Values) Support. """ +from tablib.compat import unicode from tablib.formats._csv import ( export_set as export_set_wrapper, import_set as import_set_wrapper, @@ -12,8 +13,7 @@ from tablib.formats._csv import ( title = 'tsv' extensions = ('tsv',) -DEFAULT_ENCODING = 'utf-8' -DELIMITER = '\t' +DELIMITER = unicode('\t') def export_set(dataset): """Returns TSV representation of Dataset.""" -- cgit v1.2.1 From 69abfc3ada5d754cb152119c0b4777043657cb6e Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 13 Jun 2017 12:29:55 -0400 Subject: use safe load --- tablib/formats/_yaml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_yaml.py b/tablib/formats/_yaml.py index 5aecb42..3d17baf 100644 --- a/tablib/formats/_yaml.py +++ b/tablib/formats/_yaml.py @@ -33,7 +33,7 @@ def import_book(dbook, in_stream): dbook.wipe() - for sheet in yaml.load(in_stream): + for sheet in yaml.safe_load(in_stream): data = tablib.Dataset() data.title = sheet['title'] data.dict = sheet['data'] -- cgit v1.2.1 From 00e2ffa2efd1a7dd47d2a4a2a4303e1a8beb71e3 Mon Sep 17 00:00:00 2001 From: Jason Myers Date: Sat, 26 Aug 2017 20:43:35 -0500 Subject: Adding initial DataFrames Support Signed-off-by: Jason Myers --- tablib/formats/__init__.py | 3 ++- tablib/formats/_df.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tablib/formats/_df.py (limited to 'tablib/formats') diff --git a/tablib/formats/__init__.py b/tablib/formats/__init__.py index 5cca19f..94b5bc9 100644 --- a/tablib/formats/__init__.py +++ b/tablib/formats/__init__.py @@ -13,5 +13,6 @@ from . import _xlsx as xlsx from . import _ods as ods from . import _dbf as dbf from . import _latex as latex +from . import _df as df -available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods) +available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods, df) diff --git a/tablib/formats/_df.py b/tablib/formats/_df.py new file mode 100644 index 0000000..5996ce9 --- /dev/null +++ b/tablib/formats/_df.py @@ -0,0 +1,40 @@ +""" Tablib - DataFrame Support. +""" + + +import sys + + +if sys.version_info[0] > 2: + from io import BytesIO +else: + from cStringIO import StringIO as BytesIO + +from pandas import DataFrame + +import tablib + +from tablib.compat import unicode + +title = 'df' +extensions = ('df', ) + +def detect(stream): + """Returns True if given stream is a DataFrame.""" + try: + DataFrame(stream) + return True + except ValueError: + return False + + +def export_set(dset, index=None): + """Returns DataFrame representation of DataBook.""" + dataframe = DataFrame(dset.dict, columns=dset.headers) + return dataframe + + +def import_set(dset, in_stream): + """Returns dataset from DataFrame.""" + dset.wipe() + dset.dict = in_stream.to_dict(orient='records') -- cgit v1.2.1 From 7f1db4023f2310529822d721379b1019aaf320fc Mon Sep 17 00:00:00 2001 From: Ryan Castner Date: Fri, 1 Sep 2017 13:21:21 -0400 Subject: Raise NotImplementedError if pandas is not installed --- tablib/formats/_df.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_df.py b/tablib/formats/_df.py index 5996ce9..44b967f 100644 --- a/tablib/formats/_df.py +++ b/tablib/formats/_df.py @@ -10,7 +10,10 @@ if sys.version_info[0] > 2: else: from cStringIO import StringIO as BytesIO -from pandas import DataFrame +try: + from pandas import DataFrame +except ImportError: + DataFrame = None import tablib @@ -21,6 +24,8 @@ extensions = ('df', ) def detect(stream): """Returns True if given stream is a DataFrame.""" + if DataFrame is None: + return False try: DataFrame(stream) return True @@ -30,6 +35,10 @@ def detect(stream): def export_set(dset, index=None): """Returns DataFrame representation of DataBook.""" + if DataFrame is None: + raise NotImplementedError( + 'DataFrame Format requires `pandas` to be installed.' + ' Try `pip install tablib[pandas]`.') dataframe = DataFrame(dset.dict, columns=dset.headers) return dataframe -- cgit v1.2.1 From 61063e2b09a5cd26926311a34826fd33be0b1757 Mon Sep 17 00:00:00 2001 From: DougHudgeon Date: Mon, 25 Jun 2018 14:17:34 +1000 Subject: Updated xlsx format to use openpyxl's .active property --- tablib/formats/_xlsx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index 20f55df..67aa5f4 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -71,7 +71,7 @@ def import_set(dset, in_stream, headers=True): dset.wipe() xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream)) - sheet = xls_book.get_active_sheet() + sheet = xls_book.active dset.title = sheet.title -- cgit v1.2.1 From ac3cf67620c89f71b73bed891355e8dc0491a541 Mon Sep 17 00:00:00 2001 From: Gregory Bataille Date: Wed, 12 Sep 2018 13:34:55 +0200 Subject: fix(): remove openpyxl warning by properly accessing cells (#296) --- tablib/formats/_xlsx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index 20f55df..eb921f9 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -119,7 +119,7 @@ def dset_sheet(dataset, ws, freeze_panes=True): row_number = i + 1 for j, col in enumerate(row): col_idx = get_column_letter(j + 1) - cell = ws.cell('%s%s' % (col_idx, row_number)) + cell = ws['%s%s' % (col_idx, row_number)] # bold headers if (row_number == 1) and dataset.headers: -- cgit v1.2.1 From 38486231cc4dc46d1ff055a38fe7c236cbe5c8aa Mon Sep 17 00:00:00 2001 From: Norman Hooper Date: Wed, 12 Sep 2018 20:27:10 +0200 Subject: reStructuredText (#336) * median for Python 2 * More compat * Support reStructuredText * Tests --- tablib/formats/__init__.py | 3 +- tablib/formats/_rst.py | 273 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tablib/formats/_rst.py (limited to 'tablib/formats') diff --git a/tablib/formats/__init__.py b/tablib/formats/__init__.py index 94b5bc9..3eb747e 100644 --- a/tablib/formats/__init__.py +++ b/tablib/formats/__init__.py @@ -14,5 +14,6 @@ from . import _ods as ods from . import _dbf as dbf from . import _latex as latex from . import _df as df +from . import _rst as rst -available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods, df) +available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods, df, rst) diff --git a/tablib/formats/_rst.py b/tablib/formats/_rst.py new file mode 100644 index 0000000..4b53ad7 --- /dev/null +++ b/tablib/formats/_rst.py @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- + +""" Tablib - reStructuredText Support +""" +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from textwrap import TextWrapper + +from tablib.compat import ( + median, + unicode, + izip_longest, +) + + +title = 'rst' +extensions = ('rst',) + + +MAX_TABLE_WIDTH = 80 # Roughly. It may be wider to avoid breaking words. + + +JUSTIFY_LEFT = 'left' +JUSTIFY_CENTER = 'center' +JUSTIFY_RIGHT = 'right' +JUSTIFY_VALUES = (JUSTIFY_LEFT, JUSTIFY_CENTER, JUSTIFY_RIGHT) + + +def to_unicode(value): + if isinstance(value, bytes): + return value.decode('utf-8') + return unicode(value) + + +def _max_word_len(text): + """ + Return the length of the longest word in `text`. + + + >>> _max_word_len('Python Module for Tabular Datasets') + 8 + + """ + return max((len(word) for word in text.split())) + + +def _get_column_string_lengths(dataset): + """ + Returns a list of string lengths of each column, and a list of + maximum word lengths. + """ + if dataset.headers: + column_lengths = [[len(h)] for h in dataset.headers] + word_lens = [_max_word_len(h) for h in dataset.headers] + else: + column_lengths = [[] for _ in range(dataset.width)] + word_lens = [0 for _ in range(dataset.width)] + for row in dataset.dict: + values = iter(row.values() if hasattr(row, 'values') else row) + for i, val in enumerate(values): + text = to_unicode(val) + column_lengths[i].append(len(text)) + word_lens[i] = max(word_lens[i], _max_word_len(text)) + return column_lengths, word_lens + + +def _row_to_lines(values, widths, wrapper, sep='|', justify=JUSTIFY_LEFT): + """ + Returns a table row of wrapped values as a list of lines + """ + if justify not in JUSTIFY_VALUES: + raise ValueError('Value of "justify" must be one of "{}"'.format( + '", "'.join(JUSTIFY_VALUES) + )) + if justify == JUSTIFY_LEFT: + just = lambda text, width: text.ljust(width) + elif justify == JUSTIFY_CENTER: + just = lambda text, width: text.center(width) + else: + just = lambda text, width: text.rjust(width) + lpad = sep + ' ' if sep else '' + rpad = ' ' + sep if sep else '' + pad = ' ' + sep + ' ' + cells = [] + for value, width in zip(values, widths): + wrapper.width = width + text = to_unicode(value) + cell = wrapper.wrap(text) + cells.append(cell) + lines = izip_longest(*cells, fillvalue='') + lines = ( + (just(cell_line, widths[i]) for i, cell_line in enumerate(line)) + for line in lines + ) + lines = [''.join((lpad, pad.join(line), rpad)) for line in lines] + return lines + + +def _get_column_widths(dataset, max_table_width=MAX_TABLE_WIDTH, pad_len=3): + """ + Returns a list of column widths proportional to the median length + of the text in their cells. + """ + str_lens, word_lens = _get_column_string_lengths(dataset) + median_lens = [int(median(lens)) for lens in str_lens] + total = sum(median_lens) + if total > max_table_width - (pad_len * len(median_lens)): + column_widths = (max_table_width * l // total for l in median_lens) + else: + column_widths = (l for l in median_lens) + # Allow for separator and padding: + column_widths = (w - pad_len if w > pad_len else w for w in column_widths) + # Rather widen table than break words: + column_widths = [max(w, l) for w, l in zip(column_widths, word_lens)] + return column_widths + + +def export_set_as_simple_table(dataset, column_widths=None): + """ + Returns reStructuredText grid table representation of dataset. + """ + lines = [] + wrapper = TextWrapper() + if column_widths is None: + column_widths = _get_column_widths(dataset, pad_len=2) + border = ' '.join(['=' * w for w in column_widths]) + + lines.append(border) + if dataset.headers: + lines.extend(_row_to_lines( + dataset.headers, + column_widths, + wrapper, + sep='', + justify=JUSTIFY_CENTER, + )) + lines.append(border) + for row in dataset.dict: + values = iter(row.values() if hasattr(row, 'values') else row) + lines.extend(_row_to_lines(values, column_widths, wrapper, '')) + lines.append(border) + return '\n'.join(lines) + + +def export_set_as_grid_table(dataset, column_widths=None): + """ + Returns reStructuredText grid table representation of dataset. + + + >>> from tablib import Dataset + >>> from tablib.formats import rst + >>> bits = ((0, 0), (1, 0), (0, 1), (1, 1)) + >>> data = Dataset() + >>> data.headers = ['A', 'B', 'A and B'] + >>> for a, b in bits: + ... data.append([bool(a), bool(b), bool(a * b)]) + >>> print(rst.export_set(data, force_grid=True)) + +-------+-------+-------+ + | A | B | A and | + | | | B | + +=======+=======+=======+ + | False | False | False | + +-------+-------+-------+ + | True | False | False | + +-------+-------+-------+ + | False | True | False | + +-------+-------+-------+ + | True | True | True | + +-------+-------+-------+ + + """ + lines = [] + wrapper = TextWrapper() + if column_widths is None: + column_widths = _get_column_widths(dataset) + header_sep = '+=' + '=+='.join(['=' * w for w in column_widths]) + '=+' + row_sep = '+-' + '-+-'.join(['-' * w for w in column_widths]) + '-+' + + lines.append(row_sep) + if dataset.headers: + lines.extend(_row_to_lines( + dataset.headers, + column_widths, + wrapper, + justify=JUSTIFY_CENTER, + )) + lines.append(header_sep) + for row in dataset.dict: + values = iter(row.values() if hasattr(row, 'values') else row) + lines.extend(_row_to_lines(values, column_widths, wrapper)) + lines.append(row_sep) + return '\n'.join(lines) + + +def _use_simple_table(head0, col0, width0): + """ + Use a simple table if the text in the first column is never wrapped + + + >>> _use_simple_table('menu', ['egg', 'bacon'], 10) + True + >>> _use_simple_table(None, ['lobster thermidor', 'spam'], 10) + False + + """ + if head0 is not None: + head0 = to_unicode(head0) + if len(head0) > width0: + return False + for cell in col0: + cell = to_unicode(cell) + if len(cell) > width0: + return False + return True + + +def export_set(dataset, **kwargs): + """ + Returns reStructuredText table representation of dataset. + + Returns a simple table if the text in the first column is never + wrapped, otherwise returns a grid table. + + + >>> from tablib import Dataset + >>> bits = ((0, 0), (1, 0), (0, 1), (1, 1)) + >>> data = Dataset() + >>> data.headers = ['A', 'B', 'A and B'] + >>> for a, b in bits: + ... data.append([bool(a), bool(b), bool(a * b)]) + >>> table = data.rst + >>> table.split('\\n') == [ + ... '===== ===== =====', + ... ' A B A and', + ... ' B ', + ... '===== ===== =====', + ... 'False False False', + ... 'True False False', + ... 'False True False', + ... 'True True True ', + ... '===== ===== =====', + ... ] + True + + """ + if not dataset.dict: + return '' + force_grid = kwargs.get('force_grid', False) + max_table_width = kwargs.get('max_table_width', MAX_TABLE_WIDTH) + column_widths = _get_column_widths(dataset, max_table_width) + + use_simple_table = _use_simple_table( + dataset.headers[0] if dataset.headers else None, + dataset.get_col(0), + column_widths[0], + ) + if use_simple_table and not force_grid: + return export_set_as_simple_table(dataset, column_widths) + else: + return export_set_as_grid_table(dataset, column_widths) + + +def export_book(databook): + """ + reStructuredText representation of a Databook. + + Tables are separated by a blank line. All tables use the grid + format. + """ + return '\n\n'.join(export_set(dataset, force_grid=True) + for dataset in databook._datasets) -- cgit v1.2.1 From 3d5943a8a43a721bea1f3e33dc669a581a095c53 Mon Sep 17 00:00:00 2001 From: Bruno Soares Date: Wed, 12 Sep 2018 15:49:46 -0300 Subject: Fix: Circular reference detected error (#332) * Rename function name * Add uuid handler on json dumps * Add myself to authors --- tablib/formats/_json.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py index a3d6cc3..a3b88f8 100644 --- a/tablib/formats/_json.py +++ b/tablib/formats/_json.py @@ -3,6 +3,7 @@ """ Tablib - JSON Support """ import decimal +from uuid import UUID import tablib @@ -15,24 +16,23 @@ title = 'json' extensions = ('json', 'jsn') -def date_handler(obj): - if isinstance(obj, decimal.Decimal): +def serialize_objects_handler(obj): + if isinstance(obj, decimal.Decimal) or isinstance(obj, UUID): return str(obj) elif hasattr(obj, 'isoformat'): return obj.isoformat() else: return obj - # return obj.isoformat() if hasattr(obj, 'isoformat') else obj def export_set(dataset): """Returns JSON representation of Dataset.""" - return json.dumps(dataset.dict, default=date_handler) + return json.dumps(dataset.dict, default=serialize_objects_handler) def export_book(databook): """Returns JSON representation of Databook.""" - return json.dumps(databook._package(), default=date_handler) + return json.dumps(databook._package(), default=serialize_objects_handler) def import_set(dset, in_stream): -- cgit v1.2.1 From 4f8949417e5126f8416b61d7feb6cd666bae4fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20=22RooTer=22=20Urba=C5=84ski?= Date: Wed, 12 Sep 2018 21:15:20 +0200 Subject: ujson presence no longer breaks tablib (resolves #297) (#311) --- tablib/formats/_json.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_json.py b/tablib/formats/_json.py index a3b88f8..bbd2c96 100644 --- a/tablib/formats/_json.py +++ b/tablib/formats/_json.py @@ -3,14 +3,11 @@ """ Tablib - JSON Support """ import decimal +import json from uuid import UUID import tablib -try: - import ujson as json -except ImportError: - import json title = 'json' extensions = ('json', 'jsn') -- cgit v1.2.1 From d38549ef1e43c6c296b7f3d44210d61e34e6c4c9 Mon Sep 17 00:00:00 2001 From: lepuchi Date: Tue, 2 Oct 2018 23:26:19 +0530 Subject: only add row if it exists --- tablib/formats/_csv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_csv.py b/tablib/formats/_csv.py index 994b23b..06e7830 100644 --- a/tablib/formats/_csv.py +++ b/tablib/formats/_csv.py @@ -44,7 +44,7 @@ def import_set(dset, in_stream, headers=True, **kwargs): if (i == 0) and (headers): dset.headers = row - else: + elif row: dset.append(row) -- cgit v1.2.1 From a28a057559bcfb39612e91af59f0e80a1796caf6 Mon Sep 17 00:00:00 2001 From: Tsuyoshi Hombashi Date: Sat, 6 Oct 2018 19:19:09 +0900 Subject: Replace a deprecated method call Workbook.remove_sheet method deprecated since openpyxl 2.4.0 --- tablib/formats/_xlsx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index eb921f9..1d29ba8 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -52,7 +52,7 @@ def export_book(databook, freeze_panes=True): wb = Workbook() for sheet in wb.worksheets: - wb.remove_sheet(sheet) + wb.remove(sheet) for i, dset in enumerate(databook._datasets): ws = wb.create_sheet() ws.title = dset.title if dset.title else 'Sheet%s' % (i) -- cgit v1.2.1 From 102073c426d7a76384e75379abbf1a3b0ed78662 Mon Sep 17 00:00:00 2001 From: Mathias Loesch Date: Wed, 23 Jan 2019 14:18:01 +0100 Subject: Add Jira table export --- tablib/formats/__init__.py | 3 ++- tablib/formats/_jira.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tablib/formats/_jira.py (limited to 'tablib/formats') diff --git a/tablib/formats/__init__.py b/tablib/formats/__init__.py index 3eb747e..418e607 100644 --- a/tablib/formats/__init__.py +++ b/tablib/formats/__init__.py @@ -15,5 +15,6 @@ from . import _dbf as dbf from . import _latex as latex from . import _df as df from . import _rst as rst +from . import _jira as jira -available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods, df, rst) +available = (json, xls, yaml, csv, dbf, tsv, html, jira, latex, xlsx, ods, df, rst) diff --git a/tablib/formats/_jira.py b/tablib/formats/_jira.py new file mode 100644 index 0000000..55fce52 --- /dev/null +++ b/tablib/formats/_jira.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +"""Tablib - Jira table export support. + + Generates a Jira table from the dataset. +""" +from tablib.compat import unicode + +title = 'jira' + + +def export_set(dataset): + """Formats the dataset according to the Jira table syntax: + + ||heading 1||heading 2||heading 3|| + |col A1|col A2|col A3| + |col B1|col B2|col B3| + + :param dataset: dataset to serialize + :type dataset: tablib.core.Dataset + """ + + header = _get_header(dataset.headers) if dataset.headers else '' + body = _get_body(dataset) + return '%s\n%s' % (header, body) if header else body + + +def _get_body(dataset): + return '\n'.join([_serialize_row(row) for row in dataset]) + + +def _get_header(headers): + return _serialize_row(headers, delimiter='||') + + +def _serialize_row(row, delimiter='|'): + return '%s%s%s' % (delimiter, + delimiter.join([unicode(item) if item else ' ' for item in row]), + delimiter) -- cgit v1.2.1 From b057cdf05e4b9d20f6e1743fc9c63d32c54f1908 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sat, 2 Mar 2019 08:42:53 -0800 Subject: Trim trailing white space throughout the project Many editors clean up trailing white space on save. By removing it all in one go, it helps keep future diffs cleaner by avoiding spurious white space changes on unrelated lines. --- tablib/formats/_dbf.py | 3 --- tablib/formats/_xls.py | 2 +- tablib/formats/_xlsx.py | 4 +--- 3 files changed, 2 insertions(+), 7 deletions(-) (limited to 'tablib/formats') diff --git a/tablib/formats/_dbf.py b/tablib/formats/_dbf.py index a0c183d..710797d 100644 --- a/tablib/formats/_dbf.py +++ b/tablib/formats/_dbf.py @@ -89,6 +89,3 @@ def detect(stream): # When unpacking a string argument with less than 8 chars, struct.error is # raised. return False - - - diff --git a/tablib/formats/_xls.py b/tablib/formats/_xls.py index 77e2bb1..82f8b86 100644 --- a/tablib/formats/_xls.py +++ b/tablib/formats/_xls.py @@ -25,7 +25,7 @@ def detect(stream): xlrd.open_workbook(file_contents=stream) return True except (TypeError, XLRDError): - pass + pass try: xlrd.open_workbook(file_contents=stream.read()) return True diff --git a/tablib/formats/_xlsx.py b/tablib/formats/_xlsx.py index 816fd37..ebafc4f 100644 --- a/tablib/formats/_xlsx.py +++ b/tablib/formats/_xlsx.py @@ -129,7 +129,7 @@ def dset_sheet(dataset, ws, freeze_panes=True): if freeze_panes: # Export Freeze only after first Line ws.freeze_panes = 'A2' - + # bold separators elif len(row) < dataset.width: cell.value = unicode('%s' % col, errors='ignore') @@ -145,5 +145,3 @@ def dset_sheet(dataset, ws, freeze_panes=True): cell.value = unicode('%s' % col, errors='ignore') except TypeError: cell.value = unicode(col) - - -- cgit v1.2.1