forked from CGM_Public/pretix_original
Decouple CachedTicket from CachedFile
This commit is contained in:
54
src/pretix/base/migrations/0051_auto_20161221_1720.py
Normal file
54
src/pretix/base/migrations/0051_auto_20161221_1720.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2016-12-21 17:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.orders
|
||||
|
||||
|
||||
def invalidate_ticket_cache(apps, schema_editor):
|
||||
CachedTicket = apps.get_model('pretixbase', 'CachedTicket')
|
||||
for ct in CachedTicket.objects.all():
|
||||
try:
|
||||
if ct.cachedfile:
|
||||
ct.cachedfile.delete()
|
||||
if ct.cachedfile.file:
|
||||
ct.cachedfile.file.delete(False)
|
||||
except models.Model.DoesNotExist:
|
||||
pass
|
||||
ct.delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0050_orderposition_positionid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
invalidate_ticket_cache, migrations.RunPython.noop
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='cachedticket',
|
||||
name='cachedfile',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cachedticket',
|
||||
name='extension',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cachedticket',
|
||||
name='file',
|
||||
field=models.FileField(blank=True, null=True, upload_to=pretix.base.models.orders.cachedticket_name),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cachedticket',
|
||||
name='type',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -15,7 +15,7 @@ from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from ..decimal import round_decimal
|
||||
from .base import CachedFile, LoggedModel
|
||||
from .base import LoggedModel
|
||||
from .event import Event
|
||||
from .items import Item, ItemVariation, Question, QuestionOption, Quota
|
||||
|
||||
@@ -565,16 +565,28 @@ class InvoiceAddress(models.Model):
|
||||
vat_id = models.CharField(max_length=255, blank=True, verbose_name=_('VAT ID'))
|
||||
|
||||
|
||||
def cachedticket_name(instance, filename: str) -> str:
|
||||
secret = get_random_string(length=16, allowed_chars=string.ascii_letters + string.digits)
|
||||
return 'tickets/{org}/{ev}/{code}-{no}-{prov}-{secret}.pdf'.format(
|
||||
org=instance.order_position.order.event.organizer.slug,
|
||||
ev=instance.order_position.order.event.slug,
|
||||
prov=instance.provider,
|
||||
no=instance.order_position.positionid,
|
||||
code=instance.order_position.order.code,
|
||||
secret=secret
|
||||
)
|
||||
|
||||
|
||||
class CachedTicket(models.Model):
|
||||
order_position = models.ForeignKey(OrderPosition, on_delete=models.CASCADE)
|
||||
cachedfile = models.ForeignKey(CachedFile, on_delete=models.CASCADE, null=True)
|
||||
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)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=CachedTicket)
|
||||
def cached_file_delete(sender, instance, **kwargs):
|
||||
try:
|
||||
if instance.cachedfile:
|
||||
instance.cachedfile.delete()
|
||||
except CachedFile.DoesNotExist:
|
||||
pass
|
||||
def cachedticket_delete(sender, instance, **kwargs):
|
||||
if instance.file:
|
||||
# Pass false so FileField doesn't save the model.
|
||||
instance.file.delete(False)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
from datetime import timedelta
|
||||
import os
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedFile, CachedTicket, Event, Order, OrderPosition, cachedfile_name,
|
||||
)
|
||||
from pretix.base.models import CachedTicket, Event, Order, OrderPosition
|
||||
from pretix.base.services.async import ProfiledTask
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from pretix.celery import app
|
||||
@@ -17,23 +15,21 @@ from pretix.helpers.database import rolledback_transaction
|
||||
@app.task(base=ProfiledTask)
|
||||
def generate(order_position: str, provider: str):
|
||||
order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position)
|
||||
ct = CachedTicket.objects.get_or_create(order_position=order_position, provider=provider)[0]
|
||||
if not ct.cachedfile:
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.expires = order_position.order.event.date_from + timedelta(days=30)
|
||||
cf.save()
|
||||
ct.cachedfile = cf
|
||||
ct.save()
|
||||
try:
|
||||
ct = CachedTicket.objects.get(order_position=order_position, provider=provider)
|
||||
except CachedTicket.DoesNotExist:
|
||||
ct = CachedTicket(order_position=order_position, provider=provider)
|
||||
|
||||
with language(order_position.order.locale):
|
||||
responses = register_ticket_outputs.send(order_position.order.event)
|
||||
for receiver, response in responses:
|
||||
prov = response(order_position.order.event)
|
||||
if prov.identifier == provider:
|
||||
ct.cachedfile.filename, ct.cachedfile.type, data = prov.generate(order_position)
|
||||
ct.cachedfile.file.save(cachedfile_name(ct.cachedfile, ct.cachedfile.filename), ContentFile(data))
|
||||
ct.cachedfile.save()
|
||||
filename, ct.type, data = prov.generate(order_position)
|
||||
path, ext = os.path.splitext(filename)
|
||||
ct.extension = ext
|
||||
ct.save()
|
||||
ct.file.save(filename, ContentFile(data))
|
||||
|
||||
|
||||
class DummyRollbackException(Exception):
|
||||
|
||||
@@ -32,7 +32,8 @@ class BaseTicketOutput:
|
||||
def generate(self, order: 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.
|
||||
filename, a file type and file content. The extension will be taken from the filename
|
||||
which is otherwise ignored.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import os
|
||||
|
||||
from django.http import FileResponse, Http404, HttpRequest, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
@@ -23,8 +21,7 @@ class DownloadView(TemplateView):
|
||||
return HttpResponse('1' if self.object.file else '0')
|
||||
elif self.object.file:
|
||||
resp = FileResponse(self.object.file.file, content_type=self.object.type)
|
||||
_, ext = os.path.splitext(self.object.filename)
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}{}"'.format(self.object.id, ext)
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}"'.format(self.object.filename)
|
||||
return resp
|
||||
else:
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
{% for line in items.positions %}
|
||||
<div class="row-fluid product-row">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
{{ line.positionid }}
|
||||
<strong>{{ line.item.name }}</strong>
|
||||
{% if line.variation %}
|
||||
– {{ line.variation }}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.http import FileResponse, Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.http import FileResponse, Http404, HttpResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext, ugettext_lazy as _
|
||||
from django.views.generic import TemplateView, View
|
||||
|
||||
from pretix.base.models import (
|
||||
CachedFile, CachedTicket, Invoice, Order, OrderPosition,
|
||||
)
|
||||
from pretix.base.models import CachedTicket, Invoice, Order, OrderPosition
|
||||
from pretix.base.models.orders import InvoiceAddress
|
||||
from pretix.base.services.invoices import (
|
||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
||||
@@ -513,18 +508,25 @@ class OrderDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
messages.error(request, _('Ticket download is not (yet) enabled.'))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
ct = CachedTicket.objects.get_or_create(
|
||||
order_position=self.order_position, provider=self.output.identifier
|
||||
)[0]
|
||||
if not ct.cachedfile:
|
||||
cf = CachedFile()
|
||||
cf.date = now()
|
||||
cf.expires = self.request.event.date_from + timedelta(days=30)
|
||||
cf.save()
|
||||
ct.cachedfile = cf
|
||||
ct.save()
|
||||
generate.apply_async(args=(self.order_position.id, self.output.identifier))
|
||||
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
||||
try:
|
||||
ct = CachedTicket.objects.get(
|
||||
order_position=self.order_position, provider=self.output.identifier
|
||||
)
|
||||
except CachedTicket.DoesNotExist:
|
||||
ct = None
|
||||
|
||||
if 'ajax' in request.GET:
|
||||
return HttpResponse('1' if ct and ct.file else '0')
|
||||
elif not ct or not ct.file:
|
||||
generate.apply_async(args=(self.order_position.id, self.output.identifier))
|
||||
return render(request, "pretixbase/cachedfiles/pending.html", {})
|
||||
else:
|
||||
resp = FileResponse(ct.file.file, content_type='application/pdf')
|
||||
resp['Content-Disposition'] = 'attachment; filename="{}-{}-{}-{}{}"'.format(
|
||||
self.request.event.slug.upper(), self.order.code, self.order_position.positionid,
|
||||
self.output.identifier, ct.extension
|
||||
)
|
||||
return resp
|
||||
|
||||
|
||||
class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
|
||||
|
||||
Reference in New Issue
Block a user