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, )