diff --git a/src/pretix/base/exporter.py b/src/pretix/base/exporter.py index 49439007d4..2424adfff2 100644 --- a/src/pretix/base/exporter.py +++ b/src/pretix/base/exporter.py @@ -157,6 +157,21 @@ class JSONExporter(BaseExporter): return 'pretixdata.json', 'application/json', json.dumps(jo, cls=DjangoJSONEncoder) +class MailExporter(BaseExporter): + identifier = 'mailaddrs' + verbose_name = 'Email addresses (text file)' + + def render(self, form_data: dict): + addrs = self.event.orders.values('email') + data = "\r\n".join(set(a['email'] for a in addrs)) + return 'pretixemails.txt', 'text/plain', data.encode("utf-8") + + @receiver(register_data_exporters, dispatch_uid="exporter_json") def register_json_export(sender, **kwargs): return JSONExporter + + +@receiver(register_data_exporters, dispatch_uid="exporter_mail") +def register_mail_export(sender, **kwargs): + return MailExporter diff --git a/src/pretix/plugins/checkinlists/__init__.py b/src/pretix/plugins/checkinlists/__init__.py new file mode 100644 index 0000000000..d199fa1377 --- /dev/null +++ b/src/pretix/plugins/checkinlists/__init__.py @@ -0,0 +1,32 @@ +from django.apps import AppConfig +from django.utils.functional import cached_property +from django.utils.translation import ugettext_lazy as _ + +from pretix import __version__ as version +from pretix.base.plugins import PluginType + + +class CheckinlistsApp(AppConfig): + name = 'pretix.plugins.checkinlists' + verbose_name = _("Check-in lists") + + class PretixPluginMeta: + type = PluginType.PAYMENT + name = _("Check-in list exporter") + author = _("the pretix team") + version = version + description = _("This plugin allows you to generate check-in lists for your conference.") + + def ready(self): + from . import signals # NOQA + + @cached_property + def compatibility_errors(self): + errs = [] + try: + import reportlab # NOQA + except ImportError: + errs.append("Python package 'reportlab' is not installed.") + return errs + +default_app_config = 'pretix.plugins.checkinlists.CheckinlistsApp' diff --git a/src/pretix/plugins/checkinlists/exporters.py b/src/pretix/plugins/checkinlists/exporters.py new file mode 100644 index 0000000000..7c9bd9840a --- /dev/null +++ b/src/pretix/plugins/checkinlists/exporters.py @@ -0,0 +1,116 @@ +import csv +import io +from collections import OrderedDict + +from django import forms +from django.utils.translation import ugettext as _ + +from pretix.base.exporter import BaseExporter +from pretix.base.models import Order, OrderPosition + + +class BaseCheckinList(BaseExporter): + pass + + +class CSVCheckinList(BaseCheckinList): + name = "overview" + identifier = 'checkinlistcsv' + verbose_name = _('Check-in list (CSV)') + + @property + def export_form_fields(self): + return OrderedDict( + [ + ('items', + forms.ModelMultipleChoiceField( + queryset=self.event.items.all(), + label=_('Limit to products'), + widget=forms.CheckboxSelectMultiple, + initial=self.event.items.filter(admission=True) + )), + ('secrets', + forms.BooleanField( + label=_('Include QR-code secret'), + required=False + )), + ('paid_only', + forms.BooleanField( + label=_('Only paid orders'), + initial=True, + required=False + )), + ('sort', + forms.ChoiceField( + label=_('Sort by'), + initial='name', + choices=( + ('name', _('Attendee name')), + ('code', _('Order code')), + ), + widget=forms.RadioSelect, + required=False + )), + ('questions', + forms.ModelMultipleChoiceField( + queryset=self.event.questions.all(), + label=_('Include questions'), + widget=forms.CheckboxSelectMultiple, + required=False + )), + ] + ) + + def render(self, form_data: dict): + output = io.StringIO() + writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC, delimiter=",") + + questions = list(form_data['questions']) + qs = OrderPosition.objects.filter( + order__event=self.event, item__in=form_data['items'] + ).prefetch_related( + 'answers', 'answers__question' + ).select_related('order', 'item', 'variation') + + if form_data['sort'] == 'name': + qs = qs.order_by('attendee_name') + elif form_data['sort'] == 'code': + qs = qs.order_by('order__code') + + headers = [ + _('Order code'), _('Attendee name'), _('Product'), _('Price') + ] + if form_data['paid_only']: + qs = qs.filter(order__status=Order.STATUS_PAID) + else: + qs = qs.filter(order__status__in=(Order.STATUS_PAID, Order.STATUS_PENDING)) + headers.append(_('Paid')) + + if form_data['secrets']: + headers.append(_('Secret')) + + for q in questions: + headers.append(str(q.question)) + + writer.writerow(headers) + + for op in qs: + row = [ + op.order.code, + op.attendee_name, + str(op.item.name) + (" – " + str(op.variation.value) if op.variation else ""), + op.price, + ] + if not form_data['paid_only']: + row.append(_('Yes') if op.order.status == Order.STATUS_PAID else _('No')) + if form_data['secrets']: + row.append(op.secret) + acache = {} + for a in op.answers.all(): + acache[a.question_id] = str(a) + for q in questions: + row.append(acache.get(q.pk, '')) + + writer.writerow(row) + + return 'checkin.csv', 'text/csv', output.getvalue().encode("utf-8") diff --git a/src/pretix/plugins/checkinlists/signals.py b/src/pretix/plugins/checkinlists/signals.py new file mode 100644 index 0000000000..c632e6ad93 --- /dev/null +++ b/src/pretix/plugins/checkinlists/signals.py @@ -0,0 +1,9 @@ +from django.dispatch import receiver + +from pretix.base.signals import register_data_exporters + + +@receiver(register_data_exporters, dispatch_uid="export_checkinlist_csv") +def register_csv(sender, **kwargs): + from .exporters import CSVCheckinList + return CSVCheckinList diff --git a/src/pretix/settings.py b/src/pretix/settings.py index 2235469fbb..aa7b2679d7 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -160,6 +160,7 @@ INSTALLED_APPS = [ 'pretix.plugins.sendmail', 'pretix.plugins.statistics', 'pretix.plugins.reports', + 'pretix.plugins.checkinlists', 'pretix.plugins.pretixdroid', 'easy_thumbnails' ]