From 3259ebb3615768716e4f8dde7c84cb5ca08ae1f0 Mon Sep 17 00:00:00 2001
From: ingo <ingo.leindecker@fro.at>
Date: Mon, 18 Dec 2017 19:31:33 +0100
Subject: [PATCH] More endpoints and improvements.

Problem to solve: there's a mix between API usecases for Playlist-UI and Website.

See #22
---
 program/models.py      |  15 +-
 program/serializers.py | 129 ++++++++++++++++-
 program/views.py       | 312 +++++++++++++++++++----------------------
 pv/settings.py         |   7 -
 pv/urls.py             |  12 +-
 5 files changed, 285 insertions(+), 190 deletions(-)

diff --git a/program/models.py b/program/models.py
index 570169a5..e856f8cd 100644
--- a/program/models.py
+++ b/program/models.py
@@ -255,7 +255,7 @@ class Host(models.Model):
     ppoi = PPOIField('Image PPOI')
     height = models.PositiveIntegerField('Image Height', blank=True, null=True, editable=False)
     width = models.PositiveIntegerField('Image Width', blank=True, null=True,editable=False)
-    image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='user_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
+    image = VersatileImageField(_("Profile picture"), blank=True, null=True, upload_to='host_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload a picture of yourself. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
 
     class Meta:
         ordering = ('name',)
@@ -321,6 +321,19 @@ class Show(models.Model):
     def active_schedules(self):
         return self.schedules.filter(until__gt=date.today())
 
+    def is_editable(self, show_id):
+        """
+        Whether the current user can edit the given show
+        @return boolean
+        """
+        if self.request.user.is_superuser:
+            show_ids = Show.objects.all().values_list('id', flat=True)
+        else:
+            show_ids = self.request.user.shows.all().values_list('id', flat=True)
+
+        return int(show_id) in show_ids
+
+
 
 class RRule(models.Model):
 
diff --git a/program/serializers.py b/program/serializers.py
index 47594149..3b5ce0cb 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -13,8 +13,24 @@ class UserSerializer(serializers.ModelSerializer):
 
     class Meta:
         model = User
-        exclude = ('password',)
-        #fields = '__all__'
+        fields = '__all__'
+
+
+    def create(self, validated_data):
+        """
+        Create and return a new User instance, given the validated data.
+        """
+
+        profile_data = validated_data.pop('profile')
+        user = super(UserSerializer, self).create(validated_data)
+        user.date_joined = datetime.today()
+        user.set_password(validated_data['password'])
+        user.save()
+
+        profile = Profile(user=user, cba_username=profile_data.get('cba_username'), cba_user_token=profile_data.get('cba_user_token'))
+        profile.save()
+
+        return user
 
 
     def update(self, instance, validated_data):
@@ -27,7 +43,11 @@ class UserSerializer(serializers.ModelSerializer):
         instance.email = validated_data.get('email', instance.email)
 
         # TODO: How to hook into this from ProfileSerializer without having to call it here?
-        profile = Profile.objects.get(user=instance.id)
+        try:
+            profile = Profile.objects.get(user=instance.id)
+        except ObjectDoesNotExist:
+            profile = Profile.objects.create(user=instance, **validated_data['profile'])
+
         profile.cba_username = validated_data['profile'].get('cba_username')
         profile.cba_user_token = validated_data['profile'].get('cba_user_token')
         profile.save()
@@ -36,21 +56,39 @@ class UserSerializer(serializers.ModelSerializer):
         return instance
 
 
+
 class CategorySerializer(serializers.ModelSerializer):
     class Meta:
         model = Category
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Category instance, given the validated data.
+        """
+        instance.category = validated_data.get('category', instance.category)
+        instance.abbrev = validated_data.get('abbrev', instance.abbrev)
+        instance.slug = validated_data.get('slug', instance.slug)
+        instance.color = validated_data.get('color', instance.color)
+        instance.description = validated_data.get('description', instance.description)
+
+        instance.save()
+        return instance
+
+
 class HostSerializer(serializers.ModelSerializer):
     class Meta:
         model = Host
         fields = '__all__'
 
+
     def update(self, instance, validated_data):
         """
         Update and return an existing Host instance, given the validated data.
         """
+
+        # TODO: Still put this into a sub app?
         instance.name = validated_data.get('name', instance.name)
         instance.is_always_visible = validated_data.get('is_always_visible', instance.is_always_visible)
         instance.email = validated_data.get('email', instance.email)
@@ -75,30 +113,90 @@ class LanguageSerializer(serializers.ModelSerializer):
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Language instance, given the validated data.
+        """
+        instance.name = validated_data.get('name', instance.name)
+
+        instance.save()
+        return instance
+
+
 class TopicSerializer(serializers.ModelSerializer):
     class Meta:
         model = Topic
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Topic instance, given the validated data.
+        """
+        instance.topic = validated_data.get('topic', instance.topic)
+        instance.abbrev = validated_data.get('abbrev', instance.abbrev)
+        instance.slug = validated_data.get('slug', instance.slug)
+
+        instance.save()
+        return instance
+
+
 class MusicFocusSerializer(serializers.ModelSerializer):
     class Meta:
         model = MusicFocus
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing MusicFocus instance, given the validated data.
+        """
+        instance.focus = validated_data.get('focus', instance.focus)
+        instance.abbrev = validated_data.get('abbrev', instance.abbrev)
+        instance.slug = validated_data.get('slug', instance.slug)
+
+        instance.save()
+        return instance
+
+
 class TypeSerializer(serializers.ModelSerializer):
     class Meta:
         model = Type
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Type instance, given the validated data.
+        """
+        instance.type = validated_data.get('type', instance.type)
+        instance.slug = validated_data.get('slug', instance.slug)
+        instance.color = validated_data.get('color', instance.color)
+        instance.text_color = validated_data.get('text_color', instance.text_color)
+        instance.enabled = validated_data.get('enabled', instance.enabled)
+
+        instance.save()
+        return instance
+
+
 class RTRCategorySerializer(serializers.ModelSerializer):
     class Meta:
         model = RTRCategory
         fields = '__all__'
 
 
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing RTRCategory instance, given the validated data.
+        """
+        instance.rtrcategory = validated_data.get('rtrcategory', instance.rtrcategory)
+        instance.abbrev = validated_data.get('abbrev', instance.abbrev)
+        instance.slug = validated_data.get('slug', instance.slug)
+
+        instance.save()
+        return instance
+
+
 '''
 class OwnersSerializer(serializers.ModelSerializer):
     class Meta:
@@ -106,6 +204,7 @@ class OwnersSerializer(serializers.ModelSerializer):
         fields = '__all__'
 '''
 
+
 class ShowSerializer(serializers.HyperlinkedModelSerializer):
     category = CategorySerializer(many=True)
     hosts = HostSerializer(many=True)
@@ -143,16 +242,36 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
         instance.website = validated_data.get('website', instance.website)
         instance.cba_series_id = validated_data.get('cba_series_id', instance.cba_series_id)
         instance.fallback_pool = validated_data.get('fallback_pool', instance.fallback_pool)
+
         instance.save()
         return instance
 
 
+# TODO: collision detection
+class ScheduleSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Schedule
+        fields = '__all__'
+
+    def create(self, validated_data):
+        """
+        Create and return a new Schedule instance, given the validated data.
+        """
+        return Schedule.objects.create(**validated_data)
+
+
+    def update(self, instance, validated_data):
+        """
+        Update and return an existing Schedule instance, given the validated data.
+        """
+        return instance
+
+
 class TimeSlotSerializer(serializers.ModelSerializer):
     class Meta:
         model = TimeSlot
         fields = '__all__'
 
-
     def create(self, validated_data):
         """
         Create and return a new TimeSlot instance, given the validated data.
@@ -177,7 +296,6 @@ class NoteSerializer(serializers.ModelSerializer):
         model = Note
         fields = '__all__'
 
-
     def create(self, validated_data):
         """
         Create and return a new Note instance, given the validated data.
@@ -200,5 +318,6 @@ class NoteSerializer(serializers.ModelSerializer):
         instance.image = validated_data.get('image', instance.image)
         instance.status = validated_data.get('status', instance.status)
         instance.cba_id = validated_data.get('cba_id', instance.cba_id)
+
         instance.save()
         return instance
\ No newline at end of file
diff --git a/program/views.py b/program/views.py
index 85f468dc..001ee661 100644
--- a/program/views.py
+++ b/program/views.py
@@ -14,13 +14,13 @@ from django.views.generic.list import ListView
 from rest_framework import permissions, serializers, status, viewsets
 from rest_framework.views import APIView
 from rest_framework.response import Response
-
-from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
+from rest_framework.decorators import detail_route
 
 from program.models import Type, MusicFocus, Language, Note, Show, Category, RTRCategory, Topic, TimeSlot, Host, Schedule
-from program.serializers import TypeSerializer, LanguageSerializer, MusicFocusSerializer, NoteSerializer, ShowSerializer, CategorySerializer, RTRCategorySerializer, TopicSerializer, TimeSlotSerializer, HostSerializer, UserSerializer
+from program.serializers import TypeSerializer, LanguageSerializer, MusicFocusSerializer, NoteSerializer, ShowSerializer, ScheduleSerializer, CategorySerializer, RTRCategorySerializer, TopicSerializer, TimeSlotSerializer, HostSerializer, UserSerializer
 from program.utils import tofirstdayinisoweek, get_cached_shows
 
+
 class CalendarView(TemplateView):
     template_name = 'calendar.html'
 
@@ -320,44 +320,6 @@ def json_timeslots_specials(request):
                         content_type="application/json; charset=utf-8")
 
 
