import csv from io import 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)) if extension == 'xlsm': file_data = pyexcel_xlsx.get_data(file_content) instance = cls.from_rows( # Get the first sheet from the workbook list(file_data.values())[0], filename, ) return instance 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, )