Fix #406 -- Allow moving waiting list entries to the top or bottom

This commit is contained in:
Raphael Michel
2018-08-07 10:53:07 +02:00
parent 0100604798
commit 67897dfcc0
7 changed files with 124 additions and 23 deletions

View File

@@ -8,7 +8,7 @@ class WaitingListSerializer(I18nAwareModelSerializer):
class Meta:
model = WaitingListEntry
fields = ('id', 'created', 'email', 'voucher', 'item', 'variation', 'locale', 'subevent')
fields = ('id', 'created', 'email', 'voucher', 'item', 'variation', 'locale', 'subevent', 'priority')
read_only_fields = ('id', 'created', 'voucher')
def validate(self, data):

View File

@@ -0,0 +1,28 @@
# Generated by Django 2.1 on 2018-08-07 08:41
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0098_auto_20180731_1243'),
]
operations = [
migrations.AlterModelOptions(
name='waitinglistentry',
options={'ordering': ('-priority', 'created'), 'verbose_name': 'Waiting list entry', 'verbose_name_plural': 'Waiting list entries'},
),
migrations.AddField(
model_name='waitinglistentry',
name='priority',
field=models.IntegerField(default=0),
),
migrations.AlterField(
model_name='waitinglistentry',
name='voucher',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Voucher', verbose_name='Assigned voucher'),
),
]

View File

@@ -65,11 +65,12 @@ class WaitingListEntry(LoggedModel):
max_length=190,
default='en'
)
priority = models.IntegerField(default=0)
class Meta:
verbose_name = _("Waiting list entry")
verbose_name_plural = _("Waiting list entries")
ordering = ['created']
ordering = ('-priority', 'created')
def __str__(self):
return '%s waits for %s' % (str(self.email), str(self.item))

View File

@@ -22,7 +22,9 @@ def assign_automatically(event_id: int, user_id: int=None, subevent_id: int=None
qs = WaitingListEntry.objects.filter(
event=event, voucher__isnull=True
).select_related('item', 'variation').prefetch_related('item__quotas', 'variation__quotas').order_by('created')
).select_related('item', 'variation').prefetch_related(
'item__quotas', 'variation__quotas'
).order_by('-priority', 'created')
if subevent_id and event.has_subevents:
subevent = event.subevents.get(id=subevent_id)

View File

@@ -151,7 +151,15 @@
{% if request.event.has_subevents %}
<td>{{ e.subevent.name }} {{ e.subevent.get_date_range_display }}</td>
{% endif %}
<td>{{ e.created|date:"SHORT_DATETIME_FORMAT" }}</td>
<td>
{{ e.created|date:"SHORT_DATETIME_FORMAT" }}
{% if e.priority != 0 %}
<span class="label label-info" data-toggle="tooltip"
title="{% trans "This entry has a modified priority. The higher this number is, the earlier this person will be assigned a voucher." %}">
{% if e.priority > 0 %}+{% endif %}{{ e.priority }}
</span>
{% endif %}
</td>
<td>
{% if e.voucher %}
{% if e.voucher.redeemed >= e.voucher.max_usages %}
@@ -184,8 +192,22 @@
</td>
<td class="text-right">
{% if not e.voucher %}
<button name="move_top" value="{{ e.pk }}" class="btn btn-default btn-sm"
data-toggle="tooltip" title="{% trans "Move to the top of the list" %}">
<span class="fa fa-thumbs-up"></span>
</button>
<button name="move_end" value="{{ e.pk }}" class="btn btn-default btn-sm"
data-toggle="tooltip" title="{% trans "Move to the end of the list" %}">
<span class="fa fa-thumbs-down"></span>
</button>
<a href="{% url "control:event.orders.waitinglist.delete" organizer=request.event.organizer.slug event=request.event.slug entry=e.id %}?next={{ request.get_full_path|urlencode }}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
{% else %}
<button class="btn btn-default btn-sm disabled">
<span class="fa fa-thumbs-up"></span>
</button>
<button class="btn btn-default btn-sm disabled">
<span class="fa fa-thumbs-down"></span>
</button>
<span class="btn btn-danger btn-sm disabled"><i class="fa fa-trash"></i></span>
{% endif %}
</td>

View File

@@ -3,7 +3,7 @@ import io
from django.contrib import messages
from django.db import transaction
from django.db.models import F, Q, Sum
from django.db.models import F, Max, Min, Q, Sum
from django.db.models.functions import Coalesce
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect
@@ -52,14 +52,12 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
permission = 'can_view_orders'
def post(self, request, *args, **kwargs):
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_orders',
request=request):
messages.error(request, _('You do not have permission to do this'))
return self._redirect_back()
if 'assign' in request.POST:
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_orders',
request=request):
messages.error(request, _('You do not have permission to do this'))
return redirect(reverse('control:event.orders.waitinglist', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug
}))
try:
wle = WaitingListEntry.objects.get(
pk=request.POST.get('assign'), event=self.request.event,
@@ -71,18 +69,44 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
else:
messages.success(request, _('An email containing a voucher code has been sent to the '
'specified address.'))
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
return redirect(self.request.GET.get("next"))
return redirect(reverse('control:event.orders.waitinglist', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug
}))
return self._redirect_back()
except WaitingListEntry.DoesNotExist:
messages.error(request, _('Waiting list entry not found.'))
return redirect(reverse('control:event.orders.waitinglist', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug
}))
return self._redirect_back()
if 'move_top' in request.POST:
try:
wle = WaitingListEntry.objects.get(
pk=request.POST.get('move_top'), event=self.request.event,
)
wle.priority = self.request.event.waitinglistentries.aggregate(m=Max('priority'))['m'] + 1
wle.save(update_fields=['priority'])
messages.success(request, _('The waiting list entry has been moved to the top.'))
return self._redirect_back()
except WaitingListEntry.DoesNotExist:
messages.error(request, _('Waiting list entry not found.'))
return self._redirect_back()
if 'move_end' in request.POST:
try:
wle = WaitingListEntry.objects.get(
pk=request.POST.get('move_end'), event=self.request.event,
)
wle.priority = self.request.event.waitinglistentries.aggregate(m=Min('priority'))['m'] - 1
wle.save(update_fields=['priority'])
messages.success(request, _('The waiting list entry has been moved to the end of the list.'))
return self._redirect_back()
except WaitingListEntry.DoesNotExist:
messages.error(request, _('Waiting list entry not found.'))
return self._redirect_back()
def _redirect_back(self):
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
return redirect(self.request.GET.get("next"))
return redirect(reverse('control:event.orders.waitinglist', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug
}))
def get_queryset(self):
qs = WaitingListEntry.objects.filter(
@@ -172,7 +196,7 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
headers = [
_('E-mail address'), _('Product'), _('On list since'), _('Status'), _('Voucher code'),
_('Language')
_('Language'), _('Priority')
]
if self.request.event.has_subevents:
headers.append(pgettext('subevent', 'Date'))
@@ -201,6 +225,7 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
status,
w.voucher.code if w.voucher else '',
w.locale,
str(w.priority)
]
if self.request.event.has_subevents:
row.append(str(w.subevent))