Questions at check-in time (#745)

Questions at check-in time
This commit is contained in:
Raphael Michel
2018-01-22 22:55:54 +01:00
committed by GitHub
parent 7fb2d0526e
commit d0dfde382c
20 changed files with 754 additions and 47 deletions

View File

@@ -453,6 +453,7 @@ TEST_QUESTION_RES = {
"type": "C",
"required": False,
"items": [],
"ask_during_checkin": False,
"position": 0,
"options": [
{

View File

@@ -5,6 +5,7 @@ from decimal import Decimal
import pytest
import pytz
from dateutil.tz import tzoffset
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
@@ -12,6 +13,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.utils.timezone import now
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, CartPosition, CheckinList, Event, Item, ItemCategory,
ItemVariation, Order, OrderPosition, Organizer, Question, Quota, User,
@@ -1130,3 +1132,106 @@ class CheckinListTestCase(TestCase):
assert lists[2].checkin_count == 1
assert lists[2].position_count == 2
assert lists[2].percent == 50
@pytest.mark.django_db
@pytest.mark.parametrize("qtype,answer,expected", [
(Question.TYPE_STRING, "a", "a"),
(Question.TYPE_TEXT, "v", "v"),
(Question.TYPE_NUMBER, "3", Decimal("3")),
(Question.TYPE_NUMBER, "2.56", Decimal("2.56")),
(Question.TYPE_NUMBER, 2.45, Decimal("2.45")),
(Question.TYPE_NUMBER, 3, Decimal("3")),
(Question.TYPE_NUMBER, Decimal("4.56"), Decimal("4.56")),
(Question.TYPE_NUMBER, "abc", ValidationError),
(Question.TYPE_BOOLEAN, "True", True),
(Question.TYPE_BOOLEAN, "true", True),
(Question.TYPE_BOOLEAN, "False", False),
(Question.TYPE_BOOLEAN, "false", False),
(Question.TYPE_BOOLEAN, "0", False),
(Question.TYPE_BOOLEAN, "", False),
(Question.TYPE_BOOLEAN, True, True),
(Question.TYPE_BOOLEAN, False, False),
(Question.TYPE_DATE, "2018-01-16", datetime.date(2018, 1, 16)),
(Question.TYPE_DATE, datetime.date(2018, 1, 16), datetime.date(2018, 1, 16)),
(Question.TYPE_DATE, "2018-13-16", ValidationError),
(Question.TYPE_TIME, "15:20", datetime.time(15, 20)),
(Question.TYPE_TIME, datetime.time(15, 20), datetime.time(15, 20)),
(Question.TYPE_TIME, "44:20", ValidationError),
(Question.TYPE_DATETIME, "2018-01-16T15:20:00+01:00",
datetime.datetime(2018, 1, 16, 15, 20, 0, tzinfo=tzoffset(None, 3600))),
(Question.TYPE_DATETIME, "2018-01-16T15:20:00Z",
datetime.datetime(2018, 1, 16, 15, 20, 0, tzinfo=tzoffset(None, 0))),
(Question.TYPE_DATETIME, "2018-01-16T15:20:00",
datetime.datetime(2018, 1, 16, 15, 20, 0, tzinfo=tzoffset(None, 3600))),
(Question.TYPE_DATETIME, "2018-01-16T15:AB:CD", ValidationError),
])
def test_question_answer_validation(qtype, answer, expected):
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(),
)
event.settings.timezone = 'Europe/Berlin'
q = Question(type=qtype, event=event)
if isinstance(expected, type) and issubclass(expected, Exception):
with pytest.raises(expected):
q.clean_answer(answer)
elif callable(expected):
assert expected(q.clean_answer(answer))
else:
assert q.clean_answer(answer) == expected
@pytest.mark.django_db
def test_question_answer_validation_localized_decimal():
q = Question(type='N')
with language("de"):
assert q.clean_answer("2,56") == Decimal("2.56")
@pytest.mark.django_db
def test_question_answer_validation_choice():
organizer = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=organizer, name='Dummy', slug='dummy',
date_from=now(), date_to=now() - timedelta(hours=1),
)
q = Question.objects.create(type='C', event=event, question='Q')
o1 = q.options.create(answer='A')
o2 = q.options.create(answer='B')
q2 = Question.objects.create(type='C', event=event, question='Q2')
o3 = q2.options.create(answer='C')
assert q.clean_answer(str(o1.pk)) == o1
assert q.clean_answer(o1.pk) == o1
assert q.clean_answer(str(o2.pk)) == o2
assert q.clean_answer(o2.pk) == o2
with pytest.raises(ValidationError):
q.clean_answer(str(o2.pk + 1000))
with pytest.raises(ValidationError):
q.clean_answer('FOO')
with pytest.raises(ValidationError):
q.clean_answer(str(o3.pk))
@pytest.mark.django_db
def test_question_answer_validation_multiple_choice():
organizer = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=organizer, name='Dummy', slug='dummy',
date_from=now(), date_to=now() - timedelta(hours=1),
)
q = Question.objects.create(type='M', event=event, question='Q')
o1 = q.options.create(answer='A')
o2 = q.options.create(answer='B')
q.options.create(answer='D')
q2 = Question.objects.create(type='M', event=event, question='Q2')
o3 = q2.options.create(answer='C')
assert q.clean_answer("{},{}".format(str(o1.pk), str(o2.pk))) == [o1, o2]
assert q.clean_answer([str(o1.pk), str(o2.pk)]) == [o1, o2]
assert q.clean_answer([str(o1.pk)]) == [o1]
assert q.clean_answer([o1.pk]) == [o1]
assert q.clean_answer([o1.pk, o3.pk]) == [o1]
assert q.clean_answer([o1.pk, o3.pk + 1000]) == [o1]
with pytest.raises(ValidationError):
assert q.clean_answer([o1.pk, 'FOO']) == [o1]

View File

@@ -326,3 +326,233 @@ def test_status(client, env):
'variations': []
}
]
@pytest.fixture
def question(env):
q = env[0].questions.create(question='Size', type='C', required=True, ask_during_checkin=True)
a1 = q.options.create(answer="M")
a2 = q.options.create(answer="L")
q.items.add(env[3].item)
return q, a1, a2
@pytest.mark.django_db
def test_question_number(client, env, question):
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
question[0].options.all().delete()
question[0].type = 'N'
question[0].save()
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert jdata['questions'] == [
{
'id': question[0].pk,
'type': 'N',
'question': 'Size',
'required': True,
'position': question[0].position,
'options': []
}
]
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234',
'answer_{}'.format(question[0].pk): '3.24',
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'ok'
assert env[3].answers.get(question=question[0]).answer == '3.24'
@pytest.mark.django_db
def test_question_choice(client, env, question):
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert jdata['questions'] == [
{
'id': question[0].pk,
'type': 'C',
'question': 'Size',
'required': True,
'position': question[0].position,
'options': [
{
'id': question[1].pk,
'answer': 'M'
},
{
'id': question[2].pk,
'answer': 'L'
}
]
}
]
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234',
'answer_{}'.format(question[0].pk): question[1].pk,
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'ok'
assert env[3].answers.get(question=question[0]).answer == 'M'
assert list(env[3].answers.get(question=question[0]).options.all()) == [question[1]]
@pytest.mark.django_db
def test_question_invalid(client, env, question):
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert len(jdata['questions']) == 1
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234', 'questions_supported': 'true',
'answer_{}'.format(question[0].pk): "A",
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'incomplete'
assert len(jdata['questions']) == 1
@pytest.mark.django_db
def test_question_required(client, env, question):
question[0].required = True
question[0].save()
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert len(jdata['questions']) == 1
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234', 'questions_supported': 'true',
'answer_{}'.format(question[0].pk): "",
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'incomplete'
assert len(jdata['questions']) == 1
@pytest.mark.django_db
def test_question_optional(client, env, question):
question[0].required = False
question[0].save()
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert len(jdata['questions']) == 1
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234', 'questions_supported': 'true',
'answer_{}'.format(question[0].pk): "",
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'ok'
@pytest.mark.django_db
def test_question_multiple_choice(client, env, question):
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
question[0].type = 'M'
question[0].save()
resp = client.post('/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={'secret': '1234', 'questions_supported': 'true'})
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['version'] == API_VERSION
assert jdata['status'] == 'incomplete'
assert jdata['questions'] == [
{
'id': question[0].pk,
'type': 'M',
'question': 'Size',
'required': True,
'position': question[0].position,
'options': [
{
'id': question[1].pk,
'answer': 'M'
},
{
'id': question[2].pk,
'answer': 'L'
}
]
}
]
resp = client.post(
'/pretixdroid/api/%s/%s/redeem/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'),
data={
'secret': '1234', 'questions_supported': 'true',
'answer_{}'.format(question[0].pk): "{},{}".format(question[1].pk, question[2].pk),
}
)
jdata = json.loads(resp.content.decode("utf-8"))
assert jdata['status'] == 'ok'
assert env[3].answers.get(question=question[0]).answer == 'M, L'
assert set(env[3].answers.get(question=question[0]).options.all()) == {question[1], question[2]}
@pytest.mark.django_db
def test_download_questions(client, env, question):
AppConfiguration.objects.create(event=env[0], key='abcdefg', list=env[5])
resp = client.get('/pretixdroid/api/%s/%s/download/?key=%s' % (env[0].organizer.slug, env[0].slug, 'abcdefg'))
jdata = json.loads(resp.content.decode("utf-8"))
assert len(jdata['results']) == 2
assert jdata['questions'] == [
{
'id': question[0].pk,
'type': 'C',
'question': 'Size',
'required': True,
'position': question[0].position,
'items': [env[3].item.pk],
'options': [
{
'id': question[1].pk,
'answer': 'M'
},
{
'id': question[2].pk,
'answer': 'L'
}
]
}
]