Fix #878 -- Add multi-event widget

This commit is contained in:
Raphael Michel
2019-03-19 12:16:09 +01:00
parent ca7d55082b
commit 49e706a580
13 changed files with 1096 additions and 168 deletions

View File

@@ -1,8 +1,12 @@
import calendar
import hashlib
import json
import logging
from collections import defaultdict
from datetime import date, datetime, timedelta
from urllib.parse import urljoin
import pytz
from django.conf import settings
from django.contrib.staticfiles import finders
from django.core.files.base import ContentFile
@@ -24,16 +28,21 @@ from django.views.i18n import (
from lxml import etree
from pretix.base.i18n import language
from pretix.base.models import CartPosition, Voucher
from pretix.base.models import CartPosition, Event, Quota, SubEvent, Voucher
from pretix.base.services.cart import error_messages
from pretix.base.settings import GlobalSettingsObject
from pretix.base.templatetags.rich_text import rich_text
from pretix.helpers.daterange import daterange
from pretix.helpers.thumb import get_thumbnail
from pretix.multidomain.urlreverse import build_absolute_uri
from pretix.presale.views.cart import get_or_create_cart_id
from pretix.presale.views.event import (
get_grouped_items, item_group_by_category,
)
from pretix.presale.views.organizer import (
EventListMixin, add_events_for_days, add_subevents_for_days,
filter_qs_by_attr, weeks_for_template,
)
logger = logging.getLogger(__name__)
@@ -43,7 +52,8 @@ def indent(s):
def widget_css_etag(request, **kwargs):
return request.event.settings.presale_widget_css_checksum or request.organizer.settings.presale_widget_css_checksum
o = getattr(request, 'event', request.organizer)
return o.settings.presale_widget_css_checksum or o.settings.presale_widget_css_checksum
def widget_js_etag(request, lang, **kwargs):
@@ -54,8 +64,9 @@ def widget_js_etag(request, lang, **kwargs):
@condition(etag_func=widget_css_etag)
@cache_page(60)
def widget_css(request, **kwargs):
if request.event.settings.presale_widget_css_file:
resp = FileResponse(default_storage.open(request.event.settings.presale_widget_css_file),
o = getattr(request, 'event', request.organizer)
if o.settings.presale_widget_css_file:
resp = FileResponse(default_storage.open(o.settings.presale_widget_css_file),
content_type='text/css')
return resp
else:
@@ -151,7 +162,7 @@ def get_picture(event, picture):
return urljoin(build_absolute_uri(event, 'presale:event.index'), get_thumbnail(picture.name, '60x60^').thumb.url)
class WidgetAPIProductList(View):
class WidgetAPIProductList(EventListMixin, View):
def _get_items(self):
items, display_add_to_cart = get_grouped_items(
@@ -201,33 +212,179 @@ class WidgetAPIProductList(View):
})
return grps, display_add_to_cart, len(items)
def dispatch(self, request, *args, **kwargs):
def response(self, data):
resp = JsonResponse(data)
resp['Access-Control-Allow-Origin'] = '*'
return resp
def get(self, request, *args, **kwargs):
if not hasattr(request, 'event'):
return self._get_event_list(request, **kwargs)
if not request.event.live:
resp = JsonResponse({
return self.response({
'error': ugettext('This ticket shop is currently disabled.')
})
resp['Access-Control-Allow-Origin'] = '*'
return resp
self.subevent = None
if request.event.has_subevents:
if 'subevent' in kwargs:
self.subevent = request.event.subevents.filter(pk=kwargs['subevent'], active=True).first()
if not self.subevent:
raise Http404()
return self.response({
'error': ugettext('The selected date does not exist in this event series.')
})
else:
raise Http404()
return self._get_event_list(request, **kwargs)
else:
if 'subevent' in kwargs:
raise Http404()
return self.response({
'error': ugettext('This is not an event series.')
})
return self._get_event_view(request, **kwargs)
def dispatch(self, request, *args, **kwargs):
if 'lang' in request.GET and request.GET.get('lang') in [lc for lc, ll in settings.LANGUAGES]:
with language(request.GET.get('lang')):
return super().dispatch(request, *args, **kwargs)
return self.get(request, **kwargs)
else:
return super().dispatch(request, *args, **kwargs)
return self.get(request, **kwargs)
def get(self, request, **kwargs):
def _get_availability(self, ev, event):
availability = {}
if ev.presale_is_running and event.settings.event_list_availability and ev.best_availability_state is not None:
if ev.best_availability_state == Quota.AVAILABILITY_OK:
availability['color'] = 'green'
availability['text'] = ugettext('Tickets on sale')
elif event.settings.waiting_list_enabled and ev.best_availability_state >= 0:
availability['color'] = 'orange'
availability['text'] = ugettext('Waiting list')
elif ev.best_availability_state == Quota.AVAILABILITY_RESERVED:
availability['color'] = 'orange'
availability['text'] = ugettext('Reserved')
elif ev.best_availability_state < Quota.AVAILABILITY_RESERVED:
availability['color'] = 'red'
availability['text'] = ugettext('Sold out')
elif ev.presale_is_running:
availability['color'] = 'green'
availability['text'] = ugettext('Tickets on sale')
elif ev.presale_has_ended:
availability['color'] = 'red'
availability['text'] = ugettext('Sale over')
elif event.settings.presale_start_show_date and ev.presale_start:
availability['color'] = 'orange'
availability['text'] = ugettext('from %(start_date)s') % date_format(ev.presale_start, "SHORT_DATE_FORMAT")
else:
availability['color'] = 'orange'
availability['text'] = ugettext('Sale Soon')
return availability
def _serialize_events(self, ebd):
events = []
for e in ebd:
ev = e['event']
if isinstance(ev, SubEvent):
event = ev.event
else:
event = ev
events.append({
'name': str(ev.name),
'time': date_format(e['time'], 'TIME_FORMAT') if e.get('time') and event.settings.show_times else None,
'continued': e['continued'],
'date_range': ev.get_date_range_display() + (
" " + date_format(ev.date_from, "TIME_FORMAT") if event.settings.show_times else ""
),
'availability': self._get_availability(ev, event),
'event_url': build_absolute_uri(event, 'presale:event.index'),
'subevent': ev.pk if isinstance(ev, SubEvent) else None,
})
return events
def _get_event_list(self, request, **kwargs):
data = {}
o = getattr(request, 'event', request.organizer)
list_type = self.request.GET.get("style", o.settings.event_list_type)
data['list_type'] = list_type
if list_type == "calendar":
self._set_month_year()
_, ndays = calendar.monthrange(self.year, self.month)
data['date'] = date(self.year, self.month, 1)
if hasattr(self.request, 'event'):
tz = pytz.timezone(self.request.event.settings.timezone)
else:
tz = pytz.UTC
before = datetime(self.year, self.month, 1, 0, 0, 0, tzinfo=tz) - timedelta(days=1)
after = datetime(self.year, self.month, ndays, 0, 0, 0, tzinfo=tz) + timedelta(days=1)
ebd = defaultdict(list)
if hasattr(self.request, 'event'):
add_subevents_for_days(
self.request.event.subevents_annotated('web'),
before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
else:
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web'), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), before, after, ebd, timezones)
data['weeks'] = weeks_for_template(ebd, self.year, self.month)
for w in data['weeks']:
for d in w:
if not d:
continue
d['events'] = self._serialize_events(d['events'] or [])
else:
if hasattr(self.request, 'event'):
evs = self.request.event.subevents_sorted(
self.request.event.subevents_annotated(self.request.sales_channel)
)
data['events'] = [
{
'name': str(ev.name),
'date_range': ev.get_date_range_display() + (
" " + date_format(ev.date_from, "TIME_FORMAT") if ev.event.settings.show_times else ""
),
'availability': self._get_availability(ev, ev.event),
'event_url': build_absolute_uri(ev.event, 'presale:event.index'),
'subevent': ev.pk,
} for ev in evs
]
else:
data['events'] = []
qs = self._get_event_queryset()
for event in qs:
tz = pytz.timezone(event.cache.get_or_set('timezone', lambda: event.settings.timezone))
if event.has_subevents:
dr = daterange(
event.min_from.astimezone(tz),
(event.max_fromto or event.max_to or event.max_from).astimezone(tz)
)
avail = {'color': 'none', 'text': ugettext('Event series')}
else:
dr = event.get_date_range_display() + (
" " + date_format(event.date_from, "TIME_FORMAT") if event.settings.show_times else ""
)
avail = self._get_availability(event, event)
data['events'].append({
'name': str(event.name),
'date_range': dr,
'availability': avail,
'event_url': build_absolute_uri(event, 'presale:event.index'),
})
return self.response(data)
def _get_event_view(self, request, **kwargs):
data = {
'currency': request.event.currency,
'display_net_prices': request.event.settings.display_net_prices,
@@ -241,6 +398,7 @@ class WidgetAPIProductList(View):
data['cart_exists'] = True
ev = self.subevent or request.event
data['name'] = str(ev.name)
fail = False
if not ev.presale_is_running:
@@ -298,6 +456,4 @@ class WidgetAPIProductList(View):
self.request.event.get_cache().set('vouchers_exist', vouchers_exist)
data['vouchers_exist'] = vouchers_exist
resp = JsonResponse(data)
resp['Access-Control-Allow-Origin'] = '*'
return resp
return self.response(data)