Event series calendar: Allow to hide all past events (#3142)

This commit is contained in:
Raphael Michel
2023-03-06 18:25:45 +01:00
committed by GitHub
parent 62cbed4891
commit f29aa73f8d
9 changed files with 115 additions and 42 deletions

View File

@@ -693,6 +693,7 @@ class EventSettingsSerializer(SettingsSerializer):
'frontpage_subevent_ordering',
'event_list_type',
'event_list_available_only',
'event_calendar_future_only',
'frontpage_text',
'event_info_text',
'attendee_names_asked',

View File

@@ -1397,6 +1397,19 @@ DEFAULTS = {
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Hide all unavailable dates from calendar or list views"),
help_text=_("This option currently only affects the calendar of this event series, not the organizer-wide "
"calendar.")
)
},
'event_calendar_future_only': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Hide all past dates from calendar"),
help_text=_("This option currently only affects the calendar of this event series, not the organizer-wide "
"calendar.")
)
},
'allow_modifications_after_checkin': {

View File

@@ -511,6 +511,7 @@ class EventSettingsForm(SettingsForm):
'low_availability_percentage',
'event_list_type',
'event_list_available_only',
'event_calendar_future_only',
'frontpage_text',
'event_info_text',
'attendee_names_asked',
@@ -602,6 +603,7 @@ class EventSettingsForm(SettingsForm):
del self.fields['frontpage_subevent_ordering']
del self.fields['event_list_type']
del self.fields['event_list_available_only']
del self.fields['event_calendar_future_only']
# create "virtual" fields for better UX when editing <name>_asked and <name>_required fields
self.virtual_keys = []

View File

@@ -315,6 +315,9 @@
{% if sform.event_list_available_only %}
{% bootstrap_field sform.event_list_available_only layout="control" %}
{% endif %}
{% if sform.event_calendar_future_only %}
{% bootstrap_field sform.event_calendar_future_only layout="control" %}
{% endif %}
{% bootstrap_field sform.low_availability_percentage layout="control" addon_after="%" %}
{% url "control:organizer.edit" organizer=request.organizer.slug as org_url %}

View File

@@ -4,11 +4,13 @@
<nav aria-label="{% trans "calendar navigation" %}">
<ul class="row calendar-nav">
<li class="col-sm-4 col-xs-2 text-left flip">
<a href="?{% url_replace request "date" subevent_list.before|date:"Y-m" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.before|date:"F Y" %}Show previous month, {{ month }}{% endblocktrans %}">
<span class="fa fa-arrow-left" aria-hidden="true"></span>
<span class="hidden-xs">{{ subevent_list.before|date:"F Y" }}</span>
</a>
{% if subevent_list.has_before %}
<a href="?{% url_replace request "date" subevent_list.before|date:"Y-m" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.before|date:"F Y" %}Show previous month, {{ month }}{% endblocktrans %}">
<span class="fa fa-arrow-left" aria-hidden="true"></span>
<span class="hidden-xs">{{ subevent_list.before|date:"F Y" }}</span>
</a>
{% endif %}
</li>
<li class="col-sm-4 col-xs-8 text-center">
<form class="form-inline" method="get" id="monthselform" action="{% eventurl event "presale:event.index" cart_namespace=cart_namespace %}">
@@ -32,11 +34,13 @@
</form>
</li>
<li class="col-sm-4 col-xs-2 text-right flip">
<a href="?{% url_replace request "date" subevent_list.after|date:"Y-m" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.after|date:"F Y" %}Show next month, {{ month }}{% endblocktrans %}">
<span class="hidden-xs">{{ subevent_list.after|date:"F Y" }}</span>
<span class="fa fa-arrow-right" aria-hidden="true"></span>
</a>
{% if subevent_list.has_after %}
<a href="?{% url_replace request "date" subevent_list.after|date:"Y-m" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with month=subevent_list.after|date:"F Y" %}Show next month, {{ month }}{% endblocktrans %}">
<span class="hidden-xs">{{ subevent_list.after|date:"F Y" }}</span>
<span class="fa fa-arrow-right" aria-hidden="true"></span>
</a>
{% endif %}
</li>
</ul>
</nav>

View File

@@ -4,11 +4,13 @@
<nav aria-label="{% trans "calendar navigation" %}">
<ul class="row calendar-nav">
<li class="col-sm-4 col-xs-2 text-left flip">
<a href="?{% url_replace request "date" subevent_list.before|date:"o-\WW" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.before|date:subevent_list.week_format %}Show previous week, {{ week }}{% endblocktrans %}">
<span class="fa fa-arrow-left" aria-hidden="true"></span>
<span class="hidden-xs">{{ subevent_list.before|date:subevent_list.week_format }}</span>
</a>
{% if subevent_list.has_before %}
<a href="?{% url_replace request "date" subevent_list.before|date:"o-\WW" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.before|date:subevent_list.week_format %}Show previous week, {{ week }}{% endblocktrans %}">
<span class="fa fa-arrow-left" aria-hidden="true"></span>
<span class="hidden-xs">{{ subevent_list.before|date:subevent_list.week_format }}</span>
</a>
{% endif %}
</li>
<li class="col-sm-4 col-xs-8 text-center">
<form class="form-inline" method="get" id="monthselform" action="{% eventurl event "presale:event.index" cart_namespace=cart_namespace %}">
@@ -36,11 +38,13 @@
</form>
</li>
<li class="col-sm-4 col-xs-2 text-right flip">
<a href="?{% url_replace request "date" subevent_list.after|date:"o-\WW" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.after|date:subevent_list.week_format %}Show next week, {{ week }}{% endblocktrans %}">
<span class="hidden-xs">{{ subevent_list.after|date:subevent_list.week_format }}</span>
<span class="fa fa-arrow-right" aria-hidden="true"></span>
</a>
{% if subevent_list.has_after %}
<a href="?{% url_replace request "date" subevent_list.after|date:"o-\WW" %}"
class="btn btn-default" data-save-scrollpos aria-label="{% blocktrans with week=subevent_list.after|date:subevent_list.week_format %}Show next week, {{ week }}{% endblocktrans %}">
<span class="hidden-xs">{{ subevent_list.after|date:subevent_list.week_format }}</span>
<span class="fa fa-arrow-right" aria-hidden="true"></span>
</a>
{% endif %}
</li>
</ul>
</nav>

View File

@@ -63,7 +63,7 @@ from pretix.base.channels import get_all_sales_channels
from pretix.base.models import (
ItemVariation, Quota, SeatCategoryMapping, Voucher,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.event import Event, SubEvent
from pretix.base.models.items import (
ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
)
@@ -74,7 +74,7 @@ from pretix.presale.ical import get_public_ical
from pretix.presale.signals import item_description
from pretix.presale.views.organizer import (
EventListMixin, add_subevents_for_days, days_for_template,
filter_qs_by_attr, weeks_for_template,
filter_qs_by_attr, has_before_after, weeks_for_template,
)
from ...helpers.formats.en.formats import SHORT_MONTH_DAY_FORMAT, WEEK_FORMAT
@@ -586,6 +586,11 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
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)
if self.request.event.settings.event_calendar_future_only:
limit_before = now().astimezone(tz)
else:
limit_before = before
context['date'] = date(self.year, self.month, 1)
context['before'] = before
context['after'] = after
@@ -593,7 +598,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
ebd = defaultdict(list)
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
limit_before, after, ebd, set(), self.request.event,
self.kwargs.get('cart_namespace'),
voucher,
)
@@ -606,9 +611,22 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
lambda: len(set(str(n) for n in self.request.event.subevents.order_by().values_list('name', flat=True).annotate(c=Count('*'))[:250])) != 1,
timeout=120,
)
context['weeks'] = weeks_for_template(ebd, self.year, self.month)
context['weeks'] = weeks_for_template(ebd, self.year, self.month, future_only=self.request.event.settings.event_calendar_future_only)
context['months'] = [date(self.year, i + 1, 1) for i in range(12)]
context['years'] = range(now().year - 2, now().year + 3)
if self.request.event.settings.event_calendar_future_only:
context['years'] = range(now().year, now().year + 3)
else:
context['years'] = range(now().year - 2, now().year + 3)
context['has_before'], context['has_after'] = has_before_after(
Event.objects.none(),
SubEvent.objects.filter(
event=self.request.event,
),
before,
after,
future_only=self.request.event.settings.event_calendar_future_only
)
elif context['list_type'] == "week":
self._set_week_year()
tz = self.request.event.timezone
@@ -620,6 +638,11 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, tzinfo=tz
) + timedelta(days=1)
if self.request.event.settings.event_calendar_future_only:
limit_before = now().astimezone(tz)
else:
limit_before = before
context['date'] = week.monday()
context['before'] = before
context['after'] = after
@@ -627,7 +650,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
ebd = defaultdict(list)
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request),
before, after, ebd, set(), self.request.event,
limit_before, after, ebd, set(), self.request.event,
self.kwargs.get('cart_namespace'),
voucher,
)
@@ -640,13 +663,15 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
lambda: len(set(str(n) for n in self.request.event.subevents.order_by().values_list('name', flat=True).annotate(c=Count('*'))[:250])) != 1,
timeout=120,
)
context['days'] = days_for_template(ebd, week)
context['days'] = days_for_template(ebd, week, future_only=self.request.event.settings.event_calendar_future_only)
years = (self.year - 1, self.year, self.year + 1)
weeks = []
for year in years:
weeks += [
(date_fromisocalendar(year, i + 1, 1), date_fromisocalendar(year, i + 1, 7))
for i in range(53 if date(year, 12, 31).isocalendar()[1] == 53 else 52)
if not self.request.event.settings.event_calendar_future_only or
date_fromisocalendar(year, i + 1, 7) > now().astimezone(tz).replace(tzinfo=None)
]
context['weeks'] = [[w for w in weeks if w[0].year == year] for year in years]
context['week_format'] = get_format('WEEK_FORMAT')
@@ -655,6 +680,16 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['short_month_day_format'] = get_format('SHORT_MONTH_DAY_FORMAT')
if context['short_month_day_format'] == 'SHORT_MONTH_DAY_FORMAT':
context['short_month_day_format'] = SHORT_MONTH_DAY_FORMAT
context['has_before'], context['has_after'] = has_before_after(
Event.objects.none(),
SubEvent.objects.filter(
event=self.request.event,
),
before,
after,
future_only=self.request.event.settings.event_calendar_future_only
)
else:
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier).using(settings.DATABASE_REPLICA), self.request)