-def json_get_timeslots_by_show(request):
-    '''
-    Returns a JSON object of timeslots of a given show from 4 weeks ago until 12 weeks in the future
-    Called by /api/v1/timeslots/?show_id=1 to populate a timeslot-select for being assigned to a note
-    '''
-
-    if not request.user.is_authenticated():
-        return JsonResponse(_('Permission denied.'))
-
-    if request.method == 'GET' and request.GET.get('show_id') != None:
-
-        four_weeks_ago = datetime.now() - timedelta(weeks=4)
-        in_twelve_weeks = datetime.now() + timedelta(weeks=12)
-
-        timeslots = []
-        saved_timeslot_id = 0 if request.GET.get('timeslot_id') == None else int(request.GET.get('timeslot_id'))
-
-        # If the saved timeslot is part of the currently selected show,
-        # include it as the first select-option in order not to lose it if it's past
-        if saved_timeslot_id > 0:
-            try:
-                saved_timeslot = TimeSlot.objects.get(pk=int(request.GET.get('timeslot_id')),show=int(request.GET.get('show_id')))
-                timeslots.append( { 'timeslot': str(saved_timeslot), 'timeslot_id': saved_timeslot.id, 'start': saved_timeslot.start, 'end': saved_timeslot.end } )
-            except ObjectDoesNotExist:
-                pass
-
-        for timeslot in TimeSlot.objects.filter(show=int(request.GET.get('show_id')),
-                                                start__gt=four_weeks_ago,
-                                                start__lt=in_twelve_weeks).exclude(pk=saved_timeslot_id):
-
-            timeslots.append( { 'timeslot': str(timeslot), 'timeslot_id' : timeslot.id, 'start': timeslot.start, 'end': timeslot.end } )
-
-        return JsonResponse( timeslots, safe=False )
-
-    else:
-        return JsonResponse( _('No show_id given.'), safe=False )
-
-
 
 
 ####################################################################
