mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-06 11:23:48 -05:00
`app/utils.py` is a bit of a dumping ground for things we don’t have a better place for. We now have a place and structure for storing ‘model’ code (‘model’ in the model, view, controller (MVC) sense of the word). This commit moves the spreadsheet model to that place.
106 lines
2.8 KiB
Python
106 lines
2.8 KiB
Python
import csv
|
|
from io import BytesIO, StringIO
|
|
from os import path
|
|
|
|
import pyexcel
|
|
import pyexcel_xlsx
|
|
|
|
|
|
class Spreadsheet():
|
|
|
|
ALLOWED_FILE_EXTENSIONS = ('csv', 'xlsx', 'xls', 'ods', 'xlsm', 'tsv')
|
|
|
|
def __init__(self, csv_data=None, rows=None, filename=''):
|
|
|
|
self.filename = filename
|
|
|
|
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):
|
|
return {
|
|
'file_name': self.filename,
|
|
'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
|
|
|
|
@staticmethod
|
|
def get_extension(filename):
|
|
return path.splitext(filename)[1].lower().lstrip('.')
|
|
|
|
@staticmethod
|
|
def normalise_newlines(file_content):
|
|
return '\r\n'.join(file_content.read().decode('utf-8').splitlines())
|
|
|
|
@classmethod
|
|
def from_rows(cls, rows, filename=''):
|
|
return cls(rows=rows, filename=filename)
|
|
|
|
@classmethod
|
|
def from_dict(cls, dictionary, filename=''):
|
|
return cls.from_rows(
|
|
zip(
|
|
*sorted(dictionary.items(), key=lambda pair: pair[0])
|
|
),
|
|
filename=filename,
|
|
)
|
|
|
|
@classmethod
|
|
def from_file(cls, file_content, filename=''):
|
|
extension = cls.get_extension(filename)
|
|
|
|
if extension == 'csv':
|
|
return cls(csv_data=Spreadsheet.normalise_newlines(file_content), filename=filename)
|
|
|
|
if extension == 'tsv':
|
|
file_content = StringIO(
|
|
Spreadsheet.normalise_newlines(file_content))
|
|
|
|
instance = cls.from_rows(
|
|
pyexcel.iget_array(
|
|
file_type=extension,
|
|
file_stream=file_content),
|
|
filename)
|
|
pyexcel.free_resources()
|
|
return instance
|
|
|
|
@classmethod
|
|
def from_file_form(cls, form):
|
|
return cls.from_file(
|
|
form.file.data,
|
|
filename=form.file.data.filename,
|
|
)
|
|
|
|
@property
|
|
def as_rows(self):
|
|
if not self._rows:
|
|
self._rows = list(csv.reader(
|
|
StringIO(self._csv_data),
|
|
quoting=csv.QUOTE_MINIMAL,
|
|
skipinitialspace=True,
|
|
))
|
|
return self._rows
|
|
|
|
@property
|
|
def as_excel_file(self):
|
|
io = BytesIO()
|
|
pyexcel_xlsx.save_data(io, {'Sheet 1': self.as_rows})
|
|
return io.getvalue()
|