View File

@@ -401,11 +401,11 @@ class OrganizerIndex(OrganizerViewMixin, EventListMixin, ListView):
return ctx
def has_before_after(eventqs, subeventqs, before, after):
def has_before_after(eventqs, subeventqs, before, after, future_only=False):
eqs = eventqs.filter(is_public=True, live=True, has_subevents=False)
sqs = subeventqs.filter(active=True, is_public=True)
return (
eqs.filter(Q(date_from__lte=before)).exists() or sqs.filter(Q(date_from__lte=before)).exists(),
(not future_only or before > now()) and (eqs.filter(Q(date_from__lte=before)).exists() or sqs.filter(Q(date_from__lte=before)).exists()),
eqs.filter(Q(date_to__gte=after) | Q(date_from__gte=after)).exists() or sqs.filter(Q(date_to__gte=after) | Q(date_from__gte=after)).exists()
)
@@ -413,7 +413,6 @@ def has_before_after(eventqs, subeventqs, before, after):
def add_events_for_days(request, baseqs, before, after, ebd, timezones):
qs = baseqs.filter(is_public=True, live=True, has_subevents=False).filter(
Q(Q(date_to__gte=before) & Q(date_from__lte=after)) |
Q(Q(date_from__lte=after) & Q(date_to__gte=before)) |
Q(Q(date_to__isnull=True) & Q(date_from__gte=before) & Q(date_from__lte=after))
).order_by(
'date_from'
@@ -471,7 +470,6 @@ def add_events_for_days(request, baseqs, before, after, ebd, timezones):
def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_namespace=None, voucher=None):
qs = qs.filter(active=True, is_public=True).filter(
Q(Q(date_to__gte=before) & Q(date_from__lte=after)) |
Q(Q(date_from__lte=after) & Q(date_to__gte=before)) |
Q(Q(date_to__isnull=True) & Q(date_from__gte=before) & Q(date_from__lte=after))
).order_by(
'date_from'
@@ -562,7 +560,7 @@ def sort_ev(e):
return e['time'] or time(0, 0, 0), str(e['event'].name)
def days_for_template(ebd, week):
def days_for_template(ebd, week, future_only=False):
day_format = get_format('WEEK_DAY_FORMAT')
if day_format == 'WEEK_DAY_FORMAT':
day_format = 'SHORT_DATE_FORMAT'
@@ -574,11 +572,13 @@ def days_for_template(ebd, week):
'events': sorted(ebd.get(day), key=sort_ev) if day in ebd else []
}
for day in week.days()
if not future_only or day > now().astimezone(get_current_timezone()).date()
]
def weeks_for_template(ebd, year, month):
def weeks_for_template(ebd, year, month, future_only=False):
calendar.setfirstweekday(0) # TODO: Configurable
today = now().astimezone(get_current_timezone()).date()
return [
[
{
@@ -594,6 +594,9 @@ def weeks_for_template(ebd, year, month):
for day in week
]
for week in calendar.monthcalendar(year, month)
if not future_only or (
any(day != 0 and date(year, month, day) > today for day in week)
)
]

View File

@@ -497,6 +497,10 @@ class WidgetAPIProductList(EventListMixin, View):
tz = self.request.organizer.timezone
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)
if hasattr(self.request, 'event') and self.request.event.settings.event_calendar_future_only:
limit_before = min(after, now().astimezone(tz))
else:
limit_before = before
ebd = defaultdict(list)
@@ -507,7 +511,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__sales_channels__contains=self.request.sales_channel.identifier
), self.request
),
before, after, ebd, set(), self.request.event,
limit_before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
else:
@@ -519,7 +523,7 @@ class WidgetAPIProductList(EventListMixin, View):
sales_channels__contains=self.request.sales_channel.identifier
), self.request
),
before, after, ebd, timezones
limit_before, after, ebd, timezones
)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
event__organizer=self.request.organizer,
@@ -528,7 +532,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__sales_channels__contains=self.request.sales_channel.identifier
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), before, after, ebd, timezones)
)), self.request), limit_before, after, ebd, timezones)
data['weeks'] = weeks_for_template(ebd, self.year, self.month)
for w in data['weeks']:
@@ -553,11 +557,16 @@ class WidgetAPIProductList(EventListMixin, View):
week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, tzinfo=tz
) + timedelta(days=1)
if hasattr(self.request, 'event') and self.request.event.settings.event_calendar_future_only:
limit_before = now().astimezone(tz)
else:
limit_before = before
ebd = defaultdict(list)
if hasattr(self.request, 'event'):
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated('web'), self.request),
before, after, ebd, set(), self.request.event,
limit_before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
else:
@@ -565,7 +574,7 @@ class WidgetAPIProductList(EventListMixin, View):
add_events_for_days(
self.request,
filter_qs_by_attr(Event.annotated(self.request.organizer.events, 'web'), self.request),
before, after, ebd, timezones
limit_before, after, ebd, timezones
)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
event__organizer=self.request.organizer,
@@ -573,7 +582,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
)), self.request), before, after, ebd, timezones)
)), self.request), limit_before, after, ebd, timezones)
data['days'] = days_for_template(ebd, week)
for d in data['days']:
@@ -582,9 +591,8 @@ class WidgetAPIProductList(EventListMixin, View):
offset = int(self.request.GET.get("offset", 0))
limit = 50
if hasattr(self.request, 'event'):
evs = self.request.event.subevents_sorted(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request)
)
evs = filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel.identifier), self.request)
evs = self.request.event.subevents_sorted(evs)
ordering = self.request.event.settings.get('frontpage_subevent_ordering', default='date_ascending', as_type=str)
data['has_more_events'] = False
if ordering in ("date_ascending", "date_descending"):