@@ -373,39 +335,36 @@ class APIUserViewSet(viewsets.ModelViewSet):
     Superusers may access and update all users
     """
 
-    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions] #, TokenHasReadWriteScope]
+    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions]
     serializer_class = UserSerializer
-    queryset = User.objects.all()
+    queryset = User.objects.none()
     required_scopes = ['users']
 
-    def list(self, request):
-        # Commons users only see themselves
-        if request.user.is_superuser:
-            users = User.objects.all()
+
+    def get_queryset(self):
+        """Constrain access to oneself except for superusers"""
+        if self.request.user.is_superuser:
+            return User.objects.all()
         else:
-            users = User.objects.filter(pk=request.user.id)
+            return User.objects.filter(pk=self.request.user.id)
+
 
+    def list(self, request):
+        users = self.get_queryset()
         serializer = UserSerializer(users, many=True)
         return Response(serializer.data)
 
 
     def retrieve(self, request, pk=None):
         """Returns a single user"""
-        if pk != None:
-            pk = int(pk)
-            try:
-                user = User.objects.get(pk=pk)
-            except ObjectDoesNotExist:
-                return Response(status=status.HTTP_404_NOT_FOUND)
-
-            # Common users may only see themselves
-            if not request.user.is_superuser and user.id != request.user.id:
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
+        user = get_object_or_404(User, pk=pk)
 
-            serializer = UserSerializer(user)
-            return Response(serializer.data)
+        # Common users may only see themselves
+        if not request.user.is_superuser and user.id != request.user.id:
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-        return Response(status=status.HTTP_400_BAD_REQUEST)
+        serializer = UserSerializer(user)
+        return Response(serializer.data)
 
 
     def partial_update(self, request, pk=None):
@@ -423,29 +382,76 @@ class APIUserViewSet(viewsets.ModelViewSet):
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
 
+
+
 class APIShowViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/shows/  Returns shows a user owns
-    /api/v1/shows/1 Used for retrieving a single show or update (if owned)
+    /api/v1/shows/                                             Returns shows a user owns
+    /api/v1/shows/?active=true                                 Returns all active shows (no matter if owned by user)
+    /api/v1/shows/1                                            Used for retrieving a single show or update (if owned)
+    /api/v1/shows/1/schedules                                  Returns all schedules for the given show
+    /api/v1/shows/1/timeslots                                  Returns all timeslots for the given show
+    /api/v1/shows/1/timeslots?start=2017-01-01&end=2017-12-31  Returns all timeslots for the given show
 
     Superusers may access and update all shows
     """
 
