Fix #202 -- Allow the manual ordering of questions

* Allow the manual ordering of questions
Update Unit Tests
Fix some typos

* Add migrations

* Minor notation change
This commit is contained in:
Enrique Saez
2016-08-29 18:36:56 +02:00
committed by Raphael Michel
parent 68967fbfda
commit 3583dde1db
8 changed files with 119 additions and 2 deletions

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.9 on 2016-08-21 19:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0031_auto_20160816_0648'),
]
operations = [
migrations.AddField(
model_name='question',
name='position',
field=models.IntegerField(default=0),
),
]

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.9 on 2016-08-21 22:22
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0032_question_position'),
]
operations = [
migrations.AlterModelOptions(
name='question',
options={'ordering': ('position', 'id'), 'verbose_name': 'Question', 'verbose_name_plural': 'Questions'},
),
]

View File

@@ -386,10 +386,14 @@ class Question(LoggedModel):
blank=True,
help_text=_('This question will be asked to buyers of the selected products')
)
position = models.IntegerField(
default=0
)
class Meta:
verbose_name = _("Question")
verbose_name_plural = _("Questions")
ordering = ('position', 'id')
def __str__(self):
return str(self.question)
@@ -404,6 +408,13 @@ class Question(LoggedModel):
if self.event:
self.event.get_cache().clear()
@property
def sortkey(self):
return self.position, self.id
def __lt__(self, other) -> bool:
return self.sortkey < other.sortkey
class QuestionOption(models.Model):
question = models.ForeignKey('Question', related_name='options')

View File

@@ -41,6 +41,10 @@
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">{{ q.question }}</a></strong>
</td>
<td>{{ q.get_type_display }}</td>
<td>
<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 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
<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 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
</td>
<td class="text-right"><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>

View File

@@ -50,6 +50,9 @@ urlpatterns = [
url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
url(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(),
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.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),

View File

@@ -208,7 +208,7 @@ def category_move(request, category, up=True):
if cat.position != i:
cat.position = i
cat.save()
messages.success(request, _('The order of categories as been updated.'))
messages.success(request, _('The order of categories has been updated.'))
@event_permission_required("can_change_items")
@@ -237,6 +237,49 @@ class QuestionList(ListView):
return self.request.event.questions.all()
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):
model = Question
template_name = 'pretixcontrol/items/question_delete.html'

View File

@@ -9,7 +9,7 @@ class QuestionsViewMixin:
@cached_property
def forms(self):
"""
A list of forms with one form for each cart cart position that has questions
A list of forms with one form for each cart position that has questions
the user can answer. All forms have a custom prefix, so that they can all be
submitted at once.
"""