Question list: Drop pagination, allow to mix ordering with system fields

This commit is contained in:
Raphael Michel
2020-06-23 13:05:54 +02:00
parent 868292f9b3
commit 2a5c24482e
8 changed files with 215 additions and 204 deletions

View File

@@ -244,8 +244,10 @@ class BaseQuestionsForm(forms.Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
add_fields = {}
if item.admission and event.settings.attendee_names_asked: if item.admission and event.settings.attendee_names_asked:
self.fields['attendee_name_parts'] = NamePartsFormField( add_fields['attendee_name_parts'] = NamePartsFormField(
max_length=255, max_length=255,
required=event.settings.attendee_names_required and not self.all_optional, required=event.settings.attendee_names_required and not self.all_optional,
scheme=event.settings.name_scheme, scheme=event.settings.name_scheme,
@@ -254,7 +256,7 @@ class BaseQuestionsForm(forms.Form):
initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts), initial=(cartpos.attendee_name_parts if cartpos else orderpos.attendee_name_parts),
) )
if item.admission and event.settings.attendee_emails_asked: if item.admission and event.settings.attendee_emails_asked:
self.fields['attendee_email'] = forms.EmailField( add_fields['attendee_email'] = forms.EmailField(
required=event.settings.attendee_emails_required and not self.all_optional, required=event.settings.attendee_emails_required and not self.all_optional,
label=_('Attendee email'), label=_('Attendee email'),
initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email), initial=(cartpos.attendee_email if cartpos else orderpos.attendee_email),
@@ -265,14 +267,14 @@ class BaseQuestionsForm(forms.Form):
) )
) )
if item.admission and event.settings.attendee_company_asked: if item.admission and event.settings.attendee_company_asked:
self.fields['company'] = forms.CharField( add_fields['company'] = forms.CharField(
required=event.settings.attendee_company_required and not self.all_optional, required=event.settings.attendee_company_required and not self.all_optional,
label=_('Company'), label=_('Company'),
initial=(cartpos.company if cartpos else orderpos.company), initial=(cartpos.company if cartpos else orderpos.company),
) )
if item.admission and event.settings.attendee_addresses_asked: if item.admission and event.settings.attendee_addresses_asked:
self.fields['street'] = forms.CharField( add_fields['street'] = forms.CharField(
required=event.settings.attendee_addresses_required and not self.all_optional, required=event.settings.attendee_addresses_required and not self.all_optional,
label=_('Address'), label=_('Address'),
widget=forms.Textarea(attrs={ widget=forms.Textarea(attrs={
@@ -282,7 +284,7 @@ class BaseQuestionsForm(forms.Form):
}), }),
initial=(cartpos.street if cartpos else orderpos.street), initial=(cartpos.street if cartpos else orderpos.street),
) )
self.fields['zipcode'] = forms.CharField( add_fields['zipcode'] = forms.CharField(
required=event.settings.attendee_addresses_required and not self.all_optional, required=event.settings.attendee_addresses_required and not self.all_optional,
label=_('ZIP code'), label=_('ZIP code'),
initial=(cartpos.zipcode if cartpos else orderpos.zipcode), initial=(cartpos.zipcode if cartpos else orderpos.zipcode),
@@ -290,7 +292,7 @@ class BaseQuestionsForm(forms.Form):
'autocomplete': 'postal-code', 'autocomplete': 'postal-code',
}), }),
) )
self.fields['city'] = forms.CharField( add_fields['city'] = forms.CharField(
required=event.settings.attendee_addresses_required and not self.all_optional, required=event.settings.attendee_addresses_required and not self.all_optional,
label=_('City'), label=_('City'),
initial=(cartpos.city if cartpos else orderpos.city), initial=(cartpos.city if cartpos else orderpos.city),
@@ -299,7 +301,7 @@ class BaseQuestionsForm(forms.Form):
}), }),
) )
country = (cartpos.country if cartpos else orderpos.country) or guess_country(event) country = (cartpos.country if cartpos else orderpos.country) or guess_country(event)
self.fields['country'] = CountryField( add_fields['country'] = CountryField(
countries=CachedCountries countries=CachedCountries
).formfield( ).formfield(
required=event.settings.attendee_addresses_required and not self.all_optional, required=event.settings.attendee_addresses_required and not self.all_optional,
@@ -324,7 +326,7 @@ class BaseQuestionsForm(forms.Form):
self.data = self.data.copy() self.data = self.data.copy()
del self.data[fprefix + 'state'] del self.data[fprefix + 'state']
self.fields['state'] = forms.ChoiceField( add_fields['state'] = forms.ChoiceField(
label=pgettext_lazy('address', 'State'), label=pgettext_lazy('address', 'State'),
required=False, required=False,
choices=c, choices=c,
@@ -332,7 +334,14 @@ class BaseQuestionsForm(forms.Form):
'autocomplete': 'address-level1', 'autocomplete': 'address-level1',
}), }),
) )
self.fields['state'].widget.is_required = True add_fields['state'].widget.is_required = True
field_positions = list(
[
(n, event.settings.system_question_order.get(n if n != 'state' else 'country', 0))
for n in add_fields.keys()
]
)
for q in questions: for q in questions:
# Do we already have an answer? Provide it as the initial value # Do we already have an answer? Provide it as the initial value
@@ -485,7 +494,12 @@ class BaseQuestionsForm(forms.Form):
field._required = q.required and not self.all_optional field._required = q.required and not self.all_optional
field.required = False field.required = False
self.fields['question_%s' % q.id] = field add_fields['question_%s' % q.id] = field
field_positions.append(('question_%s' % q.id, q.position))
field_positions.sort(key=lambda e: e[1])
for fname, p in field_positions:
self.fields[fname] = add_fields[fname]
responses = question_form_fields.send(sender=event, position=pos) responses = question_form_fields.send(sender=event, position=pos)
data = pos.meta_info_data data = pos.meta_info_data

