prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  tickets should be decoded on python 3
  As seen from the recent quicket hook posts
  
  TypeError at /tickets/quicket_hook/
  the JSON object must be str, not 'bytes'
  
  
  </issue>
  <code>
  [start of wafer/tickets/views.py]
  1 import json
  2 import logging
  3 
  4 from django.conf import settings
  5 from django.contrib.auth import get_user_model
  6 from django.core.exceptions import PermissionDenied, ValidationError
  7 from django.core.urlresolvers import reverse
  8 from django.http import HttpResponse, Http404
  9 from django.views.decorators.csrf import csrf_exempt
  10 from django.views.decorators.http import require_POST
  11 from django.views.generic.edit import FormView
  12 
  13 from wafer.tickets.models import Ticket, TicketType
  14 from wafer.tickets.forms import TicketForm
  15 
  16 log = logging.getLogger(__name__)
  17 
  18 
  19 class ClaimView(FormView):
  20     template_name = 'wafer.tickets/claim.html'
  21     form_class = TicketForm
  22 
  23     def get_context_data(self, **kwargs):
  24         context = super(ClaimView, self).get_context_data(**kwargs)
  25         context['can_claim'] = self.can_claim()
  26         return context
  27 
  28     def can_claim(self):
  29         if settings.WAFER_REGISTRATION_MODE != 'ticket':
  30             raise Http404('Ticket-based registration is not in use')
  31         if not settings.WAFER_REGISTRATION_OPEN:
  32             return False
  33         return not self.request.user.userprofile.is_registered()
  34 
  35     def form_valid(self, form):
  36         if not self.can_claim():
  37             raise ValidationError('User may not claim a ticket')
  38         ticket = Ticket.objects.get(barcode=form.cleaned_data['barcode'])
  39         ticket.user = self.request.user
  40         ticket.save()
  41         return super(ClaimView, self).form_valid(form)
  42 
  43     def get_success_url(self):
  44         return reverse(
  45             'wafer_user_profile', args=(self.request.user.username,))
  46 
  47 
  48 @csrf_exempt
  49 @require_POST
  50 def quicket_hook(request):
  51     '''
  52     Quicket.co.za can POST something like this when tickets are bought:
  53     {
  54       "reference": "REF00123456",
  55       "event_id": 123,
  56       "event_name": "My Big Event",
  57       "amount": 0.00,
  58       "email": "demo@example.com",
  59       "action": "checkout_started",
  60       // Options are "checkout_started","checkout_cancelled","eft_pending",
  61       //             "checkout_completed"
  62       "tickets": [
  63         {
  64           "id": 122,
  65           "attendee_name": "",
  66           "attendee_email": "",
  67           "ticket_type": "Free Ticket",
  68           "price": 0.00,
  69           "barcode": 12345,
  70         },
  71         ...
  72       ],
  73     }
  74     '''
  75     if request.GET.get('secret') != settings.WAFER_TICKETS_SECRET:
  76         raise PermissionDenied('Incorrect secret')
  77 
  78     payload = json.load(request)
  79     for ticket in payload['tickets']:
  80         import_ticket(ticket['barcode'], ticket['ticket_type'],
  81                       ticket['attendee_email'])
  82 
  83     return HttpResponse("Noted\n", content_type='text/plain')
  84 
  85 
  86 def import_ticket(ticket_barcode, ticket_type, email):
  87     if Ticket.objects.filter(barcode=ticket_barcode).exists():
  88         log.debug('Ticket already registered: %s', ticket_barcode)
  89         return
  90 
  91     # truncate long ticket type names to length allowed by database
  92     ticket_type = ticket_type[:TicketType.MAX_NAME_LENGTH]
  93     type_, created = TicketType.objects.get_or_create(name=ticket_type)
  94 
  95     UserModel = get_user_model()
  96 
  97     try:
  98         user = UserModel.objects.get(email=email, ticket=None)
  99     except UserModel.DoesNotExist:
  100         user = None
  101     except UserModel.MultipleObjectsReturned:
  102         # We're can't uniquely identify the user to associate this ticket
  103         # with, so leave it for them to figure out via the 'claim ticket'
  104         # interface
  105         user = None
  106 
  107     ticket = Ticket.objects.create(
  108         barcode=ticket_barcode,
  109         email=email,
  110         type=type_,
  111         user=user,
  112     )
  113     ticket.save()
  114 
  115     if user:
  116         log.info('Ticket registered: %s and linked to user', ticket)
  117     else:
  118         log.info('Ticket registered: %s. Unclaimed', ticket)
  119
  [end of wafer/tickets/views.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/wafer/tickets/views.py b/wafer/tickets/views.py
  --- a/wafer/tickets/views.py
  +++ b/wafer/tickets/views.py
  @@ -75,7 +75,8 @@
       if request.GET.get('secret') != settings.WAFER_TICKETS_SECRET:
           raise PermissionDenied('Incorrect secret')
   
  -    payload = json.load(request)
  +    # This is required for python 3, and in theory fine on python 2
  +    payload = json.loads(request.body.decode('utf8'))
       for ticket in payload['tickets']:
           import_ticket(ticket['barcode'], ticket['ticket_type'],
                         ticket['attendee_email'])
  
verification_info: {"golden_diff": "diff --git a/wafer/tickets/views.py b/wafer/tickets/views.py\n--- a/wafer/tickets/views.py\n+++ b/wafer/tickets/views.py\n@@ -75,7 +75,8 @@\n     if request.GET.get('secret') != settings.WAFER_TICKETS_SECRET:\n         raise PermissionDenied('Incorrect secret')\n \n-    payload = json.load(request)\n+    # This is required for python 3, and in theory fine on python 2\n+    payload = json.loads(request.body.decode('utf8'))\n     for ticket in payload['tickets']:\n         import_ticket(ticket['barcode'], ticket['ticket_type'],\n                       ticket['attendee_email'])\n", "issue": "tickets should be decoded on python 3\nAs seen from the recent quicket hook posts\n\nTypeError at /tickets/quicket_hook/\nthe JSON object must be str, not 'bytes'\n\n", "before_files": [{"content": "import json\nimport logging\n\nfrom django.conf import settings\nfrom django.contrib.auth import get_user_model\nfrom django.core.exceptions import PermissionDenied, ValidationError\nfrom django.core.urlresolvers import reverse\nfrom django.http import HttpResponse, Http404\nfrom django.views.decorators.csrf import csrf_exempt\nfrom django.views.decorators.http import require_POST\nfrom django.views.generic.edit import FormView\n\nfrom wafer.tickets.models import Ticket, TicketType\nfrom wafer.tickets.forms import TicketForm\n\nlog = logging.getLogger(__name__)\n\n\nclass ClaimView(FormView):\n    template_name = 'wafer.tickets/claim.html'\n    form_class = TicketForm\n\n    def get_context_data(self, **kwargs):\n        context = super(ClaimView, self).get_context_data(**kwargs)\n        context['can_claim'] = self.can_claim()\n        return context\n\n    def can_claim(self):\n        if settings.WAFER_REGISTRATION_MODE != 'ticket':\n            raise Http404('Ticket-based registration is not in use')\n        if not settings.WAFER_REGISTRATION_OPEN:\n            return False\n        return not self.request.user.userprofile.is_registered()\n\n    def form_valid(self, form):\n        if not self.can_claim():\n            raise ValidationError('User may not claim a ticket')\n        ticket = Ticket.objects.get(barcode=form.cleaned_data['barcode'])\n        ticket.user = self.request.user\n        ticket.save()\n        return super(ClaimView, self).form_valid(form)\n\n    def get_success_url(self):\n        return reverse(\n            'wafer_user_profile', args=(self.request.user.username,))\n\n\n@csrf_exempt\n@require_POST\ndef quicket_hook(request):\n    '''\n    Quicket.co.za can POST something like this when tickets are bought:\n    {\n      \"reference\": \"REF00123456\",\n      \"event_id\": 123,\n      \"event_name\": \"My Big Event\",\n      \"amount\": 0.00,\n      \"email\": \"demo@example.com\",\n      \"action\": \"checkout_started\",\n      // Options are \"checkout_started\",\"checkout_cancelled\",\"eft_pending\",\n      //             \"checkout_completed\"\n      \"tickets\": [\n        {\n          \"id\": 122,\n          \"attendee_name\": \"\",\n          \"attendee_email\": \"\",\n          \"ticket_type\": \"Free Ticket\",\n          \"price\": 0.00,\n          \"barcode\": 12345,\n        },\n        ...\n      ],\n    }\n    '''\n    if request.GET.get('secret') != settings.WAFER_TICKETS_SECRET:\n        raise PermissionDenied('Incorrect secret')\n\n    payload = json.load(request)\n    for ticket in payload['tickets']:\n        import_ticket(ticket['barcode'], ticket['ticket_type'],\n                      ticket['attendee_email'])\n\n    return HttpResponse(\"Noted\\n\", content_type='text/plain')\n\n\ndef import_ticket(ticket_barcode, ticket_type, email):\n    if Ticket.objects.filter(barcode=ticket_barcode).exists():\n        log.debug('Ticket already registered: %s', ticket_barcode)\n        return\n\n    # truncate long ticket type names to length allowed by database\n    ticket_type = ticket_type[:TicketType.MAX_NAME_LENGTH]\n    type_, created = TicketType.objects.get_or_create(name=ticket_type)\n\n    UserModel = get_user_model()\n\n    try:\n        user = UserModel.objects.get(email=email, ticket=None)\n    except UserModel.DoesNotExist:\n        user = None\n    except UserModel.MultipleObjectsReturned:\n        # We're can't uniquely identify the user to associate this ticket\n        # with, so leave it for them to figure out via the 'claim ticket'\n        # interface\n        user = None\n\n    ticket = Ticket.objects.create(\n        barcode=ticket_barcode,\n        email=email,\n        type=type_,\n        user=user,\n    )\n    ticket.save()\n\n    if user:\n        log.info('Ticket registered: %s and linked to user', ticket)\n    else:\n        log.info('Ticket registered: %s. Unclaimed', ticket)\n", "path": "wafer/tickets/views.py"}]}