prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  File upload for Indicator Period Data requires Django login
  When uploading a photo/file to the /rest/v1/indicator_period_data/nnn/upload_file/ endpoint, a Django login is required, which is not the case for the rest of the API; an API key should be sufficient.
  
  
  </issue>
  <code>
  [start of akvo/rest/views/indicator_period_data.py]
  1 # -*- coding: utf-8 -*-
  2 
  3 # Akvo RSR is covered by the GNU Affero General Public License.
  4 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  5 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  6 
  7 
  8 from akvo.rsr.models import IndicatorPeriodData, IndicatorPeriodDataComment
  9 
  10 from ..serializers import (IndicatorPeriodDataSerializer, IndicatorPeriodDataFrameworkSerializer,
  11                            IndicatorPeriodDataCommentSerializer)
  12 from ..viewsets import PublicProjectViewSet
  13 
  14 from rest_framework.decorators import api_view, permission_classes
  15 from rest_framework.permissions import IsAuthenticated
  16 from rest_framework.response import Response
  17 
  18 
  19 class IndicatorPeriodDataViewSet(PublicProjectViewSet):
  20     """
  21     """
  22     queryset = IndicatorPeriodData.objects.all()
  23     serializer_class = IndicatorPeriodDataSerializer
  24     filter_fields = {
  25         'period': ['exact'],
  26         'period__indicator': ['exact'],
  27         'period__indicator__result': ['exact'],
  28         'period__indicator__result__project': ['exact'],
  29         'user': ['exact'],
  30         'status': ['exact'],
  31         'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  32         'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  33         'relative_data': ['exact'],
  34         'data': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  35         'period_actual_value': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  36         'update_method': ['exact']
  37     }
  38 
  39     project_relation = 'period__indicator__result__project__'
  40 
  41 
  42 class IndicatorPeriodDataFrameworkViewSet(PublicProjectViewSet):
  43     """
  44     """
  45     queryset = IndicatorPeriodData.objects.all()
  46     serializer_class = IndicatorPeriodDataFrameworkSerializer
  47     filter_fields = {
  48         'period': ['exact'],
  49         'period__indicator': ['exact'],
  50         'period__indicator__result': ['exact'],
  51         'period__indicator__result__project': ['exact'],
  52         'user': ['exact'],
  53         'status': ['exact'],
  54         'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  55         'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  56         'relative_data': ['exact'],
  57         'data': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  58         'period_actual_value': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  59         'update_method': ['exact']
  60     }
  61     project_relation = 'period__indicator__result__project__'
  62 
  63 
  64 class IndicatorPeriodDataCommentViewSet(PublicProjectViewSet):
  65     """
  66     """
  67     queryset = IndicatorPeriodDataComment.objects.all()
  68     serializer_class = IndicatorPeriodDataCommentSerializer
  69     filter_fields = {
  70         'data': ['exact'],
  71         'data__period': ['exact'],
  72         'data__period__indicator': ['exact'],
  73         'data__period__indicator__result': ['exact'],
  74         'data__period__indicator__result__project': ['exact'],
  75         'user': ['exact'],
  76         'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
  77         'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ]
  78     }
  79     project_relation = 'period__indicator__result__project__'
  80 
  81 
  82 @api_view(['POST'])
  83 @permission_classes((IsAuthenticated, ))
  84 def indicator_upload_file(request, pk=None):
  85     """
  86     Special API call for directly uploading a file.
  87 
  88     :param request; A Django request object.
  89     :param pk; The primary key of an IndicatorPeriodData instance.
  90     """
  91     update = IndicatorPeriodData.objects.get(pk=pk)
  92     upload_file = request.FILES['file']
  93 
  94     # TODO: Permissions
  95     # user = request.user
  96 
  97     file_type = request.POST.copy()['type']
  98     if file_type == 'photo':
  99         update.photo = upload_file
  100         update.save(update_fields=['photo'])
  101         return Response({'file': update.photo.url})
  102     elif file_type == 'file':
  103         update.file = upload_file
  104         update.save(update_fields=['file'])
  105         return Response({'file': update.file.url})
  106 
  107     # TODO: Error response
  108     return Response({})
  109
  [end of akvo/rest/views/indicator_period_data.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/akvo/rest/views/indicator_period_data.py b/akvo/rest/views/indicator_period_data.py
  --- a/akvo/rest/views/indicator_period_data.py
  +++ b/akvo/rest/views/indicator_period_data.py
  @@ -11,8 +11,10 @@
                              IndicatorPeriodDataCommentSerializer)
   from ..viewsets import PublicProjectViewSet
   
  +from django.http import HttpResponseForbidden
  +
  +from rest_framework import status
   from rest_framework.decorators import api_view, permission_classes
  -from rest_framework.permissions import IsAuthenticated
   from rest_framework.response import Response
   
   
  @@ -80,7 +82,6 @@
   
   
   @api_view(['POST'])
  -@permission_classes((IsAuthenticated, ))
   def indicator_upload_file(request, pk=None):
       """
       Special API call for directly uploading a file.
  @@ -91,18 +92,24 @@
       update = IndicatorPeriodData.objects.get(pk=pk)
       upload_file = request.FILES['file']
   
  -    # TODO: Permissions
  -    # user = request.user
  -
  -    file_type = request.POST.copy()['type']
  -    if file_type == 'photo':
  -        update.photo = upload_file
  -        update.save(update_fields=['photo'])
  -        return Response({'file': update.photo.url})
  -    elif file_type == 'file':
  -        update.file = upload_file
  -        update.save(update_fields=['file'])
  -        return Response({'file': update.file.url})
  -
  -    # TODO: Error response
  -    return Response({})
  +    # Permissions
  +    user = getattr(request, 'user', None)
  +    if not user:
  +        return Response({'error': 'User is not logged in'}, status=status.HTTP_403_FORBIDDEN)
  +
  +    if not user.has_perm('rsr.change_project', update.period.indicator.result.project):
  +        return Response({'error': 'User has no permission to place an update'},
  +                        status=status.HTTP_403_FORBIDDEN)
  +
  +    try:
  +        file_type = request.POST.copy()['type']
  +        if file_type == 'photo':
  +            update.photo = upload_file
  +            update.save(update_fields=['photo'])
  +            return Response({'file': update.photo.url})
  +        elif file_type == 'file':
  +            update.file = upload_file
  +            update.save(update_fields=['file'])
  +            return Response({'file': update.file.url})
  +    except Exception as e:
  +        return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
  
verification_info: {"golden_diff": "diff --git a/akvo/rest/views/indicator_period_data.py b/akvo/rest/views/indicator_period_data.py\n--- a/akvo/rest/views/indicator_period_data.py\n+++ b/akvo/rest/views/indicator_period_data.py\n@@ -11,8 +11,10 @@\n                            IndicatorPeriodDataCommentSerializer)\n from ..viewsets import PublicProjectViewSet\n \n+from django.http import HttpResponseForbidden\n+\n+from rest_framework import status\n from rest_framework.decorators import api_view, permission_classes\n-from rest_framework.permissions import IsAuthenticated\n from rest_framework.response import Response\n \n \n@@ -80,7 +82,6 @@\n \n \n @api_view(['POST'])\n-@permission_classes((IsAuthenticated, ))\n def indicator_upload_file(request, pk=None):\n     \"\"\"\n     Special API call for directly uploading a file.\n@@ -91,18 +92,24 @@\n     update = IndicatorPeriodData.objects.get(pk=pk)\n     upload_file = request.FILES['file']\n \n-    # TODO: Permissions\n-    # user = request.user\n-\n-    file_type = request.POST.copy()['type']\n-    if file_type == 'photo':\n-        update.photo = upload_file\n-        update.save(update_fields=['photo'])\n-        return Response({'file': update.photo.url})\n-    elif file_type == 'file':\n-        update.file = upload_file\n-        update.save(update_fields=['file'])\n-        return Response({'file': update.file.url})\n-\n-    # TODO: Error response\n-    return Response({})\n+    # Permissions\n+    user = getattr(request, 'user', None)\n+    if not user:\n+        return Response({'error': 'User is not logged in'}, status=status.HTTP_403_FORBIDDEN)\n+\n+    if not user.has_perm('rsr.change_project', update.period.indicator.result.project):\n+        return Response({'error': 'User has no permission to place an update'},\n+                        status=status.HTTP_403_FORBIDDEN)\n+\n+    try:\n+        file_type = request.POST.copy()['type']\n+        if file_type == 'photo':\n+            update.photo = upload_file\n+            update.save(update_fields=['photo'])\n+            return Response({'file': update.photo.url})\n+        elif file_type == 'file':\n+            update.file = upload_file\n+            update.save(update_fields=['file'])\n+            return Response({'file': update.file.url})\n+    except Exception as e:\n+        return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)\n", "issue": "File upload for Indicator Period Data requires Django login\nWhen uploading a photo/file to the /rest/v1/indicator_period_data/nnn/upload_file/ endpoint, a Django login is required, which is not the case for the rest of the API; an API key should be sufficient.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\n\nfrom akvo.rsr.models import IndicatorPeriodData, IndicatorPeriodDataComment\n\nfrom ..serializers import (IndicatorPeriodDataSerializer, IndicatorPeriodDataFrameworkSerializer,\n                           IndicatorPeriodDataCommentSerializer)\nfrom ..viewsets import PublicProjectViewSet\n\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\n\n\nclass IndicatorPeriodDataViewSet(PublicProjectViewSet):\n    \"\"\"\n    \"\"\"\n    queryset = IndicatorPeriodData.objects.all()\n    serializer_class = IndicatorPeriodDataSerializer\n    filter_fields = {\n        'period': ['exact'],\n        'period__indicator': ['exact'],\n        'period__indicator__result': ['exact'],\n        'period__indicator__result__project': ['exact'],\n        'user': ['exact'],\n        'status': ['exact'],\n        'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'relative_data': ['exact'],\n        'data': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'period_actual_value': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'update_method': ['exact']\n    }\n\n    project_relation = 'period__indicator__result__project__'\n\n\nclass IndicatorPeriodDataFrameworkViewSet(PublicProjectViewSet):\n    \"\"\"\n    \"\"\"\n    queryset = IndicatorPeriodData.objects.all()\n    serializer_class = IndicatorPeriodDataFrameworkSerializer\n    filter_fields = {\n        'period': ['exact'],\n        'period__indicator': ['exact'],\n        'period__indicator__result': ['exact'],\n        'period__indicator__result__project': ['exact'],\n        'user': ['exact'],\n        'status': ['exact'],\n        'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'relative_data': ['exact'],\n        'data': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'period_actual_value': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'update_method': ['exact']\n    }\n    project_relation = 'period__indicator__result__project__'\n\n\nclass IndicatorPeriodDataCommentViewSet(PublicProjectViewSet):\n    \"\"\"\n    \"\"\"\n    queryset = IndicatorPeriodDataComment.objects.all()\n    serializer_class = IndicatorPeriodDataCommentSerializer\n    filter_fields = {\n        'data': ['exact'],\n        'data__period': ['exact'],\n        'data__period__indicator': ['exact'],\n        'data__period__indicator__result': ['exact'],\n        'data__period__indicator__result__project': ['exact'],\n        'user': ['exact'],\n        'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],\n        'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ]\n    }\n    project_relation = 'period__indicator__result__project__'\n\n\n@api_view(['POST'])\n@permission_classes((IsAuthenticated, ))\ndef indicator_upload_file(request, pk=None):\n    \"\"\"\n    Special API call for directly uploading a file.\n\n    :param request; A Django request object.\n    :param pk; The primary key of an IndicatorPeriodData instance.\n    \"\"\"\n    update = IndicatorPeriodData.objects.get(pk=pk)\n    upload_file = request.FILES['file']\n\n    # TODO: Permissions\n    # user = request.user\n\n    file_type = request.POST.copy()['type']\n    if file_type == 'photo':\n        update.photo = upload_file\n        update.save(update_fields=['photo'])\n        return Response({'file': update.photo.url})\n    elif file_type == 'file':\n        update.file = upload_file\n        update.save(update_fields=['file'])\n        return Response({'file': update.file.url})\n\n    # TODO: Error response\n    return Response({})\n", "path": "akvo/rest/views/indicator_period_data.py"}]}