View File

@@ -56,6 +56,10 @@ DEFAULTS = {
) )
}, },
'system_question_order': {
'default': {},
'type': dict,
},
'attendee_names_asked': { 'attendee_names_asked': {
'default': 'True', 'default': 'True',
'type': bool, 'type': bool,

View File

@@ -10,81 +10,89 @@
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% csrf_token %} {% csrf_token %}
{% if questions|length == 0 %} <p>
<div class="empty-collection"> <a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new question" %}
<p> </a>
{% blocktrans trimmed %} </p>
You haven't created any questions yet. <div class="table-responsive">
{% endblocktrans %} <table class="table table-hover table-quotas">
</p> <thead>
<tr>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" <th>{% trans "Question" %}</th>
class="btn btn-primary btn-lg"><i class="fa fa-plus"></i> {% trans "Create a new question" %}</a> <th>{% trans "Type" %}</th>
</div> <th class="iconcol"></th>
{% else %} <th class="iconcol"></th>
<p> <th class="iconcol"></th>
<a href="{% url "control:event.items.questions.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create a new question" %} <th>{% trans "Products" %}</th>
</a> <th class="action-col-2"></th>
</p> <th class="action-col-2"></th>
<div class="table-responsive"> </tr>
<table class="table table-hover table-quotas"> </thead>
<thead> <tbody data-dnd-url="{% url "control:event.items.questions.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
<tr> {% for q in questions %}
<th>{% trans "Question" %}</th> <tr data-dnd-id="{{ q.id }}">
<th>{% trans "Type" %}</th> <td>
<th class="iconcol"></th> <strong>
<th class="iconcol"></th> {% if q.pk %}
<th class="iconcol"></th> <a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">
<th>{% trans "Products" %}</th> {% endif %}
<th class="action-col-2"></th> {{ q.question }}
<th class="action-col-2"></th> {% if q.pk %}
</tr> </a>
</thead> {% endif %}
<tbody data-dnd-url="{% url "control:event.items.questions.reorder" organizer=request.event.organizer.slug event=request.event.slug %}"> </strong>
{% for q in questions %} </td>
<tr data-dnd-id="{{q.id}}"> <td>
<td><strong><a href=" {% if q.pk %}
{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">{{ q.question }}</a></strong>
</td>
<td>
{{ q.get_type_display }} {{ q.get_type_display }}
</td> {% else %}
<td> {% trans "System question" %}
{% if q.required %} {% endif %}
<span class="fa fa-exclamation-circle text-muted" data-toggle="tooltip" title="{% trans "Required question" %}"></span> </td>
{% endif %} <td>
</td> {% if q.required %}
<td> <span class="fa fa-exclamation-circle text-muted" data-toggle="tooltip" title="{% trans "Required question" %}"></span>
{% if q.ask_during_checkin %} {% endif %}
<span class="fa fa-check-square text-muted" data-toggle="tooltip" title="{% trans "Ask during check-in" %}"></span> </td>
{% endif %} <td>
{% if q.pk and q.ask_during_checkin %}
<span class="fa fa-check-square text-muted" data-toggle="tooltip" title="{% trans "Ask during check-in" %}"></span>
{% endif %}
</td> </td>
<td> <td>
{% if q.hidden %} {% if q.pk and q.hidden %}
<span class="fa fa-eye-slash text-muted" data-toggle="tooltip" title="{% trans "Hidden question" %}"></span> <span class="fa fa-eye-slash text-muted" data-toggle="tooltip" title="{% trans "Hidden question" %}"></span>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if q.pk %}
<ul> <ul>
{% for item in q.items.all %} {% for item in q.items.all %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a></li> <li>
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a>
</li>
{% endfor %} {% endfor %}
</ul> </ul>
</td> {% else %}
<td> <small>{% trans "All admission products" %}</small>
<a href="{% url "control:event.items.questions.up" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 and is_paginated and not page_obj.has_previous %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a> {% endif %}
<a href="{% url "control:event.items.questions.down" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 and is_paginated and not page_obj.has_next %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a> </td>
</td> <td class="dnd-container">
<td class="text-right flip"> </td>
<td class="text-right flip">
{% if q.pk %}
<a href="{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-bar-chart"></i></a>
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a> <a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a> <a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
</td> {% else %}
</tr> <a href="{% url "control:event.settings" organizer=request.event.organizer.slug event=request.event.slug %}#tab-0-2-open"
{% endfor %} class="btn btn-default btn-sm"><i class="fa fa-wrench"></i></a>
</tbody> {% endif %}
</table> </td>
</div> </tr>
{% include "pretixcontrol/pagination.html" %} {% endfor %}
{% endif %} </tbody>
</table>
</div>
{% endblock %} {% endblock %}

View File

@@ -180,9 +180,6 @@ urlpatterns = [
url(r'^questions/reorder$', item.reorder_questions, name='event.items.questions.reorder'), url(r'^questions/reorder$', item.reorder_questions, name='event.items.questions.reorder'),
url(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(), url(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(),
name='event.items.questions.delete'), name='event.items.questions.delete'),
url(r'^questions/(?P<question>\d+)/up$', item.question_move_up, name='event.items.questions.up'),
url(r'^questions/(?P<question>\d+)/down$', item.question_move_down,
name='event.items.questions.down'),
url(r'^questions/(?P<question>\d+)/$', item.QuestionView.as_view(), url(r'^questions/(?P<question>\d+)/$', item.QuestionView.as_view(),
name='event.items.questions.show'), name='event.items.questions.show'),
url(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(), url(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(),

View File

@@ -1,12 +1,12 @@
import json import json
from collections import OrderedDict from collections import OrderedDict, namedtuple
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.core.files import File from django.core.files import File
from django.db import transaction from django.db import transaction
from django.db.models import Count, Exists, F, Max, OuterRef, Prefetch, Q from django.db.models import Count, Exists, F, OuterRef, Prefetch, Q
from django.forms.models import inlineformset_factory from django.forms.models import inlineformset_factory
from django.http import ( from django.http import (
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect,
@@ -279,7 +279,12 @@ def category_move_down(request, organizer, event, category):
event=request.event.slug) event=request.event.slug)
class QuestionList(PaginationMixin, ListView): FakeQuestion = namedtuple(
'FakeQuestion', 'id question position required'
)
class QuestionList(ListView):
model = Question model = Question
context_object_name = 'questions' context_object_name = 'questions'
template_name = 'pretixcontrol/items/questions.html' template_name = 'pretixcontrol/items/questions.html'
@@ -287,98 +292,125 @@ class QuestionList(PaginationMixin, ListView):
def get_queryset(self): def get_queryset(self):
return self.request.event.questions.prefetch_related('items') return self.request.event.questions.prefetch_related('items')
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['questions'] = list(ctx['questions'])
if self.request.event.settings.attendee_names_asked:
ctx['questions'].append(
FakeQuestion(
id='attendee_name_parts',
question=_('Attendee name'),
position=self.request.event.settings.system_question_order.get(
'attendee_name_parts', 0
),
required=self.request.event.settings.attendee_names_required,
)
)
if self.request.event.settings.attendee_emails_asked:
ctx['questions'].append(
FakeQuestion(
id='attendee_email',
question=_('Attendee email'),
position=self.request.event.settings.system_question_order.get(
'attendee_email', 0
),
required=self.request.event.settings.attendee_emails_required,
)
)
if self.request.event.settings.attendee_emails_asked:
ctx['questions'].append(
FakeQuestion(
id='company',
question=_('Company'),
position=self.request.event.settings.system_question_order.get(
'company', 0
),
required=self.request.event.settings.attendee_company_required,
)
)
if self.request.event.settings.attendee_addresses_asked:
ctx['questions'].append(
FakeQuestion(
id='street',
question=_('Street'),
position=self.request.event.settings.system_question_order.get(
'street', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
ctx['questions'].append(
FakeQuestion(
id='zipcode',
question=_('ZIP code'),
position=self.request.event.settings.system_question_order.get(
'zipcode', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
ctx['questions'].append(
FakeQuestion(
id='city',
question=_('City'),
position=self.request.event.settings.system_question_order.get(
'city', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
ctx['questions'].append(
FakeQuestion(
id='country',
question=_('Country'),
position=self.request.event.settings.system_question_order.get(
'country', 0
),
required=self.request.event.settings.attendee_addresses_required,
)
)
ctx['questions'].sort(key=lambda q: q.position)
return ctx
@transaction.atomic @transaction.atomic
@event_permission_required("can_change_items") @event_permission_required("can_change_items")
def reorder_questions(request, organizer, event): def reorder_questions(request, organizer, event):
try: try:
ids = [int(id) for id in json.loads(request.body.decode('utf-8'))['ids']] ids = json.loads(request.body.decode('utf-8'))['ids']
except (JSONDecodeError, KeyError, ValueError): except (JSONDecodeError, KeyError, ValueError):
return HttpResponseBadRequest("expected JSON: {ids:[]}") return HttpResponseBadRequest("expected JSON: {ids:[]}")
input_questions = request.event.questions.filter(id__in=ids) input_questions = request.event.questions.filter(id__in=[i for i in ids if i.isdigit()])
if input_questions.count() != len(ids): if input_questions.count() != len([i for i in ids if i.isdigit()]):
raise Http404(_("Some of the provided question ids are invalid.")) raise Http404(_("Some of the provided question ids are invalid."))
first = input_questions.first() if input_questions.count() != request.event.questions.count():
last = input_questions.last() raise Http404(_("Not all questions have been selected."))
original_lowest_score = (first.position, first.id)
original_highest_score = (last.position, last.id)
if request.event.questions.filter( for q in input_questions:
Q(Q(position__gt=original_lowest_score[0]) pos = ids.index(str(q.pk))
| Q(Q(position=original_lowest_score[0]) & Q(pk__gt=original_lowest_score[1])))
&
Q(Q(position__lt=original_highest_score[0])
| Q(Q(position=original_highest_score[0]) & Q(pk__lt=original_highest_score[1])))
).exclude(id__in=ids).exists():
return HttpResponseBadRequest("ids need to be from a consecutive range of questions")
highest_position_on_previous_page = request.event.questions.filter(
Q(position__lt=original_lowest_score[0])
| Q(Q(position=original_lowest_score[0]) & Q(pk__lt=original_lowest_score[1]))
).aggregate(m=Max('position'))['m'] or 0
questions_on_later_pages = request.event.questions.filter(
Q(position__gt=original_highest_score[0])
| Q(Q(position=original_highest_score[0]) & Q(pk__gt=original_highest_score[1]))
)
ordered_questions = sorted(input_questions, key=lambda k: ids.index(k.pk))
for i, q in enumerate(ordered_questions + list(questions_on_later_pages)):
pos = highest_position_on_previous_page + 1 + i
if pos != q.position: # Save unneccessary UPDATE queries if pos != q.position: # Save unneccessary UPDATE queries
q.position = pos q.position = pos
q.save(update_fields=['position']) q.save(update_fields=['position'])
system_question_order = {}
for s in ('attendee_name_parts', 'attendee_email', 'company', 'street', 'zipcode', 'city', 'country'):
if s in ids:
system_question_order[s] = ids.index(s)
else:
system_question_order[s] = -1
request.event.settings.system_question_order = system_question_order
return HttpResponse() return HttpResponse()
def question_move(request, question, up=True):
"""
This is a helper function to avoid duplicating code in question_move_up and
question_move_down. It takes a question and a direction and then tries to bring
all items for this question in a new order.
"""
try:
question = request.event.questions.get(
id=question
)
except Question.DoesNotExist:
raise Http404(_("The selected question does not exist."))
questions = list(request.event.questions.order_by("position"))
index = questions.index(question)
if index != 0 and up:
questions[index - 1], questions[index] = questions[index], questions[index - 1]
elif index != len(questions) - 1 and not up:
questions[index + 1], questions[index] = questions[index], questions[index + 1]
for i, qt in enumerate(questions):
if qt.position != i:
qt.position = i
qt.save()
messages.success(request, _('The order of questions has been updated.'))
@event_permission_required("can_change_items")
def question_move_up(request, organizer, event, question):
question_move(request, question, up=True)
return redirect('control:event.items.questions',
organizer=request.event.organizer.slug,
event=request.event.slug)
@event_permission_required("can_change_items")
def question_move_down(request, organizer, event, question):
question_move(request, question, up=False)
return redirect('control:event.items.questions',
organizer=request.event.organizer.slug,
event=request.event.slug)
class QuestionDelete(EventPermissionRequiredMixin, DeleteView): class QuestionDelete(EventPermissionRequiredMixin, DeleteView):
model = Question model = Question
template_name = 'pretixcontrol/items/question_delete.html' template_name = 'pretixcontrol/items/question_delete.html'

View File

@@ -3,21 +3,10 @@ $(function () {
$("[data-dnd-url]").each(function(){ $("[data-dnd-url]").each(function(){
var container = $(this), var container = $(this),
url = container.data("dnd-url"), url = container.data("dnd-url"),
up = container.find("a:has(.fa-arrow-up)"),
handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>'); handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>');
function hideArrows(container){ console.log(container, container.find(".dnd-container"));
var up = container.find("a:has(.fa-arrow-up)"), container.find(".dnd-container").append(handle);
firstUp = up.first(),
down = container.find("a:has(.fa-arrow-down)"),
lastDown = down.last();
up.not(firstUp).css("display","none");
down.not(lastDown).css("display","none");
firstUp.css("display","inline-block");
lastDown.css("display","inline-block");
}
up.after(handle);
hideArrows(container);
Sortable.create(container.get(0), { Sortable.create(container.get(0), {
handle: ".dnd-sort-handle", handle: ".dnd-sort-handle",
@@ -25,8 +14,6 @@ $(function () {
var container = $(evt.to), var container = $(evt.to),
ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; }); ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; });
hideArrows(container);
$.ajax( $.ajax(
{ {
'type': 'POST', 'type': 'POST',

View File

@@ -171,35 +171,6 @@ class QuestionsTest(ItemFormTest):
self.assertTrue(c.required) self.assertTrue(c.required)
assert str(Question.objects.get(id=c.id).question) == 'How old are you?' assert str(Question.objects.get(id=c.id).question) == 'How old are you?'
def test_sort(self):
with scopes_disabled():
q1 = Question.objects.create(event=self.event1, question="Vegetarian?", type="N", required=True, position=0)
q2 = Question.objects.create(event=self.event1, question="Food allergies?", position=1)
doc = self.get_doc('/control/event/%s/%s/questions/' % (self.orga1.slug, self.event1.slug))
self.assertIn("Vegetarian?", doc.select("table > tbody > tr")[0].text)
self.assertIn("Food allergies?", doc.select("table > tbody > tr")[1].text)
self.client.get('/control/event/%s/%s/questions/%s/down' % (self.orga1.slug, self.event1.slug, q1.id))
doc = self.get_doc('/control/event/%s/%s/questions/' % (self.orga1.slug, self.event1.slug))
self.assertIn("Vegetarian?", doc.select("table > tbody > tr")[1].text)
self.assertIn("Food allergies?", doc.select("table > tbody > tr")[0].text)
self.client.get('/control/event/%s/%s/questions/%s/up' % (self.orga1.slug, self.event1.slug, q1.id))
doc = self.get_doc('/control/event/%s/%s/questions/' % (self.orga1.slug, self.event1.slug))
self.assertIn("Vegetarian?", doc.select("table > tbody > tr")[0].text)
self.assertIn("Food allergies?", doc.select("table > tbody > tr")[1].text)
self.client.post(
'/control/event/%s/%s/questions/reorder' % (self.orga1.slug, self.event1.slug),
{
"ids": [q2.id, q1.id]
},
content_type='application/json'
)
doc = self.get_doc('/control/event/%s/%s/questions/' % (self.orga1.slug, self.event1.slug))
self.assertIn("Vegetarian?", doc.select("table > tbody > tr")[1].text)
self.assertIn("Food allergies?", doc.select("table > tbody > tr")[0].text)
def test_delete(self): def test_delete(self):
with scopes_disabled(): with scopes_disabled():
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True) c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)

View File

@@ -260,8 +260,6 @@ event_permission_urls = [
# ("can_change_items", "questions/", 200), # ("can_change_items", "questions/", 200),
("can_change_items", "questions/2/", 404), ("can_change_items", "questions/2/", 404),
("can_change_items", "questions/2/delete", 404), ("can_change_items", "questions/2/delete", 404),
("can_change_items", "questions/2/up", 404),
("can_change_items", "questions/2/down", 404),
("can_change_items", "questions/reorder", 400), ("can_change_items", "questions/reorder", 400),
("can_change_items", "questions/add", 200), ("can_change_items", "questions/add", 200),
# ("can_change_items", "quotas/", 200), # ("can_change_items", "quotas/", 200),