-    queryset = Show.objects.all()
+    queryset = Show.objects.none()
     serializer_class = ShowSerializer
-    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions] #, TokenHasReadWriteScope]
+    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions]
     required_scopes = ['shows']
 
 
-    def list(self, request):
-        """Lists all shows"""
+    def get_queryset(self):
+        """Constrain access to owners except for superusers"""
+
+        if self.request.GET.get('active') == 'true':
+            '''Filter for retrieving currently running shows'''
+
+            # Get currently running schedules to filter by
+            # For single dates we test if there'll be one in the future (and ignore the until date)
+            # TODO: Really consider dstart? (=currently active, not just upcoming ones)
+            schedules = Schedule.objects.filter( Q(rrule_id__gt=1,dstart__lte=date.today(),until__gte=date.today()) |
+                                             Q(rrule_id=1,dstart__gte=date.today()) ).distinct().values_list('show_id', flat=True)
+
+            return Show.objects.filter(id__in=schedules)
 
-        # Commons users only see shows they own
-        if request.user.is_superuser:
-            shows = Show.objects.all()
+        if self.request.user.is_superuser:
+            return Show.objects.all()
         else:
-            shows = request.user.shows.all()
+            return self.request.user.shows.all()
 
+
+    @detail_route(methods=['get'], url_path='schedules')
+    def schedules(self, request, pk=None):
+        """Return all schedules of the show"""
+        show = get_object_or_404(Show, pk=pk)
+        schedules = Schedule.objects.filter(show=show)
+        serializer = ScheduleSerializer(schedules, many=True)
+        return Response(serializer.data)
+
+
+    @detail_route(methods=['get'], url_path='timeslots')
+    def timeslots(self, request, pk=None):
+        """Return timeslots of the show for the next 60 days or the given timerange"""
+        show = get_object_or_404(Show, pk=pk)
+
+        # Return next 60 days by default
+        start = datetime.combine(date.today(), time(0, 0))
+        end = start + timedelta(days=60)
+
+        if self.request.GET.get('start') and self.request.GET.get('end'):
+            start = datetime.combine( datetime.strptime(self.request.GET.get('start'), '%Y-%m-%d').date(), time(0, 0))
+            end = datetime.combine( datetime.strptime(self.request.GET.get('end'), '%Y-%m-%d').date(), time(23, 59))
+
+        timeslots = TimeSlot.objects.filter(show=show, start__gte=start, end__lte=end).order_by('start')
+        serializer = TimeSlotSerializer(timeslots, many=True)
+        return Response(serializer.data)
+
+
+    def list(self, request):
+        """Lists shows"""
+        shows = self.get_queryset()
         serializer = ShowSerializer(shows, many=True)
         return Response(serializer.data)
 
@@ -457,30 +463,16 @@ class APIShowViewSet(viewsets.ModelViewSet):
 
     def retrieve(self, request, pk=None):
         """Returns a single show"""
