def test_question_required(token_client, organizer, clist, event, order, question): with scopes_disabled(): p = order.positions.first() question[0].required = True question[0].save() resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' with scopes_disabled(): assert resp.data['questions'] == [QuestionSerializer(question[0]).data] resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {'answers': { question[0].pk: "" }}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' with scopes_disabled(): assert resp.data['questions'] == [QuestionSerializer(question[0]).data]
def test_question_multiple_choice(token_client, organizer, clist, event, order, question): with scopes_disabled(): p = order.positions.first() question[0].type = 'M' question[0].save() resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' with scopes_disabled(): assert resp.data['questions'] == [QuestionSerializer(question[0]).data] resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), { 'answers': { question[0].pk: "{},{}".format(question[1].pk, question[2].pk) } }, format='json') assert resp.status_code == 201 assert resp.data['status'] == 'ok' with scopes_disabled(): assert order.positions.first().answers.get( question=question[0]).answer == 'M, L' assert set(order.positions.first().answers.get( question=question[0]).options.all()) == {question[1], question[2]}
def test_question_choice_identifier(token_client, organizer, clist, event, order, question): with scopes_disabled(): p = order.positions.first() resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' with scopes_disabled(): assert resp.data['questions'] == [QuestionSerializer(question[0]).data] resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {'answers': { question[0].pk: str(question[1].identifier) }}, format='json') print(resp.data) assert resp.status_code == 201 assert resp.data['status'] == 'ok' with scopes_disabled(): assert order.positions.first().answers.get( question=question[0]).answer == 'M' assert list(order.positions.first().answers.get( question=question[0]).options.all()) == [question[1]]
def test_question_number(token_client, organizer, clist, event, order, question): question[0].options.all().delete() question[0].type = 'N' question[0].save() resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, order.positions.first().pk), {}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' assert resp.data['questions'] == [QuestionSerializer(question[0]).data] resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, order.positions.first().pk), {'answers': { question[0].pk: "3.24" }}, format='json') print(resp.data) assert resp.status_code == 201 assert resp.data['status'] == 'ok' assert order.positions.first().answers.get( question=question[0]).answer == '3.24'
def test_question_invalid(token_client, organizer, clist, event, order, question): resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format( organizer.slug, event.slug, clist.pk, order.positions.first().pk ), {'answers': {question[0].pk: "A"}}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' assert resp.data['questions'] == [QuestionSerializer(question[0]).data]
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') op = self.get_object(ignore_status=True) if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value(self.request.data.get('datetime')) else: dt = now() given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get('questions_supported', True), canceled_supported=self.request.data.get('canceled_supported', False), user=self.request.user, auth=self.request.auth, ) except RequiredQuestionsError as e: return Response({ 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data, 'questions': [ QuestionSerializer(q).data for q in e.questions ] }, status=400) except CheckInError as e: return Response({ 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=400) else: return Response({ 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=201)
def test_question_upload(token_client, organizer, clist, event, order, question): r = token_client.post( '/api/v1/upload', data={ 'media_type': 'image/png', 'file': ContentFile('file.png', 'invalid png content') }, format='upload', HTTP_CONTENT_DISPOSITION='attachment; filename="file.png"', ) assert r.status_code == 201 file_id_png = r.data['id'] with scopes_disabled(): p = order.positions.first() question[0].type = 'F' question[0].save() resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' with scopes_disabled(): assert resp.data['questions'] == [QuestionSerializer(question[0]).data] resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {'answers': { question[0].pk: "invalid" }}, format='json') assert resp.status_code == 400 assert resp.data['status'] == 'incomplete' resp = token_client.post( '/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'. format(organizer.slug, event.slug, clist.pk, p.pk), {'answers': { question[0].pk: file_id_png }}, format='json') assert resp.status_code == 201 assert resp.data['status'] == 'ok' with scopes_disabled(): assert order.positions.first().answers.get( question=question[0]).answer.startswith('file://') assert order.positions.first().answers.get(question=question[0]).file
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) type = self.request.data.get('type', None) or Checkin.TYPE_ENTRY if type not in dict(Checkin.CHECKIN_TYPES): raise ValidationError("Invalid check-in type.") ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value( self.request.data.get('datetime')) else: dt = now() try: queryset = self.get_queryset(ignore_status=True, ignore_products=True) if self.kwargs['pk'].isnumeric(): op = queryset.get( Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: op = queryset.get(secret=self.kwargs['pk']) except OrderPosition.DoesNotExist: revoked_matches = list( self.request.event.revoked_secrets.filter( secret=self.kwargs['pk'])) if len(revoked_matches) == 0 or not force: self.request.event.log_action('pretix.event.checkin.unknown', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) raise Http404() op = revoked_matches[0].position op.order.log_action('pretix.event.checkin.revoked', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: if q.type == Question.TYPE_FILE: given_answers[q] = self._handle_file_upload( aws[str(q.pk)]) else: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get( 'questions_supported', True), canceled_supported=self.request.data.get( 'canceled_supported', False), user=self.request.user, auth=self.request.auth, type=type, ) except RequiredQuestionsError as e: return Response( { 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data, 'questions': [QuestionSerializer(q).data for q in e.questions] }, status=400) except CheckInError as e: op.order.log_action('pretix.event.checkin.denied', data={ 'position': op.id, 'positionid': op.positionid, 'errorcode': e.code, 'force': force, 'datetime': dt, 'type': type, 'list': self.checkinlist.pk }, user=self.request.user, auth=self.request.auth) return Response( { 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) else: return Response( { 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=201)
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) type = self.request.data.get('type', None) or Checkin.TYPE_ENTRY if type not in dict(Checkin.CHECKIN_TYPES): raise ValidationError("Invalid check-in type.") ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value( self.request.data.get('datetime')) else: dt = now() common_checkin_args = dict( raw_barcode=self.kwargs['pk'], type=type, list=self.checkinlist, datetime=dt, device=self.request.auth if isinstance(self.request.auth, Device) else None, gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None, nonce=nonce, forced=force, ) raw_barcode_for_checkin = None try: queryset = self.get_queryset(ignore_status=True, ignore_products=True) if self.kwargs['pk'].isnumeric(): op = queryset.get( Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: op = queryset.get(secret=self.kwargs['pk']) except OrderPosition.DoesNotExist: revoked_matches = list( self.request.event.revoked_secrets.filter( secret=self.kwargs['pk'])) if len(revoked_matches) == 0: self.request.event.log_action('pretix.event.checkin.unknown', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) for k, s in self.request.event.ticket_secret_generators.items( ): try: parsed = s.parse_secret(self.kwargs['pk']) common_checkin_args.update({ 'raw_item': parsed.item, 'raw_variation': parsed.variation, 'raw_subevent': parsed.subevent, }) except: pass Checkin.objects.create( position=None, successful=False, error_reason=Checkin.REASON_INVALID, **common_checkin_args, ) if force and isinstance(self.request.auth, Device): # There was a bug in libpretixsync: If you scanned a ticket in offline mode that was # valid at the time but no longer exists at time of upload, the device would retry to # upload the same scan over and over again. Since we can't update all devices quickly, # here's a dirty workaround to make it stop. try: brand = self.request.auth.software_brand ver = parse(self.request.auth.software_version) legacy_mode = ((brand == 'pretixSCANPROXY' and ver < parse('0.0.3')) or (brand == 'pretixSCAN Android' and ver < parse('1.11.2')) or (brand == 'pretixSCAN' and ver < parse('1.11.2'))) if legacy_mode: return Response( { 'status': 'error', 'reason': Checkin.REASON_ALREADY_REDEEMED, 'reason_explanation': None, 'require_attention': False, '__warning': 'Compatibility hack active due to detected old pretixSCAN version', }, status=400) except: # we don't care e.g. about invalid version numbers pass return Response( { 'detail': 'Not found.', # for backwards compatibility 'status': 'error', 'reason': Checkin.REASON_INVALID, 'reason_explanation': None, 'require_attention': False, }, status=404) elif revoked_matches and force: op = revoked_matches[0].position raw_barcode_for_checkin = self.kwargs['pk'] else: op = revoked_matches[0].position op.order.log_action('pretix.event.checkin.revoked', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) Checkin.objects.create(position=op, successful=False, error_reason=Checkin.REASON_REVOKED, **common_checkin_args) return Response( { 'status': 'error', 'reason': Checkin.REASON_REVOKED, 'reason_explanation': None, 'require_attention': False, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: if q.type == Question.TYPE_FILE: given_answers[q] = self._handle_file_upload( aws[str(q.pk)]) else: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass with language(self.request.event.settings.locale): try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get( 'questions_supported', True), canceled_supported=self.request.data.get( 'canceled_supported', False), user=self.request.user, auth=self.request.auth, type=type, raw_barcode=raw_barcode_for_checkin, from_revoked_secret=True, ) except RequiredQuestionsError as e: return Response( { 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data, 'questions': [QuestionSerializer(q).data for q in e.questions] }, status=400) except CheckInError as e: op.order.log_action('pretix.event.checkin.denied', data={ 'position': op.id, 'positionid': op.positionid, 'errorcode': e.code, 'reason_explanation': e.reason, 'force': force, 'datetime': dt, 'type': type, 'list': self.checkinlist.pk }, user=self.request.user, auth=self.request.auth) Checkin.objects.create( position=op, successful=False, error_reason=e.code, error_explanation=e.reason, **common_checkin_args, ) return Response( { 'status': 'error', 'reason': e.code, 'reason_explanation': e.reason, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) else: return Response( { 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=201)
def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) type = self.request.data.get('type', None) or Checkin.TYPE_ENTRY if type not in dict(Checkin.CHECKIN_TYPES): raise ValidationError("Invalid check-in type.") ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value( self.request.data.get('datetime')) else: dt = now() common_checkin_args = dict( raw_barcode=self.kwargs['pk'], type=type, list=self.checkinlist, datetime=dt, device=self.request.auth if isinstance(self.request.auth, Device) else None, gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None, nonce=nonce, forced=force, ) try: queryset = self.get_queryset(ignore_status=True, ignore_products=True) if self.kwargs['pk'].isnumeric(): op = queryset.get( Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: op = queryset.get(secret=self.kwargs['pk']) except OrderPosition.DoesNotExist: revoked_matches = list( self.request.event.revoked_secrets.filter( secret=self.kwargs['pk'])) if len(revoked_matches) == 0: self.request.event.log_action('pretix.event.checkin.unknown', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) for k, s in self.request.event.ticket_secret_generators.items( ): try: parsed = s.parse_secret(self.kwargs['pk']) common_checkin_args.update({ 'raw_item': parsed.item, 'raw_variation': parsed.variation, 'raw_subevent': parsed.subevent, }) except: pass Checkin.objects.create( position=None, successful=False, error_reason=Checkin.REASON_INVALID, **common_checkin_args, ) raise Http404() else: op = revoked_matches[0].position op.order.log_action('pretix.event.checkin.revoked', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) Checkin.objects.create(position=op, successful=False, error_reason=Checkin.REASON_REVOKED, **common_checkin_args) return Response( { 'status': 'error', 'reason': Checkin.REASON_REVOKED, 'reason_explanation': None, 'require_attention': False, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: if q.type == Question.TYPE_FILE: given_answers[q] = self._handle_file_upload( aws[str(q.pk)]) else: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass with language(self.request.event.settings.locale): try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get( 'questions_supported', True), canceled_supported=self.request.data.get( 'canceled_supported', False), user=self.request.user, auth=self.request.auth, type=type, raw_barcode=None, ) except RequiredQuestionsError as e: return Response( { 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data, 'questions': [QuestionSerializer(q).data for q in e.questions] }, status=400) except CheckInError as e: op.order.log_action('pretix.event.checkin.denied', data={ 'position': op.id, 'positionid': op.positionid, 'errorcode': e.code, 'reason_explanation': e.reason, 'force': force, 'datetime': dt, 'type': type, 'list': self.checkinlist.pk }, user=self.request.user, auth=self.request.auth) Checkin.objects.create( position=op, successful=False, error_reason=e.code, error_explanation=e.reason, **common_checkin_args, ) return Response( { 'status': 'error', 'reason': e.code, 'reason_explanation': e.reason, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) else: return Response( { 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=201)