forked from CGM_Public/pretix_original
Fix #356 -- Download all tickets from an order
This commit is contained in:
@@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.validators
|
||||
|
||||
|
||||
|
||||
29
src/pretix/base/migrations/0059_cachedcombinedticket.py
Normal file
29
src/pretix/base/migrations/0059_cachedcombinedticket.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-13 14:07
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.orders
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0058_auto_20170107_1533'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CachedCombinedTicket',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('provider', models.CharField(max_length=255)),
|
||||
('type', models.CharField(max_length=255)),
|
||||
('extension', models.CharField(max_length=255)),
|
||||
('file', models.FileField(blank=True, null=True, upload_to=pretix.base.models.orders.cachedcombinedticket_name)),
|
||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Order')),
|
||||
],
|
||||
),
|
||||
]
|
||||
28
src/pretix/base/migrations/0060_auto_20170113_1438.py
Normal file
28
src/pretix/base/migrations/0060_auto_20170113_1438.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-13 14:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0059_cachedcombinedticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='cachedcombinedticket',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cachedticket',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -12,8 +12,10 @@ from .items import (
|
||||
)
|
||||
from .log import LogEntry
|
||||
from .orders import (
|
||||
AbstractPosition, CachedTicket, CartPosition, InvoiceAddress, Order,
|
||||
OrderPosition, QuestionAnswer, generate_position_secret, generate_secret,
|
||||
AbstractPosition, CachedCombinedTicket, CachedTicket, CartPosition,
|
||||
InvoiceAddress, Order, OrderPosition, QuestionAnswer,
|
||||
cachedcombinedticket_name, cachedticket_name, generate_position_secret,
|
||||
generate_secret,
|
||||
)
|
||||
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
||||
from .vouchers import Voucher
|
||||
|
||||
@@ -575,12 +575,33 @@ def cachedticket_name(instance, filename: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def cachedcombinedticket_name(instance, filename: str) -> str:
|
||||
secret = get_random_string(length=16, allowed_chars=string.ascii_letters + string.digits)
|
||||
return 'tickets/{org}/{ev}/{code}-{prov}-{secret}.pdf'.format(
|
||||
org=instance.order.event.organizer.slug,
|
||||
ev=instance.order.event.slug,
|
||||
prov=instance.provider,
|
||||
code=instance.order.code,
|
||||
secret=secret
|
||||
)
|
||||
|
||||
|
||||
class CachedTicket(models.Model):
|
||||
order_position = models.ForeignKey(OrderPosition, on_delete=models.CASCADE)
|
||||
provider = models.CharField(max_length=255)
|
||||
type = models.CharField(max_length=255)
|
||||
extension = models.CharField(max_length=255)
|
||||
file = models.FileField(null=True, blank=True, upload_to=cachedticket_name)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class CachedCombinedTicket(models.Model):
|
||||
order = models.ForeignKey(Order, on_delete=models.CASCADE)
|
||||
provider = models.CharField(max_length=255)
|
||||
type = models.CharField(max_length=255)
|
||||
extension = models.CharField(max_length=255)
|
||||
file = models.FileField(null=True, blank=True, upload_to=cachedcombinedticket_name)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=CachedTicket)
|
||||
|
||||
@@ -5,7 +5,9 @@ from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import CachedTicket, Event, Order, OrderPosition
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Event, Order, OrderPosition,
|
||||
)
|
||||
from pretix.base.services.async import ProfiledTask
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from pretix.celery_app import app
|
||||
@@ -37,6 +39,31 @@ def generate(order_position: str, provider: str):
|
||||
ct.file.save(filename, ContentFile(data))
|
||||
|
||||
|
||||
@app.task(base=ProfiledTask)
|
||||
def generate_order(order: int, provider: str):
|
||||
order = Order.objects.select_related('event').get(id=order)
|
||||
try:
|
||||
ct = CachedCombinedTicket.objects.get(order=order, provider=provider)
|
||||
except CachedCombinedTicket.MultipleObjectsReturned:
|
||||
CachedCombinedTicket.objects.filter(order=order, provider=provider).delete()
|
||||
ct = CachedCombinedTicket.objects.create(order=order, provider=provider, extension='',
|
||||
type='', file=None)
|
||||
except CachedCombinedTicket.DoesNotExist:
|
||||
ct = CachedCombinedTicket.objects.create(order=order, provider=provider, extension='',
|
||||
type='', file=None)
|
||||
|
||||
with language(order.locale):
|
||||
responses = register_ticket_outputs.send(order.event)
|
||||
for receiver, response in responses:
|
||||
prov = response(order.event)
|
||||
if prov.identifier == provider:
|
||||
filename, ct.type, data = prov.generate_order(order)
|
||||
path, ext = os.path.splitext(filename)
|
||||
ct.extension = ext
|
||||
ct.save()
|
||||
ct.file.save(filename, ContentFile(data))
|
||||
|
||||
|
||||
class DummyRollbackException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import os
|
||||
import tempfile
|
||||
from collections import OrderedDict
|
||||
from typing import Tuple
|
||||
from zipfile import ZipFile
|
||||
|
||||
from django import forms
|
||||
from django.http import HttpRequest
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.models import Event, OrderPosition
|
||||
from pretix.base.models import Event, Order, OrderPosition
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
|
||||
|
||||
@@ -29,7 +32,7 @@ class BaseTicketOutput:
|
||||
"""
|
||||
return self.settings.get('_enabled', as_type=bool)
|
||||
|
||||
def generate(self, order: OrderPosition) -> Tuple[str, str, str]:
|
||||
def generate(self, position: OrderPosition) -> Tuple[str, str, str]:
|
||||
"""
|
||||
This method should generate the download file and return a tuple consisting of a
|
||||
filename, a file type and file content. The extension will be taken from the filename
|
||||
@@ -37,6 +40,29 @@ class BaseTicketOutput:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def generate_order(self, order: Order) -> Tuple[str, str, str]:
|
||||
"""
|
||||
This method is the same as order() but should not generate one file per order position
|
||||
but instead one file for the full order.
|
||||
|
||||
This method is optional to implement. If you don't implement it, the default
|
||||
implementation will offer a zip file of the generate() results for the order positions.
|
||||
|
||||
This method should generate a download file and return a tuple consisting of a
|
||||
filename, a file type and file content. The extension will be taken from the filename
|
||||
which is otherwise ignored.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
with ZipFile(os.path.join(d, 'tmp.zip'), 'w') as zipf:
|
||||
for pos in order.positions.all():
|
||||
fname, __, content = self.generate(pos)
|
||||
zipf.writestr('{}-{}{}'.format(
|
||||
order.code, pos.positionid, os.path.splitext(fname)[1]
|
||||
), content)
|
||||
|
||||
with open(os.path.join(d, 'tmp.zip'), 'rb') as zipf:
|
||||
return '{}-{}.zip'.format(order.code, self.identifier), 'application/zip', zipf.read()
|
||||
|
||||
@property
|
||||
def verbose_name(self) -> str:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user