-
-        if pk != None and int(pk):
-            pk = int(pk)
-
-            # Common users may only retrieve shows they own
-            if not request.user.is_superuser and pk not in list(request.user.shows.all().values_list('id', flat=True)):
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
-
-            try:
-                show = Show.objects.get(pk=pk)
-            except ObjectDoesNotExist:
-                return Response(status=status.HTTP_404_NOT_FOUND)
-
-            serializer = ShowSerializer(show)
-            return Response(serializer.data)
-
-        return Response(status=status.HTTP_400_BAD_REQUEST)
+        show = get_object_or_404(Show, pk=pk)
+        serializer = ShowSerializer(show)
+        return Response(serializer.data)
 
 
     def partial_update(self, request, pk=None):
         serializer = ShowSerializer(data=request.data)
 
         # For common user and not owner of show: Permission denied
-        if not request.user.is_superuser and int(pk) not in list(request.user.shows.all().values_list('id', flat=True)):
+        if not Show.is_editable(self, pk):
             return Response(serializer.initial_data, status=status.HTTP_401_UNAUTHORIZED)
 
         if serializer.is_valid():
@@ -490,52 +482,51 @@ class APIShowViewSet(viewsets.ModelViewSet):
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
 
-    def destroy(self, request, pk=None):
-        '''Deleting is not allowed at the moment'''
-        return Response(status=status.HTTP_401_UNAUTHORIZED)
-
-
 class APITimeSlotViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/timeslots                                            Returns nothing
+    /api/v1/timeslots                                            Returns timeslots of the next 60 days
+    /api/v1/timeslots/?start=2017-01-01&end=2017-02-01           Returns timeslots within the given timerange
     /api/v1/timeslots/?show_id=1                                 Returns upcoming timeslots of a show 60 days in the future
     /api/v1/timeslots/?show_id=1&start=2017-01-01&end=2017-02-01 Returns timeslots of a show within the given timerange
-
-    TODO: Test for permissions to show
     """
 
-    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions] #, TokenHasReadWriteScope]
+    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions]
     serializer_class = TimeSlotSerializer
-    queryset = TimeSlot.objects.all()
+    queryset = TimeSlot.objects.none()
     required_scopes = ['timeslots']
 
 
-    def list(self, request):
-        """Lists timeslots of a show"""
+    def get_queryset(self):
+        # Return next 60 days by default
+        start = datetime.combine(date.today(), time(0, 0))
+        end = start + timedelta(days=60)
 
-        if request.GET.get('show_id') != None:
-            show_id = int(request.GET.get('show_id'))
+        if self.request.GET.get('start') and self.request.GET.get('end'):
+            start = datetime.combine( datetime.strptime(self.request.GET.get('start'), '%Y-%m-%d').date(), time(0, 0))
+            end = datetime.combine( datetime.strptime(self.request.GET.get('end'), '%Y-%m-%d').date(), time(23, 59))
 
-            if not request.user.is_superuser and show_id not in list(request.user.shows.all().values_list('id', flat=True)):
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
+        # If show is given: Return corresponding timeslots
+        if self.request.GET.get('show_id') != None:
+            show_id = int(self.request.GET.get('show_id'))
 
-            # Return next 60 days by default
-            start = datetime.combine(date.today(), time(0, 0))
-            end = start + timedelta(days=60)
+            if not Show.is_editable(self, show_id):
+                return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-            if request.GET.get('start') and request.GET.get('end'):
-                start = datetime.combine( datetime.strptime(request.GET.get('start'), '%Y-%m-%d').date(), time(0, 0))
-                end = datetime.combine( datetime.strptime(request.GET.get('end'), '%Y-%m-%d').date(), time(23, 59))
+            return TimeSlot.objects.filter(show=show_id, start__gte=start, end__lte=end).order_by('start')
 
-            timeslots = TimeSlot.objects.filter(show=show_id, start__gte=start, end__lte=end).order_by('start')
-            serializer = TimeSlotSerializer(timeslots, many=True)
+        # Otherwise return all show timeslots
+        return TimeSlot.objects.filter(start__gte=start, end__lte=end).order_by('start')
 
-            return Response(serializer.data)
 
-        return Response(status=status.HTTP_400_BAD_REQUEST)
+    def list(self, request):
+        """Lists timeslots of a show"""
+        timeslots = self.get_queryset()
+        serializer = TimeSlotSerializer(timeslots, many=True)
+        return Response(serializer.data)
 
 
     def create(self, request):
+        """Timeslots may only be created by adding/updating schedules"""
         return Response(status=HTTP_401_UNAUTHORIZED)
 
 
@@ -550,40 +541,41 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
 
-    def destroy(self, request, pk=None):
-        """Deleting is not allowed at the moment"""
-        return Response(status=status.HTTP_401_UNAUTHORIZED)
-
-
 
 class APINoteViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/notes/                Returns nothing
+    /api/v1/notes/                Returns all notes the user owns
     /ap1/v1/notes/1               Returns a single note (if owned)
     /api/v1/notes/?ids=1,2,3,4,5  Returns given notes (if owned)
 
     Superusers may access and update all notes
     """
 
