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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from ..decimal import round_decimal
|
from ..decimal import round_decimal
|
||||||
from .base import CachedFile, LoggedModel
|
from .base import LoggedModel
|
||||||
from .event import Event
|
from .event import Event
|
||||||
from .items import Item, ItemVariation, Question, QuestionOption, Quota
|
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'))
|
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):
|
class CachedTicket(models.Model):
|
||||||
order_position = models.ForeignKey(OrderPosition, on_delete=models.CASCADE)
|
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)
|
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)
|
@receiver(post_delete, sender=CachedTicket)
|
||||||
def cached_file_delete(sender, instance, **kwargs):
|
def cachedticket_delete(sender, instance, **kwargs):
|
||||||
try:
|
if instance.file:
|
||||||
if instance.cachedfile:
|
# Pass false so FileField doesn't save the model.
|
||||||
instance.cachedfile.delete()
|
instance.file.delete(False)
|
||||||
except CachedFile.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
from datetime import timedelta
|
import os
|
||||||
|
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.base.models import (
|
from pretix.base.models import CachedTicket, Event, Order, OrderPosition
|
||||||
CachedFile, CachedTicket, Event, Order, OrderPosition, cachedfile_name,
|
|
||||||
)
|
|
||||||
from pretix.base.services.async import ProfiledTask
|
from pretix.base.services.async import ProfiledTask
|
||||||
from pretix.base.signals import register_ticket_outputs
|
from pretix.base.signals import register_ticket_outputs
|
||||||
from pretix.celery import app
|
from pretix.celery import app
|
||||||
@@ -17,23 +15,21 @@ from pretix.helpers.database import rolledback_transaction
|
|||||||
@app.task(base=ProfiledTask)
|
@app.task(base=ProfiledTask)
|
||||||
def generate(order_position: str, provider: str):
|
def generate(order_position: str, provider: str):
|
||||||
order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position)
|
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]
|
try:
|
||||||
if not ct.cachedfile:
|
ct = CachedTicket.objects.get(order_position=order_position, provider=provider)
|
||||||
cf = CachedFile()
|
except CachedTicket.DoesNotExist:
|
||||||
cf.date = now()
|
ct = CachedTicket(order_position=order_position, provider=provider)
|
||||||
cf.expires = order_position.order.event.date_from + timedelta(days=30)
|
|
||||||
cf.save()
|
|
||||||
ct.cachedfile = cf
|
|
||||||
ct.save()
|
|
||||||
|
|
||||||
with language(order_position.order.locale):
|
with language(order_position.order.locale):
|
||||||
responses = register_ticket_outputs.send(order_position.order.event)
|
responses = register_ticket_outputs.send(order_position.order.event)
|
||||||
for receiver, response in responses:
|
for receiver, response in responses:
|
||||||
prov = response(order_position.order.event)
|
prov = response(order_position.order.event)
|
||||||
if prov.identifier == provider:
|
if prov.identifier == provider:
|
||||||
ct.cachedfile.filename, ct.cachedfile.type, data = prov.generate(order_position)
|
filename, ct.type, data = prov.generate(order_position)
|
||||||
ct.cachedfile.file.save(cachedfile_name(ct.cachedfile, ct.cachedfile.filename), ContentFile(data))
|
path, ext = os.path.splitext(filename)
|
||||||
ct.cachedfile.save()
|
ct.extension = ext
|
||||||
|
ct.save()
|
||||||
|
ct.file.save(filename, ContentFile(data))
|
||||||
|
|
||||||
|
|
||||||
class DummyRollbackException(Exception):
|
class DummyRollbackException(Exception):
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ class BaseTicketOutput:
|
|||||||
def generate(self, order: OrderPosition) -> Tuple[str, str, str]:
|
def generate(self, order: OrderPosition) -> Tuple[str, str, str]:
|
||||||
"""
|
"""
|
||||||
This method should generate the download file and return a tuple consisting of a
|
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()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from django.http import FileResponse, Http404, HttpRequest, HttpResponse
|
from django.http import FileResponse, Http404, HttpRequest, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
@@ -23,8 +21,7 @@ class DownloadView(TemplateView):
|
|||||||
return HttpResponse('1' if self.object.file else '0')
|
return HttpResponse('1' if self.object.file else '0')
|
||||||
elif self.object.file:
|
elif self.object.file:
|
||||||
resp = FileResponse(self.object.file.file, content_type=self.object.type)
|
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.filename)
|
||||||
resp['Content-Disposition'] = 'attachment; filename="{}{}"'.format(self.object.id, ext)
|
|
||||||
return resp
|
return resp
|
||||||
else:
|
else:
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|||||||
@@ -159,6 +159,7 @@
|
|||||||
{% for line in items.positions %}
|
{% for line in items.positions %}
|
||||||
<div class="row-fluid product-row">
|
<div class="row-fluid product-row">
|
||||||
<div class="col-md-4 col-xs-6">
|
<div class="col-md-4 col-xs-6">
|
||||||
|
{{ line.positionid }}
|
||||||
<strong>{{ line.item.name }}</strong>
|
<strong>{{ line.item.name }}</strong>
|
||||||
{% if line.variation %}
|
{% if line.variation %}
|
||||||
– {{ line.variation }}
|
– {{ line.variation }}
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.http import FileResponse, Http404
|
from django.http import FileResponse, Http404, HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext, ugettext_lazy as _
|
from django.utils.translation import gettext, ugettext_lazy as _
|
||||||
from django.views.generic import TemplateView, View
|
from django.views.generic import TemplateView, View
|
||||||
|
|
||||||
from pretix.base.models import (
|
from pretix.base.models import CachedTicket, Invoice, Order, OrderPosition
|
||||||
CachedFile, CachedTicket, Invoice, Order, OrderPosition,
|
|
||||||
)
|
|
||||||
from pretix.base.models.orders import InvoiceAddress
|
from pretix.base.models.orders import InvoiceAddress
|
||||||
from pretix.base.services.invoices import (
|
from pretix.base.services.invoices import (
|
||||||
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
|
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.'))
|
messages.error(request, _('Ticket download is not (yet) enabled.'))
|
||||||
return redirect(self.get_order_url())
|
return redirect(self.get_order_url())
|
||||||
|
|
||||||
ct = CachedTicket.objects.get_or_create(
|
try:
|
||||||
order_position=self.order_position, provider=self.output.identifier
|
ct = CachedTicket.objects.get(
|
||||||
)[0]
|
order_position=self.order_position, provider=self.output.identifier
|
||||||
if not ct.cachedfile:
|
)
|
||||||
cf = CachedFile()
|
except CachedTicket.DoesNotExist:
|
||||||
cf.date = now()
|
ct = None
|
||||||
cf.expires = self.request.event.date_from + timedelta(days=30)
|
|
||||||
cf.save()
|
if 'ajax' in request.GET:
|
||||||
ct.cachedfile = cf
|
return HttpResponse('1' if ct and ct.file else '0')
|
||||||
ct.save()
|
elif not ct or not ct.file:
|
||||||
generate.apply_async(args=(self.order_position.id, self.output.identifier))
|
generate.apply_async(args=(self.order_position.id, self.output.identifier))
|
||||||
return redirect(reverse('cachedfile.download', kwargs={'id': ct.cachedfile.id}))
|
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):
|
class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
|
||||||
|
|||||||
Reference in New Issue
Block a user