diff --git a/src/pretix/base/exporter.py b/src/pretix/base/exporter.py index d1737857d4..2372acef67 100644 --- a/src/pretix/base/exporter.py +++ b/src/pretix/base/exporter.py @@ -5,7 +5,7 @@ from typing import Tuple from defusedcsv import csv from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext, ugettext_lazy as _ from openpyxl import Workbook from openpyxl.cell.cell import KNOWN_TYPES @@ -143,3 +143,73 @@ class ListExporter(BaseExporter): return self._render_csv(form_data, dialect='excel') elif form_data.get('_format') == 'semicolon': return self._render_csv(form_data, dialect='excel', delimiter=';') + + +class MultiSheetListExporter(ListExporter): + + @property + def sheets(self): + raise NotImplementedError() + + @property + def export_form_fields(self) -> dict: + choices = [ + ('xlsx', _('Excel (.xlsx)')), + ] + for s, l in self.sheets: + choices += [ + (s + ':default', str(l) + ' – ' + ugettext('CSV (with commas)')), + (s + ':excel', str(l) + ' – ' + ugettext('CSV (Excel-style)')), + (s + ':semicolon', str(l) + ' – ' + ugettext('CSV (with semicolons)')), + ] + ff = OrderedDict( + [ + ('_format', + forms.ChoiceField( + label=_('Export format'), + choices=choices, + )), + ] + ) + ff.update(self.additional_form_fields) + return ff + + def iterate_list(self, form_data): + pass + + def iterate_sheet(self, form_data, sheet): + raise NotImplementedError() # noqa + + def _render_sheet_csv(self, form_data, sheet, **kwargs): + output = io.StringIO() + writer = csv.writer(output, **kwargs) + for line in self.iterate_sheet(form_data, sheet): + writer.writerow(line) + return self.get_filename() + '.csv', 'text/csv', output.getvalue().encode("utf-8") + + def _render_xlsx(self, form_data): + wb = Workbook() + ws = wb.get_active_sheet() + wb.remove(ws) + for s, l in self.sheets: + ws = wb.create_sheet(str(l)) + for i, line in enumerate(self.iterate_sheet(form_data, sheet=s)): + for j, val in enumerate(line): + ws.cell(row=i + 1, column=j + 1).value = str(val) if not isinstance(val, KNOWN_TYPES) else val + + with tempfile.NamedTemporaryFile(suffix='.xlsx') as f: + wb.save(f.name) + f.seek(0) + return self.get_filename() + '.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', f.read() + + def render(self, form_data: dict) -> Tuple[str, str, bytes]: + if form_data.get('_format') == 'xlsx': + return self._render_xlsx(form_data) + elif ':' in form_data.get('_format'): + sheet, f = form_data.get('_format').split(':') + if f == 'default': + return self._render_sheet_csv(form_data, sheet, quoting=csv.QUOTE_NONNUMERIC, delimiter=',') + elif f == 'csv-excel': + return self._render_sheet_csv(form_data, sheet, dialect='excel') + elif f == 'semicolon': + return self._render_sheet_csv(form_data, sheet, dialect='excel', delimiter=';')