mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-20 14:29:51 -04:00
Don’t convert Excel reports to CSV before output
Converting Python data to CSV makes every field a string. This means that in the report we return to the user every field will be a string, even if it’s come from an `int` type in Python. This is because the CSV ‘standard’ doesn’t support any kind of typing. Excel does support types for fields, so we can make our reports more useful by preserving these types. This is particularly relevant in the report we generate for performance platform, which needs the `count` column to be a number type. This commit adds extra code paths to the `Spreadsheet` class which mean that it can be instantiated from either CSV data or a list of Python data. Previously we were converting the Python data to CSV as an intermediate step, before instantiating the class.
This commit is contained in:
42
app/utils.py
42
app/utils.py
@@ -223,9 +223,15 @@ class Spreadsheet():
|
||||
|
||||
allowed_file_extensions = ['csv', 'xlsx', 'xls', 'ods', 'xlsm', 'tsv']
|
||||
|
||||
def __init__(self, csv_data, filename=''):
|
||||
def __init__(self, csv_data=None, rows=None, filename=''):
|
||||
|
||||
self.filename = filename
|
||||
self.as_csv_data = csv_data
|
||||
|
||||
if csv_data and rows:
|
||||
raise TypeError('Spreadsheet must be created from either rows or CSV data')
|
||||
|
||||
self._csv_data = csv_data or ''
|
||||
self._rows = rows or []
|
||||
|
||||
@property
|
||||
def as_dict(self):
|
||||
@@ -234,6 +240,16 @@ class Spreadsheet():
|
||||
'data': self.as_csv_data
|
||||
}
|
||||
|
||||
@property
|
||||
def as_csv_data(self):
|
||||
if not self._csv_data:
|
||||
with StringIO() as converted:
|
||||
output = csv.writer(converted)
|
||||
for row in self._rows:
|
||||
output.writerow(row)
|
||||
self._csv_data = converted.getvalue()
|
||||
return self._csv_data
|
||||
|
||||
@classmethod
|
||||
def can_handle(cls, filename):
|
||||
return cls.get_extension(filename) in cls.allowed_file_extensions
|
||||
@@ -248,11 +264,7 @@ class Spreadsheet():
|
||||
|
||||
@classmethod
|
||||
def from_rows(cls, rows, filename=''):
|
||||
with StringIO() as converted:
|
||||
output = csv.writer(converted)
|
||||
for row in rows:
|
||||
output.writerow(row)
|
||||
return cls(converted.getvalue(), filename)
|
||||
return cls(rows=rows, filename=filename)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dictionary, filename=''):
|
||||
@@ -260,7 +272,7 @@ class Spreadsheet():
|
||||
zip(
|
||||
*sorted(dictionary.items(), key=lambda pair: pair[0])
|
||||
),
|
||||
filename
|
||||
filename=filename,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -268,7 +280,7 @@ class Spreadsheet():
|
||||
extension = cls.get_extension(filename)
|
||||
|
||||
if extension == 'csv':
|
||||
return cls(Spreadsheet.normalise_newlines(file_content), filename)
|
||||
return cls(Spreadsheet.normalise_newlines(file_content), filename=filename)
|
||||
|
||||
if extension == 'tsv':
|
||||
file_content = StringIO(
|
||||
@@ -284,11 +296,13 @@ class Spreadsheet():
|
||||
|
||||
@property
|
||||
def as_rows(self):
|
||||
return list(csv.reader(
|
||||
self.as_csv_data.strip().splitlines(),
|
||||
quoting=csv.QUOTE_MINIMAL,
|
||||
skipinitialspace=True,
|
||||
))
|
||||
if not self._rows:
|
||||
self._rows = list(csv.reader(
|
||||
self._csv_data.strip().splitlines(),
|
||||
quoting=csv.QUOTE_MINIMAL,
|
||||
skipinitialspace=True,
|
||||
))
|
||||
return self._rows
|
||||
|
||||
@property
|
||||
def as_excel_file(self):
|
||||
|
||||
Reference in New Issue
Block a user