Add option to automatically check out all attendees at night (#1819)

This commit is contained in:
Raphael Michel
2020-10-21 18:26:57 +02:00
committed by GitHub
parent ffde521fcb
commit e3d9b3546d
11 changed files with 151 additions and 8 deletions

View File

@@ -15,7 +15,7 @@ class CheckinListSerializer(I18nAwareModelSerializer):
model = CheckinList
fields = ('id', 'name', 'all_products', 'limit_products', 'subevent', 'checkin_count', 'position_count',
'include_pending', 'auto_checkin_sales_channels', 'allow_multiple_entries', 'allow_entry_after_exit',
'rules')
'rules', 'exit_all_at')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.0.10 on 2020-10-20 06:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0166_auto_20201015_2029'),
]
operations = [
migrations.AddField(
model_name='checkinlist',
name='exit_all_at',
field=models.DateTimeField(blank=True, null=True),
),
]

View File

@@ -30,7 +30,10 @@ class CheckinList(LoggedModel):
help_text=_('Use this option to turn off warnings if a ticket is scanned a second time.'),
default=False
)
exit_all_at = models.DateTimeField(
verbose_name=_('Automatically check out everyone at'),
null=True, blank=True
)
auto_checkin_sales_channels = MultiStringField(
default=[],
blank=True,
@@ -62,7 +65,7 @@ class CheckinList(LoggedModel):
return qs
@property
def inside_count(self):
def positions_inside(self):
return self.positions.annotate(
last_entry=Subquery(
Checkin.objects.filter(
@@ -87,7 +90,11 @@ class CheckinList(LoggedModel):
& Q(
Q(last_exit__isnull=True) | Q(last_exit__lt=F('last_entry'))
)
).count()
)
@property
def inside_count(self):
return self.positions_inside.count()
@property
@scopes_disabled()

View File

@@ -7,11 +7,12 @@ from django.dispatch import receiver
from django.utils.functional import cached_property
from django.utils.timezone import now, override
from django.utils.translation import gettext as _
from django_scopes import scope, scopes_disabled
from pretix.base.models import (
Checkin, CheckinList, Device, Order, OrderPosition, QuestionOption,
)
from pretix.base.signals import checkin_created, order_placed
from pretix.base.signals import checkin_created, order_placed, periodic_task
from pretix.helpers.jsonlogic import Logic
@@ -262,5 +263,23 @@ def order_placed(sender, **kwargs):
for cl in cls:
if cl.all_products or op.item_id in {i.pk for i in cl.limit_products.all()}:
if not cl.subevent_id or cl.subevent_id == op.subevent_id:
ci = Checkin.objects.create(position=op, list=cl, auto_checked_in=True)
ci = Checkin.objects.create(position=op, list=cl, auto_checked_in=True, type=Checkin.TYPE_ENTRY)
checkin_created.send(event, checkin=ci)
@receiver(periodic_task, dispatch_uid="autocheckin_exit_all")
@scopes_disabled()
def process_exit_all(sender, **kwargs):
qs = CheckinList.objects.filter(
exit_all_at__lte=now(),
exit_all_at__isnull=False
).select_related('event', 'event__organizer')
for cl in qs:
for p in cl.positions_inside:
with scope(organizer=cl.event.organizer):
ci = Checkin.objects.create(
position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at
)
checkin_created.send(cl.event, checkin=ci)
cl.exit_all_at = cl.exit_all_at + timedelta(days=1)
cl.save(update_fields=['exit_all_at'])

View File

@@ -1,5 +1,8 @@
from datetime import datetime, timedelta
from django import forms
from django.urls import reverse
from django.utils.timezone import get_current_timezone, make_aware, now
from django.utils.translation import pgettext_lazy
from django_scopes.forms import (
SafeModelChoiceField, SafeModelMultipleChoiceField,
@@ -10,6 +13,21 @@ from pretix.base.models.checkin import CheckinList
from pretix.control.forms.widgets import Select2
class NextTimeField(forms.TimeField):
def to_python(self, value):
value = super().to_python(value)
if value is None:
return
tz = get_current_timezone()
result = make_aware(datetime.combine(
now().astimezone(tz).date(),
value,
), tz)
if result <= now():
result += timedelta(days=1)
return result
class CheckinListForm(forms.ModelForm):
def __init__(self, **kwargs):
self.event = kwargs.pop('event')
@@ -55,16 +73,19 @@ class CheckinListForm(forms.ModelForm):
'allow_multiple_entries',
'allow_entry_after_exit',
'rules',
'exit_all_at',
]
widgets = {
'limit_products': forms.CheckboxSelectMultiple(attrs={
'data-inverse-dependency': '<[name$=all_products]'
}),
'auto_checkin_sales_channels': forms.CheckboxSelectMultiple(),
'exit_all_at': forms.TimeInput(attrs={'class': 'timepickerfield'}),
}
field_classes = {
'limit_products': SafeModelMultipleChoiceField,
'subevent': SafeModelChoiceField,
'exit_all_at': NextTimeField,
}

View File

@@ -58,6 +58,7 @@
{% bootstrap_field form.allow_multiple_entries layout="control" %}
{% bootstrap_field form.allow_entry_after_exit layout="control" %}
{% bootstrap_field form.exit_all_at layout="control" %}
{% bootstrap_field form.auto_checkin_sales_channels layout="control" %}
<h3>{% trans "Custom check-in rule" %}</h3>

View File

@@ -299,7 +299,11 @@
{% if line.checkins.all %}
{% for c in line.checkins.all %}
{% if c.type == "exit" %}
<span class="fa fa-fw fa-sign-out" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Exit scan: {{ date }}{% endblocktrans %}"></span>
{% if c.auto_checked_in %}
<span class="fa fa-fw fa-hourglass-end" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically marked not present: {{ date }}{% endblocktrans %}"></span>
{% else %}
<span class="fa fa-fw fa-sign-out" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Exit scan: {{ date }}{% endblocktrans %}"></span>
{% endif %}
{% elif c.forced %}
<span class="fa fa-fw fa-warning" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}"></span>
{% elif c.auto_checked_in %}