-    queryset = Note.objects.all()
+    queryset = Note.objects.none()
     serializer_class = NoteSerializer
-    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions] #, TokenHasReadWriteScope]
+    permission_classes = [permissions.IsAuthenticated, permissions.DjangoModelPermissions]
     required_scopes = ['notes']
 
 
-    def list(self, request):
-        """Lists notes"""
+    def get_queryset(self):
 
-        if request.GET.get('ids') != None:
-            note_ids = request.GET.get('ids').split(',')
-            if request.user.is_superuser:
+        # TODO: Should users be able to edit other's notes if they're part of a show they own?
+        if self.request.GET.get('ids') != None:
+            note_ids = self.request.GET.get('ids').split(',')
+            if self.request.user.is_superuser:
                 notes = Note.objects.filter(id__in=note_ids)
             else:
                 # Common users only retrieve notes they own
-                notes = Note.objects.filter(id__in=note_ids,user=request.user.id)
+                notes = Note.objects.filter(id__in=note_ids,user=self.request.user.id)
         else:
-           notes = Note.objects.none()
+           notes = Note.objects.filter(user=self.request.user.id)
+
+        return notes
 
+
+    def list(self, request):
+        """Lists notes"""
+        notes = self.get_queryset()
         serializer = NoteSerializer(notes, many=True)
         return Response(serializer.data)
 
@@ -591,13 +583,15 @@ class APINoteViewSet(viewsets.ModelViewSet):
     def create(self, request):
         """
         Creates a note
-        TODO: Test!
         """
 
         # Only create a note if show_id and timeslot_id is given
-        if not int(validated_data.get('show_id')) and not int(validated_data.get('timeslot_id')):
+        if request.POST.get('show') == None or request.POST.get('timeslot') == None:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
+        if not Show.is_editable(self, request.POST.get('show')):
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
+
         serializer = NoteSerializer(data=request.data)
         if serializer.is_valid():
             serializer.save()
@@ -609,56 +603,38 @@ class APINoteViewSet(viewsets.ModelViewSet):
     def retrieve(self, request, pk=None):
         """Returns a single note"""
 
-        if pk != None:
-            try:
-                note = Note.objects.get(pk=pk)
-            except ObjectDoesNotExist:
-                return Response(status=status.HTTP_404_NOT_FOUND)
+        note = get_object_or_404(Note, pk=pk)
 
-            if not request.user.is_superuser and note.user_id != request.user.id:
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
+        if not request.user.is_superuser and int(pk) not in self.get_queryset().values_list('id', flat=True):
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-            serializer = NoteSerializer(note)
-            return Response(serializer.data)
-
-        return Response(status=status.HTTP_400_BAD_REQUEST)
+        serializer = NoteSerializer(note)
+        return Response(serializer.data)
 
 
     def partial_update(self, request, pk=None):
-        if pk != None:
-            pk = int(pk)
-            try:
-                note = Note.objects.get(pk=pk)
-            except ObjectDoesNotExist:
-                return Response(status=status.HTTP_404_NOT_FOUND)
-
-            if not request.user.is_superuser and note.user_id != request.user.id:
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
+        note = get_object_or_404(Note, pk=pk)
 
-            serializer = NoteSerializer(data=request.data)
+        if not request.user.is_superuser and note.user_id != request.user.id:
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-            if serializer.is_valid():
-                serializer.save();
-                return Response(serializer.data)
+        serializer = NoteSerializer(data=request.data)
+
+        if serializer.is_valid():
+            serializer.save();
+            return Response(serializer.data)
 
         return Response(status=status.HTTP_400_BAD_REQUEST)
 
 
     def destroy(self, request, pk=None):
-        if pk != None:
-            pk = int(pk)
-            try:
-                note = Note.objects.get(pk=pk)
-            except ObjectDoesNotExist:
-                return Response(status.HTTP_404_NOT_FOUND)
-
-            if not request.user.is_superuser and note.user_id != request.user.id:
-                return Response(status=status.HTTP_401_UNAUTHORIZED)
+        note = get_object_or_404(Note, pk=pk)
 
-            Note.objects.delete(pk=pk)
-            return Response(status=status.HTTP_204_NO_CONTENT)
+        if not request.user.is_superuser and note.user_id != request.user.id:
+            return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-        return Response(status=status.HTTP_400_BAD_REQUEST)
+        Note.objects.delete(pk=pk)
+        return Response(status=status.HTTP_204_NO_CONTENT)
 
 
 class APICategoryViewSet(viewsets.ModelViewSet):
diff --git a/pv/settings.py b/pv/settings.py
index 06595ae1..74d3a2c8 100644
--- a/pv/settings.py
+++ b/pv/settings.py
@@ -77,11 +77,6 @@ MIDDLEWARE_CLASSES = (
 
 ROOT_URLCONF = 'pv.urls'
 
-OAUTH2_PROVIDER = {
-    # this is the list of available scopes
-    'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'shows': 'Access to shows', 'notes': 'Access to notes', 'timeslots': 'Access to timeslots'}
-}
-
 REST_FRAMEWORK = {
     # Use Django's standard `django.contrib.auth` permissions,
     # or allow read-only access for unauthenticated users.
@@ -90,7 +85,6 @@ REST_FRAMEWORK = {
         #'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
     ],
     'DEFAULT AUTHENTICATION_CLASSES': [
-        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
     ]
 }
 
@@ -108,7 +102,6 @@ INSTALLED_APPS = (
     'tinymce',
     'versatileimagefield',
     'rest_framework',
-    'oauth2_provider',
     'frapp',
 )
 
diff --git a/pv/urls.py b/pv/urls.py
index 22e5e94c..ea38cf19 100644
--- a/pv/urls.py
+++ b/pv/urls.py
@@ -5,9 +5,7 @@ from django.views.static import serve
 from rest_framework import routers
 from rest_framework.authtoken import views
 
-from program.views import APIUserViewSet, APIHostViewSet, APIShowViewSet, APITimeSlotViewSet, APINoteViewSet, APICategoryViewSet, APITypeViewSet, APITopicViewSet, APIMusicFocusViewSet, APIRTRCategoryViewSet, APILanguageViewSet, json_day_schedule, json_week_schedule, json_timeslots_specials, json_get_timeslots_by_show
-
-from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
+from program.views import APIUserViewSet, APIHostViewSet, APIShowViewSet, APITimeSlotViewSet, APINoteViewSet, APICategoryViewSet, APITypeViewSet, APITopicViewSet, APIMusicFocusViewSet, APIRTRCategoryViewSet, APILanguageViewSet, json_day_schedule, json_week_schedule, json_timeslots_specials
 
 admin.autodiscover()
 
@@ -26,18 +24,14 @@ router.register(r'rtrcategories', APIRTRCategoryViewSet)
 router.register(r'languages', APILanguageViewSet)
 
 urlpatterns = [
-    url(r'^o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
-    url(r'^api-token-auth/', views.obtain_auth_token),
     url(r'^api/v1/', include(router.urls) ),
-    url(r'^api/v1/program$', json_week_schedule),
-    url(r'^api/v1/timeslots-by-show$', json_get_timeslots_by_show, name='json_get_timeslots_by_show'),
-    url(r'^api/v1/week_schedule$', json_week_schedule),
+    url(r'^api/v1/program/week', json_week_schedule),
+    url(r'^api/v1/program/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', json_day_schedule),
     url(r'^admin/', admin.site.urls),
     url(r'^program/', include('program.urls')),
     url(r'^nop', include('nop.urls')),
     url(r'^', include('frapp.urls')),
     #url(r'^tinymce/', include('tinymce.urls')),
-    url(r'^export/day_schedule/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', json_day_schedule),
     url(r'^export/timeslots_specials.json$', json_timeslots_specials),
 ]
 
-- 
GitLab