From a6af6efe636bd878180d70dea3bdafcdd9cda498 Mon Sep 17 00:00:00 2001
From: Ernesto Rico Schmidt <ernesto@helsinki.at>
Date: Fri, 20 Nov 2020 19:49:25 -0400
Subject: [PATCH] Clean-up code

- Make `Host.is_editable`, `Show.is_editable`, `Note.is_editable`, and
  `Note.get_audio_url` static methods,
- Make `Schedule.instantiate_upcoming`, `Schedule.generate_timeslots`,
  `Schedule.get_collisions`, `Schedule.generate_conflicts`,
  `Schedule.make_conflicts` and `Schedule.resolve_conflicts` static
  methods. Most of these methods do not belong here.
- Fix signatures of methods for subclasses of `viewsets.ModelViewSet`
- Fix doctrins for subclasses of `viewsets.ModelViewSet`
- Fix comparisons with None (replace '==' and '!=' with 'is' and 'is not')
---
 program/models.py      | 230 +++++++++++++----------
 program/serializers.py |  54 ++----
 program/views.py       | 404 ++++++++++++++++++++++-------------------
 3 files changed, 368 insertions(+), 320 deletions(-)

diff --git a/program/models.py b/program/models.py
index b9a0b3b5..322d796b 100644
--- a/program/models.py
+++ b/program/models.py
@@ -1,4 +1,6 @@
+import json
 from datetime import date, datetime, time, timedelta
+from urllib.request import urlopen
 
 from dateutil.relativedelta import relativedelta
 from dateutil.rrule import rrule
@@ -249,7 +251,8 @@ class Host(models.Model):
     is_active = models.BooleanField(_("Is active?"), default=True)
     email = models.EmailField(_("E-Mail"), blank=True)
     website = models.URLField(_("Website"), blank=True, help_text=_("URL to your personal website."))
-    biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True, help_text=_("Describe yourself and your fields of interest in a few sentences."))
+    biography = tinymce_models.HTMLField(_("Biography"), blank=True, null=True,
+                                         help_text=_("Describe yourself and your fields of interest in a few sentences."))
     googleplus_url = models.URLField(_("Google+ URL"), blank=True, help_text=_("URL to your Google+ profile."))
     facebook_url = models.URLField(_("Facebook URL"), blank=True, help_text=_("URL to your Facebook profile."))
     twitter_url = models.URLField(_("Twitter URL"), blank=True, help_text=_("URL to your Twitter profile."))
@@ -260,7 +263,10 @@ 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='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."))
+    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',)
@@ -276,34 +282,38 @@ class Host(models.Model):
     def active_shows(self):
         return self.shows.filter(schedules__until__gt=timezone.now())
 
-    def is_editable(self, host_id):
+    def save(self, *args, **kwargs):
+        super(Host, self).save(*args, **kwargs)
+
+        # Generate thumbnails
+        if self.image.name and THUMBNAIL_SIZES:
+            for size in THUMBNAIL_SIZES:
+                self.image.thumbnail = self.image.crop[size].name
+
+    # FIXME: this does not belong here
+    @staticmethod
+    def is_editable(host_view_set, host_id):
         """
         Whether the given host is assigned to a show the current user owns
         @return boolean
         """
-        if self.request.user.is_superuser:
+        if host_view_set.request.user.is_superuser:
             return True
 
-        host_ids = Host.objects.filter(shows__in=self.request.user.shows.all()).distinct().values_list('id', flat=True)
+        host_ids = Host.objects.filter(shows__in=host_view_set.request.user.shows.all()).distinct().values_list('id', flat=True)
         return int(host_id) in host_ids
 
-    def save(self, *args, **kwargs):
-        super(Host, self).save(*args, **kwargs)
-
-        # Generate thumbnails
-        if self.image.name and THUMBNAIL_SIZES:
-            for size in THUMBNAIL_SIZES:
-                thumbnail = self.image.crop[size].name
-
 
 class Show(models.Model):
-    predecessor = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='successors', verbose_name=_("Predecessor"))
+    predecessor = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE, related_name='successors',
+                                    verbose_name=_("Predecessor"))
     hosts = models.ManyToManyField(Host, blank=True, related_name='shows', verbose_name=_("Hosts"))
     owners = models.ManyToManyField(User, blank=True, related_name='shows', verbose_name=_("Owners"))
     language = models.ManyToManyField(Language, blank=True, related_name='language', verbose_name=_("Language"))
     type = models.ForeignKey(Type, on_delete=models.CASCADE, related_name='shows', verbose_name=_("Type"))
     category = models.ManyToManyField(Category, blank=True, related_name='shows', verbose_name=_("Category"))
-    fundingcategory = models.ForeignKey(FundingCategory, null=True, on_delete=models.CASCADE, blank=True, related_name='shows', verbose_name=_("Funding Category"))
+    fundingcategory = models.ForeignKey(FundingCategory, null=True, on_delete=models.CASCADE, blank=True, related_name='shows',
+                                        verbose_name=_("Funding Category"))
     topic = models.ManyToManyField(Topic, blank=True, related_name='shows', verbose_name=_("Topic"))
     musicfocus = models.ManyToManyField(MusicFocus, blank=True, related_name='shows', verbose_name=_("Music focus"))
     name = models.CharField(_("Name"), max_length=255, help_text=_("The show's name. Avoid a subtitle."))
@@ -311,18 +321,30 @@ class Show(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(_("Image"), blank=True, null=True, upload_to='show_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your show. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
-    logo = models.ImageField(_("Logo"), blank=True, null=True, upload_to='show_images', help_text=_("Upload a logo of your show."))
-    short_description = models.TextField(_("Short description"), help_text=_("Describe your show for your listeners in some sentences. Avoid technical data like airing times and contact information. They will be added automatically."))
-    description = tinymce_models.HTMLField(_("Description"), blank=True, null=True, help_text=_("Describe your show in detail."))
+    image = VersatileImageField(_("Image"), blank=True, null=True, upload_to='show_images', width_field='width',
+                                height_field='height', ppoi_field='ppoi', help_text=_(
+            "Upload an image to your show. Images are automatically cropped around the 'Primary Point of Interest'. Click in"
+            " the image to change it and press Save."))
+    logo = models.ImageField(_("Logo"), blank=True, null=True, upload_to='show_images',
+                             help_text=_("Upload a logo of your show."))
+    short_description = models.TextField(_("Short description"), help_text=_(
+        "Describe your show for your listeners in some sentences. Avoid technical data like airing times and contact"
+        " information. They will be added automatically."))
+    description = tinymce_models.HTMLField(_("Description"), blank=True, null=True,
+                                           help_text=_("Describe your show in detail."))
     email = models.EmailField(_("E-Mail"), blank=True, null=True, help_text=_("The main contact email address for your show."))
-    website = models.URLField(_("Website"), blank=True, null=True, help_text=_("Is there a website to your show? Type in its URL."))
-    cba_series_id = models.IntegerField(_("CBA Series ID"), blank=True, null=True, help_text=_("Link your show to a CBA series by giving its ID. This will enable CBA upload and will automatically link your show to your CBA archive. Find out your show's ID under https://cba.fro.at/series"))
-    fallback_id = models.IntegerField(_("Fallback ID"), blank=True, null=True, help_text=_("If a timeslot of your show is empty, this playlist will be aired as a backup."))
+    website = models.URLField(_("Website"), blank=True, null=True,
+                              help_text=_("Is there a website to your show? Type in its URL."))
+    cba_series_id = models.IntegerField(_("CBA Series ID"), blank=True, null=True, help_text=_(
+        "Link your show to a CBA series by giving its ID. This will enable CBA upload and will automatically link your show to"
+        " your CBA archive. Find out your show's ID under https://cba.fro.at/series"))
+    fallback_id = models.IntegerField(_("Fallback ID"), blank=True, null=True, help_text=_(
+        "If a timeslot of your show is empty, this playlist will be aired as a backup."))
     created = models.DateTimeField(auto_now_add=True, editable=False)
     last_updated = models.DateTimeField(auto_now=True, editable=False)
     is_active = models.BooleanField(_("Is active?"), default=True)
-    is_public = models.BooleanField(_("Is Public?"), default=False, help_text=_("Files and Playlists of Public Shows can only be changed by owners but may be used by everyone."))
+    is_public = models.BooleanField(_("Is Public?"), default=False, help_text=_(
+        "Files and Playlists of Public Shows can only be changed by owners but may be used by everyone."))
 
     class Meta:
         ordering = ('slug',)
@@ -331,7 +353,7 @@ class Show(models.Model):
 
     def __str__(self):
         if self.id is None:
-            return '%s' % (self.name)
+            return '%s' % self.name
 
         return '%04d | %s' % (self.id, self.name)
 
@@ -342,20 +364,21 @@ class Show(models.Model):
     def active_schedules(self):
         return self.schedules.filter(until__gt=date.today())
 
-    def is_editable(self, show_id):
+    # FIXME: this does not belong here
+    @staticmethod
+    def is_editable(show_view_set, show_id):
         """
         Whether the current user is owner of the given show
         @return boolean
         """
-        if self.request.user.is_superuser:
+        if show_view_set.request.user.is_superuser:
             return True
 
-        show_ids = self.request.user.shows.all().values_list('id', flat=True)
+        show_ids = show_view_set.request.user.shows.all().values_list('id', flat=True)
         return int(show_id) in show_ids
 
 
 class RRule(models.Model):
-
     FREQ_CHOICES = (
         (1, _("Monthly")),
         (2, _("Weekly")),
@@ -373,8 +396,7 @@ class RRule(models.Model):
     name = models.CharField(_("Name"), max_length=32, unique=True)
     freq = models.IntegerField(_("Frequency"), choices=FREQ_CHOICES)
     interval = models.IntegerField(_("Interval"), default=1)
-    bysetpos = models.IntegerField(_("Set position"), blank=True,
-                                   choices=BYSETPOS_CHOICES, null=True)
+    bysetpos = models.IntegerField(_("Set position"), blank=True, choices=BYSETPOS_CHOICES, null=True)
     count = models.IntegerField(_("Count"), blank=True, null=True)
 
     class Meta:
@@ -408,8 +430,11 @@ class Schedule(models.Model):
     add_days_no = models.IntegerField(_("Add days"), blank=True, null=True)
     add_business_days_only = models.BooleanField(_("Only add business days?"), default=False)
     fallback_id = models.IntegerField(_("Fallback ID"), blank=True, null=True)
-    automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True, choices=get_automation_id_choices())  # Deprecated
-    created = models.DateTimeField(auto_now_add=True, editable=False, null=True)  # -> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add
+    automation_id = models.IntegerField(_("Automation ID"), blank=True, null=True,
+                                        choices=get_automation_id_choices())  # Deprecated
+    # -> both see https://stackoverflow.com/questions/1737017/django-auto-now-and-auto-now-add
+    created = models.DateTimeField(auto_now_add=True, editable=False,
+                                   null=True)
     last_updated = models.DateTimeField(auto_now=True, editable=False, null=True)
 
     class Meta:
@@ -430,6 +455,11 @@ class Schedule(models.Model):
         else:
             return '%s, %s, %s - %s' % (weekday, self.rrule, tstart, tend)
 
+    def save(self, *args, **kwargs):
+        super(Schedule, self).save(*args, **kwargs)
+
+    # FIXME: this does not belong here
+    @staticmethod
     def instantiate_upcoming(sdl, show_pk, pk=None):
         """Returns an upcoming schedule instance for conflict resolution"""
 
@@ -475,6 +505,8 @@ class Schedule(models.Model):
 
         return schedule
 
+    # FIXME: this does not belong here
+    @staticmethod
     def generate_timeslots(schedule):
         """
         Returns a list of timeslot objects based on a schedule and its rrule
@@ -591,8 +623,8 @@ class Schedule(models.Model):
                 weekday = datetime.date(starts[k]).weekday()
                 if schedule.add_business_days_only and weekday > 3:
                     days_until_sunday = 6 - weekday
-                    starts[k] = starts[k] + relativedelta(days=+days_until_sunday+schedule.add_days_no)
-                    ends[k] = ends[k] + relativedelta(days=+days_until_sunday+schedule.add_days_no)
+                    starts[k] = starts[k] + relativedelta(days=+days_until_sunday + schedule.add_days_no)
+                    ends[k] = ends[k] + relativedelta(days=+days_until_sunday + schedule.add_days_no)
                 else:
                     starts[k] = starts[k] + relativedelta(days=+schedule.add_days_no)
                     ends[k] = ends[k] + relativedelta(days=+schedule.add_days_no)
@@ -604,6 +636,8 @@ class Schedule(models.Model):
 
         return timeslots
 
+    # FIXME: this does not belong here
+    @staticmethod
     def get_collisions(timeslots):
         """
         Tests a list of timeslot objects for colliding timeslots in the database
@@ -616,11 +650,11 @@ class Schedule(models.Model):
         for ts in timeslots:
 
             collision = TimeSlot.objects.filter(
-                           (Q(start__lt=ts.end) & Q(end__gte=ts.end)) |
-                           (Q(end__gt=ts.start) & Q(end__lte=ts.end)) |
-                           (Q(start__gte=ts.start) & Q(end__lte=ts.end)) |
-                           (Q(start__lte=ts.start) & Q(end__gte=ts.end))
-                        )
+                (Q(start__lt=ts.end) & Q(end__gte=ts.end)) |
+                (Q(end__gt=ts.start) & Q(end__lte=ts.end)) |
+                (Q(start__gte=ts.start) & Q(end__lte=ts.end)) |
+                (Q(start__lte=ts.start) & Q(end__gte=ts.end))
+            )
 
             if collision:
                 collisions.append(collision[0])  # TODO: Do we really always retrieve one?
@@ -629,6 +663,8 @@ class Schedule(models.Model):
 
         return collisions
 
+    # FIXME: this does not belong here
+    @staticmethod
     def generate_conflicts(timeslots):
         """
         Tests a list of timeslot objects for colliding timeslots in the database
@@ -653,31 +689,20 @@ class Schedule(models.Model):
 
             # Get collisions for each timeslot
             collision_list = list(TimeSlot.objects.filter(
-                           (Q(start__lt=ts.end) & Q(end__gte=ts.end)) |
-                           (Q(end__gt=ts.start) & Q(end__lte=ts.end)) |
-                           (Q(start__gte=ts.start) & Q(end__lte=ts.end)) |
-                           (Q(start__lte=ts.start) & Q(end__gte=ts.end))
-                        ).order_by('start'))
+                (Q(start__lt=ts.end) & Q(end__gte=ts.end)) |
+                (Q(end__gt=ts.start) & Q(end__lte=ts.end)) |
+                (Q(start__gte=ts.start) & Q(end__lte=ts.end)) |
+                (Q(start__lte=ts.start) & Q(end__gte=ts.end))
+            ).order_by('start'))
 
             # Add the projected timeslot
-            projected_entry = {}
-            projected_entry['hash'] = ts.hash
-            projected_entry['start'] = str(ts.start)
-            projected_entry['end'] = str(ts.end)
+            projected_entry = {'hash': ts.hash, 'start': str(ts.start), 'end': str(ts.end)}
 
             for c in collision_list:
-
                 # Add the collision
-                collision = {}
-                collision['id'] = c.id
-                collision['start'] = str(c.start)
-                collision['end'] = str(c.end)
-                collision['playlist_id'] = c.playlist_id
-                collision['show'] = c.show.id
-                collision['show_name'] = c.show.name
-                collision['is_repetition'] = c.is_repetition
-                collision['schedule'] = c.schedule_id
-                collision['memo'] = c.memo
+                collision = {'id': c.id, 'start': str(c.start), 'end': str(c.end), 'playlist_id': c.playlist_id,
+                             'show': c.show.id, 'show_name': c.show.name, 'is_repetition': c.is_repetition,
+                             'schedule': c.schedule_id, 'memo': c.memo}
 
                 # Get note
                 try:
@@ -709,7 +734,7 @@ class Schedule(models.Model):
                     #   |  |
                     #   +--+
                     #
-                    if ts.start < c.start and ts.end > c.start and ts.end <= c.end:
+                    if ts.end > c.start > ts.start <= c.end:
                         solution_choices.add('theirs-end')
                         solution_choices.add('ours-end')
 
@@ -723,7 +748,7 @@ class Schedule(models.Model):
                     #        |  |
                     #        +--+
                     #
-                    if ts.start >= c.start and ts.start < c.end and ts.end > c.end:
+                    if c.start <= ts.start < c.end < ts.end:
                         solution_choices.add('theirs-start')
                         solution_choices.add('ours-start')
 
@@ -769,6 +794,8 @@ class Schedule(models.Model):
 
         return conflicts
 
+    # FIXME: this does not belong here
+    @staticmethod
     def make_conflicts(sdl, schedule_pk, show_pk):
         """
         Retrieves POST vars
@@ -801,6 +828,8 @@ class Schedule(models.Model):
 
         return conflicts
 
+    # FIXME: this does not belong here
+    @staticmethod
     def resolve_conflicts(data, schedule_pk, show_pk):
         """
         Resolves conflicts
@@ -1017,10 +1046,8 @@ class Schedule(models.Model):
 
         # If 'dryrun' is true, just return the projected changes instead of executing them
         if 'dryrun' in sdl and sdl['dryrun']:
-            output = {}
-            output['create'] = [model_to_dict(ts) for ts in create]
-            output['update'] = [model_to_dict(ts) for ts in update]
-            output['delete'] = [model_to_dict(ts) for ts in delete]
+            output = {'create': [model_to_dict(ts) for ts in create], 'update': [model_to_dict(ts) for ts in update],
+                      'delete': [model_to_dict(ts) for ts in delete]}
             return output
 
         '''Database changes if no errors found'''
@@ -1063,9 +1090,6 @@ class Schedule(models.Model):
 
         return model_to_dict(schedule)
 
-    def save(self, *args, **kwargs):
-        super(Schedule, self).save(*args, **kwargs)
-
 
 class TimeSlotManager(models.Manager):
     @staticmethod
@@ -1138,7 +1162,9 @@ class TimeSlotManager(models.Manager):
 
 class TimeSlot(models.Model):
     schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE, related_name='timeslots', verbose_name=_("Schedule"))
-    start = models.DateTimeField(_("Start time"))  # Removed 'unique=True' because new Timeslots need to be created before deleting the old ones (otherwise linked notes get deleted first)
+    start = models.DateTimeField(_("Start time"))
+    # Removed 'unique=True' because new Timeslots need to be created before deleting the old ones (otherwise linked notes get
+    # deleted first)
     end = models.DateTimeField(_("End time"))
     show = models.ForeignKey(Show, editable=False, on_delete=models.CASCADE, related_name='timeslots')
     memo = models.TextField(_("Memo"), blank=True)
@@ -1165,7 +1191,7 @@ class TimeSlot(models.Model):
         super(TimeSlot, self).save(*args, **kwargs)
         return self
 
-    def generate(self, **kwargs):
+    def generate(self):
         """Returns the object instance without saving"""
 
         self.show = self.schedule.show
@@ -1187,18 +1213,28 @@ class Note(models.Model):
         (2, _("Repetition")),
     )
     timeslot = models.OneToOneField(TimeSlot, on_delete=models.CASCADE, verbose_name=_("Time slot"), unique=True)
-    title = models.CharField(_("Title"), max_length=128, help_text=_("Give your note a good headline. What will your upcoming show be about? Try to arouse interest to listen to it!<br>Avoid technical data like the show's name, its airing times or its episode number. These data are added automatically."))
+    title = models.CharField(_("Title"), max_length=128, help_text=_(
+        "Give your note a good headline. What will your upcoming show be about? Try to arouse interest to listen to it!<br>"
+        "Avoid technical data like the show's name, its airing times or its episode number. These data are added"
+        " automatically."))
     slug = models.SlugField(_("Slug"), max_length=32, unique=True, help_text=_("A simple to read URL for your show."))
-    summary = models.TextField(_("Summary"), blank=True, help_text=_("Describe your upcoming show in some sentences. Avoid technical data like airing times and contact information. They will be added automatically."))
+    summary = models.TextField(_("Summary"), blank=True, help_text=_(
+        "Describe your upcoming show in some sentences. Avoid technical data like airing times and contact information. They"
+        " will be added automatically."))
     content = tinymce_models.HTMLField(_("Content"), help_text=_("Describe your upcoming show in detail."))
     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(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width', height_field='height', ppoi_field='ppoi', help_text=_("Upload an image to your note. Images are automatically cropped around the 'Primary Point of Interest'. Click in the image to change it and press Save."))
+    image = VersatileImageField(_("Featured image"), blank=True, null=True, upload_to='note_images', width_field='width',
+                                height_field='height', ppoi_field='ppoi', help_text=_(
+            "Upload an image to your note. Images are automatically cropped around the 'Primary Point of Interest'. Click in"
+            " the image to change it and press Save."))
     status = models.IntegerField(_("Status"), choices=STATUS_CHOICES, default=1)
     start = models.DateTimeField(editable=False)
     show = models.ForeignKey(Show, on_delete=models.CASCADE, related_name='notes', editable=True)
-    cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True, help_text=_("Link the note to a certain CBA post by giving its ID. (E.g. if your post's CBA URL is https://cba.fro.at/1234, then your CBA ID is 1234)"))
+    cba_id = models.IntegerField(_("CBA ID"), blank=True, null=True, help_text=_(
+        "Link the note to a certain CBA post by giving its ID. (E.g. if your post's CBA URL is https://cba.fro.at/1234, then"
+        " your CBA ID is 1234)"))
     audio_url = models.TextField(_("Direct URL to a linked audio file"), blank=True, editable=False)
     created = models.DateTimeField(auto_now_add=True, editable=False)
     last_updated = models.DateTimeField(auto_now=True, editable=False)
@@ -1213,19 +1249,38 @@ class Note(models.Model):
     def __str__(self):
         return '%s - %s' % (self.title, self.timeslot)
 
-    def is_editable(self, note_id):
+    def save(self, *args, **kwargs):
+        self.start = self.timeslot.start
+        self.show = self.timeslot.schedule.show
+
+        timeslot = TimeSlot.objects.get(pk=self.timeslot.id)
+        timeslot.note_id = self.id
+        timeslot.save()
+
+        super(Note, self).save(*args, **kwargs)
+
+        # Generate thumbnails
+        if self.image.name and THUMBNAIL_SIZES:
+            for size in THUMBNAIL_SIZES:
+                self.image.thumbnail = self.image.crop[size].name
+
+    # FIXME: this does not belong here
+    @staticmethod
+    def is_editable(note_view_set, note_id):
         """
         Whether the given note is assigned to a show the current user owns
         @return boolean
         """
 
-        if self.request.user.is_superuser:
+        if note_view_set.request.user.is_superuser:
             return True
 
         note = Note.objects.get(pk=note_id)
 
-        return int(note.show_id) in self.request.user.shows.all().values_list('id', flat=True)
+        return int(note.show_id) in note_view_set.request.user.shows.all().values_list('id', flat=True)
 
+    # FIXME: this does not belong here
+    @staticmethod
     def get_audio_url(cba_id):
         """
         Retrieve the direct URL to the mp3 in CBA
@@ -1241,31 +1296,14 @@ class Note(models.Model):
         audio_url = ''
 
         if cba_id is not None and cba_id != '' and CBA_API_KEY != '':
-            from urllib.request import urlopen
-            import json
-
             url = CBA_AJAX_URL + '?action=cba_ajax_get_filename&post_id=' + str(cba_id) + '&api_key=' + CBA_API_KEY
 
             # For momentary testing without being whitelisted - TODO: delete the line
-            url = 'https://cba.fro.at/wp-content/plugins/cba/ajax/cba-get-filename.php?post_id=' + str(cba_id) + '&c=Ml3fASkfwR8'
+            url = 'https://cba.fro.at/wp-content/plugins/cba/ajax/cba-get-filename.php?post_id=' + str(
+                cba_id) + '&c=Ml3fASkfwR8'
 
             with urlopen(url) as conn:
                 audio_url_json = conn.read().decode('utf-8-sig')
                 audio_url = json.loads(audio_url_json)
 
         return audio_url
-
-    def save(self, *args, **kwargs):
-        self.start = self.timeslot.start
-        self.show = self.timeslot.schedule.show
-
-        timeslot = TimeSlot.objects.get(pk=self.timeslot.id)
-        timeslot.note_id = self.id
-        timeslot.save()
-
-        super(Note, self).save(*args, **kwargs)
-
-        # Generate thumbnails
-        if self.image.name and THUMBNAIL_SIZES:
-            for size in THUMBNAIL_SIZES:
-                thumbnail = self.image.crop[size].name
diff --git a/program/serializers.py b/program/serializers.py
index 897b5427..1eb947bc 100644
--- a/program/serializers.py
+++ b/program/serializers.py
@@ -18,7 +18,6 @@ class UserSerializer(serializers.ModelSerializer):
         model = User
         fields = '__all__'
 
-
     def create(self, validated_data):
         """
         Create and return a new User instance, given the validated data.
@@ -31,12 +30,12 @@ class UserSerializer(serializers.ModelSerializer):
         user.set_password(validated_data['password'])
         user.save()
 
-        profile = Profile(user=user, cba_username=profile_data.get('cba_username').strip(), cba_user_token=profile_data.get('cba_user_token').strip())
+        profile = Profile(user=user, cba_username=profile_data.get('cba_username').strip(),
+                          cba_user_token=profile_data.get('cba_user_token').strip())
         profile.save()
 
         return user
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing User instance, given the validated data.
@@ -69,13 +68,11 @@ 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.
@@ -92,9 +89,10 @@ class CategorySerializer(serializers.ModelSerializer):
 
 
 class HostSerializer(serializers.ModelSerializer):
-    thumbnails = serializers.SerializerMethodField() # Read-only
+    thumbnails = serializers.SerializerMethodField()  # Read-only
 
-    def get_thumbnails(self, host):
+    @staticmethod
+    def get_thumbnails(host):
         """Returns thumbnails"""
         thumbnails = []
 
@@ -108,7 +106,6 @@ class HostSerializer(serializers.ModelSerializer):
         model = Host
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing Host instance, given the validated data.
@@ -138,7 +135,6 @@ class LanguageSerializer(serializers.ModelSerializer):
         model = Language
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing Language instance, given the validated data.
@@ -155,7 +151,6 @@ class TopicSerializer(serializers.ModelSerializer):
         model = Topic
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing Topic instance, given the validated data.
@@ -174,7 +169,6 @@ class MusicFocusSerializer(serializers.ModelSerializer):
         model = MusicFocus
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing MusicFocus instance, given the validated data.
@@ -193,7 +187,6 @@ class TypeSerializer(serializers.ModelSerializer):
         model = Type
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing Type instance, given the validated data.
@@ -213,7 +206,6 @@ class FundingCategorySerializer(serializers.ModelSerializer):
         model = FundingCategory
         fields = '__all__'
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing FundingCategory instance, given the validated data.
@@ -228,18 +220,19 @@ class FundingCategorySerializer(serializers.ModelSerializer):
 
 
 class ShowSerializer(serializers.HyperlinkedModelSerializer):
-    owners = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(),many=True)
-    category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all(),many=True)
-    hosts = serializers.PrimaryKeyRelatedField(queryset=Host.objects.all(),many=True)
-    language = serializers.PrimaryKeyRelatedField(queryset=Language.objects.all(),many=True)
-    topic = serializers.PrimaryKeyRelatedField(queryset=Topic.objects.all(),many=True)
-    musicfocus = serializers.PrimaryKeyRelatedField(queryset=MusicFocus.objects.all(),many=True)
+    owners = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), many=True)
+    category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all(), many=True)
+    hosts = serializers.PrimaryKeyRelatedField(queryset=Host.objects.all(), many=True)
+    language = serializers.PrimaryKeyRelatedField(queryset=Language.objects.all(), many=True)
+    topic = serializers.PrimaryKeyRelatedField(queryset=Topic.objects.all(), many=True)
+    musicfocus = serializers.PrimaryKeyRelatedField(queryset=MusicFocus.objects.all(), many=True)
     type = serializers.PrimaryKeyRelatedField(queryset=Type.objects.all())
     fundingcategory = serializers.PrimaryKeyRelatedField(queryset=FundingCategory.objects.all())
-    predecessor = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all(),required=False,allow_null=True)
-    thumbnails = serializers.SerializerMethodField() # Read-only
+    predecessor = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all(), required=False, allow_null=True)
+    thumbnails = serializers.SerializerMethodField()  # Read-only
 
-    def get_thumbnails(self, show):
+    @staticmethod
+    def get_thumbnails(show):
         """Returns thumbnails"""
         thumbnails = []
 
@@ -249,7 +242,6 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
 
         return thumbnails
 
-
     class Meta:
         model = Show
         fields = ('id', 'name', 'slug', 'image', 'ppoi', 'logo', 'short_description', 'description',
@@ -257,7 +249,6 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
                   'predecessor', 'cba_series_id', 'fallback_id', 'category', 'hosts',
                   'owners', 'language', 'topic', 'musicfocus', 'thumbnails', 'is_active', 'is_public')
 
-
     def create(self, validated_data):
         """
         Create and return a new Show instance, given the validated data.
@@ -283,7 +274,6 @@ class ShowSerializer(serializers.HyperlinkedModelSerializer):
         show.save()
         return show
 
-
     def update(self, instance, validated_data):
         """
         Update and return an existing Show instance, given the validated data.
@@ -342,7 +332,6 @@ class ScheduleSerializer(serializers.ModelSerializer):
         schedule.save()
         return schedule
 
-
     def update(self, instance, validated_data):
         """Update and return an existing Schedule instance, given the validated data."""
 
@@ -375,7 +364,6 @@ class TimeSlotSerializer(serializers.ModelSerializer):
         """Create and return a new TimeSlot instance, given the validated data."""
         return TimeSlot.objects.create(**validated_data)
 
-
     def update(self, instance, validated_data):
         """Update and return an existing Show instance, given the validated data."""
 
@@ -391,9 +379,10 @@ class NoteSerializer(serializers.ModelSerializer):
     show = serializers.PrimaryKeyRelatedField(queryset=Show.objects.all())
     timeslot = serializers.PrimaryKeyRelatedField(queryset=TimeSlot.objects.all())
     host = serializers.PrimaryKeyRelatedField(queryset=Host.objects.all())
-    thumbnails = serializers.SerializerMethodField() # Read-only
+    thumbnails = serializers.SerializerMethodField()  # Read-only
 
-    def get_thumbnails(self, note):
+    @staticmethod
+    def get_thumbnails(note):
         """Returns thumbnails"""
         thumbnails = []
 
@@ -403,12 +392,10 @@ class NoteSerializer(serializers.ModelSerializer):
 
         return thumbnails
 
-
     class Meta:
         model = Note
         fields = '__all__'
 
-
     def create(self, validated_data):
         """Create and return a new Note instance, given the validated data."""
 
@@ -421,7 +408,7 @@ class NoteSerializer(serializers.ModelSerializer):
         note = Note.objects.create(**validated_data)
 
         # Assign note to timeslot
-        if note.timeslot_id != None:
+        if note.timeslot_id is not None:
             try:
                 timeslot = TimeSlot.objects.get(pk=note.timeslot_id)
                 timeslot.note_id = note.id
@@ -431,7 +418,6 @@ class NoteSerializer(serializers.ModelSerializer):
 
         return note
 
-
     def update(self, instance, validated_data):
         """Update and return an existing Note instance, given the validated data."""
 
@@ -457,7 +443,7 @@ class NoteSerializer(serializers.ModelSerializer):
             ts.save(update_fields=["note_id"])
 
         # Assign note to timeslot
-        if instance.timeslot.id != None:
+        if instance.timeslot.id is not None:
             try:
                 timeslot = TimeSlot.objects.get(pk=instance.timeslot.id)
                 timeslot.note_id = instance.id
diff --git a/program/views.py b/program/views.py
index ad24aad2..ea9158ce 100644
--- a/program/views.py
+++ b/program/views.py
@@ -16,9 +16,9 @@ from program.serializers import TypeSerializer, LanguageSerializer, MusicFocusSe
     ScheduleSerializer, CategorySerializer, FundingCategorySerializer, TopicSerializer, TimeSlotSerializer, HostSerializer, \
     UserSerializer
 from program.utils import get_cached_shows
+from pv.settings import STATION_FALLBACK_ID
 
 
-# Deprecated
 def json_day_schedule(request, year=None, month=None, day=None):
     if year is None and month is None and day is None:
         today = datetime.combine(date.today(), time(0, 0))
@@ -57,14 +57,12 @@ def json_playout(request):
          If end not given, it returns all timeslots of the next 7 days
     """
 
-    from pv.settings import STATION_FALLBACK_ID
-
-    if request.GET.get('start') == None:
+    if request.GET.get('start') is None:
         start = datetime.combine(date.today(), time(0, 0))
     else:
         start = datetime.combine(datetime.strptime(request.GET.get('start'), '%Y-%m-%d').date(), time(0, 0))
 
-    if request.GET.get('end') == None:
+    if request.GET.get('end') is None:
         # If no end was given, return the next week
         timeslots = TimeSlot.objects.get_7d_timeslots(start).select_related('schedule').select_related('show')
     else:
@@ -87,7 +85,7 @@ def json_playout(request):
             fundingcategory = FundingCategory.objects.get(pk=ts.show.fundingcategory_id)
             fdcategory = fundingcategory.fundingcategory
 
-        type = Type.objects.get(pk=ts.show.type_id)
+        type_ = Type.objects.get(pk=ts.show.type_id)
 
         classname = 'default'
 
@@ -108,7 +106,7 @@ def json_playout(request):
             'show_id': ts.show.id,
             'show_name': ts.show.name + is_repetition,
             'show_hosts': hosts,
-            'show_type': type.type,
+            'show_type': type_.type,
             'show_categories': categories,
             'show_topics': topics,
             'show_musicfocus': musicfocus,
@@ -128,7 +126,6 @@ def json_playout(request):
                         content_type="application/json; charset=utf-8")
 
 
-# Deprecated
 def json_timeslots_specials(request):
     specials = {}
     shows = get_cached_shows()['shows']
@@ -138,7 +135,7 @@ def json_timeslots_specials(request):
             specials[show['id']] = show
 
     for ts in TimeSlot.objects.filter(end__gt=timezone.now(),
-                                      schedule__automation_id__in=specials.iterkeys()).select_related('show'):
+                                      schedule__automation_id__in=specials.keys()).select_related('show'):
         automation_id = ts.schedule.automation_id
         start = ts.start.strftime('%Y-%m-%d_%H:%M:%S')
         end = ts.end.strftime('%Y-%m-%d_%H:%M:%S')
@@ -155,17 +152,12 @@ def json_timeslots_specials(request):
                         content_type="application/json; charset=utf-8")
 
 
-####################################################################
-# REST API View Sets
-####################################################################
-
-
 class APIUserViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/users   Returns oneself - Superusers see all users. Only superusers may create a user (GET, POST)
-    /api/v1/users/1 Used for retrieving or updating a single user. Non-superusers may only update certain fields. (GET, PUT) - DELETE is prohibited for everyone
+    `/users` returns oneself. Superusers see all users. Only superusers may create a user (GET, POST)
+    `/users/{user_id}` retrieves or updates a single user. Non-superusers may only update certain fields (GET, PUT)
 
-    Superusers may access and update all users
+    Superusers may access and update all users.
     """
 
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
@@ -179,13 +171,14 @@ class APIUserViewSet(viewsets.ModelViewSet):
 
         return User.objects.filter(pk=self.request.user.id)
 
-    def list(self, request):
+    def list(self, request, *args, **kwargs):
         users = self.get_queryset()
         serializer = UserSerializer(users, many=True)
         return Response(serializer.data)
 
-    def retrieve(self, request, pk=None):
+    def retrieve(self, request, *args, **kwargs):
         """Returns a single user"""
+        pk = kwargs.get('pk')
 
         # Common users only see themselves
         if not request.user.is_superuser and int(pk) != request.user.id:
@@ -195,7 +188,7 @@ class APIUserViewSet(viewsets.ModelViewSet):
         serializer = UserSerializer(user)
         return Response(serializer.data)
 
-    def create(self, request, pk=None):
+    def create(self, request, *args, **kwargs):
         """
         Create a User
         Only superusers may create a user
@@ -212,7 +205,8 @@ class APIUserViewSet(viewsets.ModelViewSet):
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def update(self, request, pk=None):
+    def update(self, request, *args, **kwargs):
+        pk = kwargs.get('pk')
 
         serializer = UserSerializer(data=request.data)
         # Common users may only edit themselves
@@ -223,35 +217,35 @@ class APIUserViewSet(viewsets.ModelViewSet):
         serializer = UserSerializer(user, data=request.data, context={'user': request.user})
 
         if serializer.is_valid():
-            serializer.save();
+            serializer.save()
             return Response(serializer.data)
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def destroy(self, request, pk=None):
+    def destroy(self, request, *args, **kwargs):
         """Deleting users is prohibited: Set 'is_active' to False instead"""
         return Response(status=status.HTTP_401_UNAUTHORIZED)
 
 
 class APIShowViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/shows/                                             Returns all shows (GET, POST)
-    /api/v1/shows/?active=true                                 Returns all active shows (= currently running) (GET)
-    /api/v1/shows/?active=false                                Returns all inactive shows (= past or upcoming) (GET)
-    /api/v1/shows/?public=true                                 Returns all public shows (GET)
-    /api/v1/shows/?public=false                                Returns all non-public shows (GET)
-    /api/v1/shows/?host=1                                      Returns shows assigned to a given host (GET)
-    /api/v1/shows/?owner=1                                     Returns shows of a given owner (GET)
-    /api/v1/shows/1                                            Used for retrieving a single show or update (if owned) (GET, PUT) - DELETE is not allowed via API. Set is_active to False instead.
-    /api/v1/shows/1/notes                                      Returns all notes to the show (GET) - POST not allowed at this level, use /shows/1/schedules/1/timeslots/1/note instead
-    /api/v1/shows/1/notes/1                                    Returns the note of the show by its ID (GET) - PUT/DELETE not allowed at this level, use /shows/1/schedules/1/timeslots/1/note/1/ instead
-    /api/v1/shows/1/schedules                                  Returns all schedules of the show (GET, POST)
-    /api/v1/shows/1/schedules/1                                Returns the schedule of the show by its ID (GET) - POST not allowed at this level, use /shows/1/schedules/ instead
-    /api/v1/shows/1/timeslots                                  Returns all timeslots of the show (GET) - Timeslots may only be added by creating/updating a schedule
-    /api/v1/shows/1/timeslots/1                                Returns the timeslot of the show (GET) - Timeslots may only be added by creating/updating a schedule
-    /api/v1/shows/1/timeslots?start=2017-01-01&end=2017-12-31  Returns all timeslots of the show within the given timerange (GET)
-    /api/v1/shows/1/timeslots/1/note                           Returns a note to the timeslot (one at max) (GET) - POST not allowed at this level, use /shows/1/schedules/1/timelots/1/note/ instead
-    /api/v1/shows/1/timeslots/1/note/1                         Returns the note of the show's timeslot by its ID (GET) - PUT/DELETE not allowed at this level, use /shows/1/schedules/1/timeslots/1/note/1/ instead
+    `/shows/` returns all shows (GET, POST)
+    `/shows/?active=true` returns all active shows (= currently running) (GET)
+    `/shows/?active=false` returns all inactive shows (= past or upcoming) (GET)
+    `/shows/?public=true` returns all public shows (GET)
+    `/shows/?public=false` returns all non-public shows (GET)
+    `/shows/?host={host_id}` returns shows assigned to a given host (GET)
+    `/shows/?owner={owner_id}` returns shows of a given owner (GET)
+    `/shows/{show_id}` retrieves or updates (if owned) a single show (GET, PUT).
+    `/shows/{show_id}/notes` returns all notes to the show (GET)
+    `/shows/{show_id}/notes/{note_id}` returns the note of the show by its ID (GET)
+    `/shows/{show_id}/schedules` returns all schedules of the show (GET, POST)
+    `/shows/{show_id}/schedules/{schedule_id}` returns the schedule of the show by its ID (GET)
+    `/shows/{show_id}/timeslots` returns all timeslots of the show (GET)
+    `/shows/{show_id}/timeslots/{timeslot_id}` returns the timeslot of the show (GET)
+    `/shows/{show_id}/timeslots?start={start_date}&end={end_date}` returns all timeslots of the show within the time range (GET)
+    `/shows/{show_id}/timeslots/{timeslot_id}/note` returns a note to the timeslot (one at max) (GET)
+    `/shows/{show_id}/timeslots/{timeslot_id}/note/1` returns the note of the show's timeslot by its ID (GET)
 
     Only superusers may add and delete shows
     """
@@ -262,20 +256,20 @@ class APIShowViewSet(viewsets.ModelViewSet):
     pagination_class = LimitOffsetPagination
 
     def get_queryset(self):
-
         shows = Show.objects.all()
 
-        '''Filters'''
-
+        # Filters
         if self.request.GET.get('active') == 'true' or self.request.GET.get('active') == 'false':
-            '''Filter currently running shows'''
-
+            # Filter currently running shows
             # Get currently running schedules to filter by first
             # 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)
             # Add limit for future?
-            show_ids = Schedule.objects.filter(Q(rrule_id__gt=1, dstart__lte=date.today(), until__gte=date.today()) |
-                                               Q(rrule_id=1, dstart__gte=date.today())
+            show_ids = 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)
 
             # Filter active shows based on timeslots as well as on the is_active flag
@@ -283,28 +277,28 @@ class APIShowViewSet(viewsets.ModelViewSet):
             shows = Show.objects.filter(id__in=show_ids, is_active=True)
 
         if self.request.GET.get('active') == 'false':
-            '''Return all shows except those which are running'''
+            # Return all shows except those which are running
             shows = Show.objects.exclude(id__in=show_ids, is_active=True)
 
         if self.request.GET.get('public') == 'true':
-            '''Return all public shows'''
+            # Return all public shows
             shows = shows.filter(is_public=True)
 
         if self.request.GET.get('public') == 'false':
-            '''Return all public shows'''
+            # Return all public shows
             shows = shows.filter(is_public=False)
 
-        if self.request.GET.get('owner') != None:
-            '''Filter shows by owner'''
+        if self.request.GET.get('owner') is not None:
+            # Filter shows by owner
             shows = shows.filter(owners__in=[int(self.request.GET.get('owner'))])
 
-        if self.request.GET.get('host') != None:
-            '''Filter shows by host'''
+        if self.request.GET.get('host') is not None:
+            # Filter shows by host
             shows = shows.filter(hosts__in=[int(self.request.GET.get('host'))])
 
         return shows
 
-    def create(self, request, pk=None):
+    def create(self, request, *args, **kwargs):
         """
         Create a show
         Only superusers may create a show
@@ -321,19 +315,24 @@ class APIShowViewSet(viewsets.ModelViewSet):
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def retrieve(self, request, pk=None):
+    def retrieve(self, request, *args, **kwargs):
         """Returns a single show"""
+
+        pk = kwargs.get('pk')
+
         show = get_object_or_404(Show, pk=pk)
         serializer = ShowSerializer(show)
 
         return Response(serializer.data)
 
-    def update(self, request, pk=None):
+    def update(self, request, *args, **kwargs):
         """
         Update a show
         Common users may only update shows they own
         """
 
+        pk = kwargs.get('pk')
+
         if not Show.is_editable(self, pk):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
@@ -344,36 +343,35 @@ class APIShowViewSet(viewsets.ModelViewSet):
             # Common users mustn't edit the show's name
             if not request.user.is_superuser:
                 serializer.validated_data['name'] = show.name
-            serializer.save();
+            serializer.save()
             return Response(serializer.data)
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def destroy(self, request, pk=None):
+    def destroy(self, request, *args, **kwargs):
         """
         Delete a show
         Only superusers may delete shows
         """
 
-        """
-        if not request.user.is_superuser:
-            return Response(status=status.HTTP_401_UNAUTHORIZED)
-
-        show = get_object_or_404(Show, pk=pk)
-        Show.objects.get(pk=pk).delete()
-
-        return Response(status=status.HTTP_204_NO_CONTENT)
-        """
+        # if not request.user.is_superuser:
+        #     return Response(status=status.HTTP_401_UNAUTHORIZED)
+        #
+        # show = get_object_or_404(Show, pk=pk)
+        # Show.objects.get(pk=pk).delete()
+        #
+        # return Response(status=status.HTTP_204_NO_CONTENT)
+        #
 
         return Response(status=status.HTTP_401_UNAUTHORIZED)
 
 
 class APIScheduleViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/schedules/          Returns schedules (GET) - POST not allowed at this level
-    /api/v1/schedules/1         Returns the given schedule (GET) - POST not allowed at this level
-    /api/v1/shows/1/schedules   Returns schedules of the show (GET, POST)
-    /api/v1/shows/1/schedules/1 Returns schedules by its ID (GET, PUT, DELETE)
+    `/schedules/` returns all schedules (GET)
+    `/schedules/{schedule_id}` returns the given schedule (GET)
+    `/shows/{show_id}/schedules` returns schedules of the show (GET, POST)
+    `/shows/{show_id}/schedules/{schedule_id}` returns schedules by its ID (GET, PUT, DELETE)
 
     Only superusers may create and update schedules
     """
@@ -383,22 +381,25 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        show_pk = self.kwargs['show_pk'] if 'show_pk' in self.kwargs else None
+        show_pk = self.kwargs.get('show_pk')
 
-        if show_pk != None:
+        if show_pk is not None:
             return Schedule.objects.filter(show=show_pk)
 
         return Schedule.objects.all()
 
-    def list(self, request, show_pk=None, pk=None):
+    def list(self, request, *args, **kwargs):
         """List Schedules of a show"""
+
         schedules = self.get_queryset()
         serializer = ScheduleSerializer(schedules, many=True)
         return Response(serializer.data)
 
-    def retrieve(self, request, pk=None, show_pk=None):
+    def retrieve(self, request, *args, **kwargs):
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
-        if show_pk != None:
+        if show_pk is not None:
             schedule = get_object_or_404(Schedule, pk=pk, show=show_pk)
         else:
             schedule = get_object_or_404(Schedule, pk=pk)
@@ -406,55 +407,57 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
         serializer = ScheduleSerializer(schedule)
         return Response(serializer.data)
 
-    def create(self, request, pk=None, show_pk=None):
+    def create(self, request, *args, **kwargs):
         """
         Create a schedule, generate timeslots, test for collisions and resolve them including notes
 
         Only superusers may add schedules
         TODO: Perhaps directly insert into database if no conflicts found
         """
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
         # Only allow creating when calling /shows/1/schedules/
-        if show_pk == None or not request.user.is_superuser:
+        if show_pk is None or not request.user.is_superuser:
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
         # The schedule dict is mandatory
-        if not 'schedule' in request.data:
+        if 'schedule' not in request.data:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         # First create submit -> return projected timeslots and collisions
-        if not 'solutions' in request.data:
+        if 'solutions' not in request.data:
             return Response(Schedule.make_conflicts(request.data['schedule'], pk, show_pk))
 
         # Otherwise try to resolve
         resolution = Schedule.resolve_conflicts(request.data, pk, show_pk)
 
         # If resolution went well
-        if not 'projected' in resolution:
+        if 'projected' not in resolution:
             return Response(resolution, status=status.HTTP_201_CREATED)
 
         # Otherwise return conflicts
         return Response(resolution)
 
-    def update(self, request, pk=None, show_pk=None):
+    def update(self, request, *args, **kwargs):
         """
         Update a schedule, generate timeslots, test for collisions and resolve them including notes
 
         Only superusers may update schedules
         """
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
         # Only allow updating when calling /shows/1/schedules/1
-        if show_pk == None or not request.user.is_superuser:
+        if show_pk is None or not request.user.is_superuser:
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-        schedule = get_object_or_404(Schedule, pk=pk, show=show_pk)
-
         # The schedule dict is mandatory
-        if not 'schedule' in request.data:
+        if 'schedule' not in request.data:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         # First update submit -> return projected timeslots and collisions
-        if not 'solutions' in request.data:
+        if 'solutions' not in request.data:
             # TODO: If nothing else than fallback_id, automation_id or is_repetition changed -> just save and don't do anything
             return Response(Schedule.make_conflicts(request.data['schedule'], pk, show_pk))
 
@@ -462,23 +465,24 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
         resolution = Schedule.resolve_conflicts(request.data, pk, show_pk)
 
         # If resolution went well
-        if not 'projected' in resolution:
+        if 'projected' not in resolution:
             return Response(resolution, status=status.HTTP_200_OK)
 
         # Otherwise return conflicts
         return Response(resolution)
 
-    def destroy(self, request, pk=None, show_pk=None):
+    def destroy(self, request, *args, **kwargs):
         """
         Delete a schedule
         Only superusers may delete schedules
         """
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
         # Only allow deleting when calling /shows/1/schedules/1
-        if show_pk == None or not request.user.is_superuser:
+        if show_pk is None or not request.user.is_superuser:
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-        schedule = get_object_or_404(Schedule, pk=pk)
         Schedule.objects.get(pk=pk).delete()
 
         return Response(status=status.HTTP_204_NO_CONTENT)
@@ -486,15 +490,15 @@ class APIScheduleViewSet(viewsets.ModelViewSet):
 
 class APITimeSlotViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/timeslots                                                     Returns timeslots of the next 60 days (GET) - Timeslots may only be added by creating/updating a schedule
-    /api/v1/timeslots/1                                                   Returns the given timeslot (GET) - PUT/DELETE not allowed at this level
-    /api/v1/timeslots/?start=2017-01-01&end=2017-02-01                    Returns timeslots within the given timerange (GET)
-    /api/v1/shows/1/timeslots                                             Returns timeslots of the show (GET, POST)
-    /api/v1/shows/1/timeslots/1                                           Returns a timeslots by its ID (GET, PUT, DELETE)
-    /api/v1/shows/1/timeslots?start=2017-01-01&end=2017-02-01             Returns timeslots of the show within the given timerange
-    /api/v1/shows/1/schedules/1/timeslots                                 Returns all timeslots of the schedule (GET, POST)
-    /api/v1/shows/1/schedules/1/timeslots/1                               Returns a timeslot by its ID (GET, DELETE). If PUT, the next repetition is returned or nothing if the next timeslot isn't one
-    /api/v1/shows/1/schedules/1/timeslots?start=2017-01-01&end=2017-02-01 Returns all timeslots of the schedule within the given timerange
+    `/timeslots` returns timeslots of the next 60 days (GET). Timeslots may only be added by creating/updating a schedule
+    `/timeslots/{timeslot_id}` returns the given timeslot (GET)
+    `/timeslots/?start={start_date}&end={end_date}` returns timeslots within the time range (GET)
+    `/shows/{show_id}/timeslots` returns timeslots of the show (GET, POST)
+    `/shows/{show_id}/timeslots/{timeslot_id}` returns a timeslots by its ID (GET, PUT, DELETE)
+    `/shows/{show_id}/timeslots?start={start_date}&end={end_date}` returns timeslots of the show within the time range
+    `/shows/{show_id}/schedules/{schedule_id}/timeslots` returns all timeslots of the schedule (GET, POST)
+    `/shows/{show_id}/schedules/{schedule_id}/timeslots/{timeslot_id}` returns a timeslot by its ID (GET, DELETE). If PUT, the next repetition is returned or nothing if the next timeslot isn't one
+    `/shows/{show_id}/schedules/{schedule_id}/timeslots?start={start_date}&end={end_date}` returns all timeslots of the schedule within the time range
     """
 
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
@@ -503,11 +507,10 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
     queryset = TimeSlot.objects.none()
 
     def get_queryset(self):
+        show_pk = self.kwargs.get('show_pk')
+        schedule_pk = self.kwargs.get('schedule_pk')
 
-        show_pk = self.kwargs['show_pk'] if 'show_pk' in self.kwargs else None
-        schedule_pk = self.kwargs['schedule_pk'] if 'schedule_pk' in self.kwargs else None
-
-        '''Filters'''
+        # Filters
 
         # Return next 60 days by default
         start = datetime.combine(date.today(), time(0, 0))
@@ -517,14 +520,14 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
             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))
 
-        '''Endpoints'''
+        # Endpoints
 
         #
         #     /shows/1/schedules/1/timeslots/
         #
         #     Returns timeslots of the given show and schedule
         #
-        if show_pk != None and schedule_pk != None:
+        if show_pk is not None and schedule_pk is not None:
             return TimeSlot.objects.filter(show=show_pk, schedule=schedule_pk, start__gte=start, end__lte=end).order_by('start')
 
         #
@@ -532,7 +535,7 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         #
         #     Returns timeslots of the show
         #
-        elif show_pk != None and schedule_pk == None:
+        elif show_pk is not None and schedule_pk is None:
             return TimeSlot.objects.filter(show=show_pk, start__gte=start, end__lte=end).order_by('start')
 
         #
@@ -543,9 +546,11 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         else:
             return TimeSlot.objects.filter(start__gte=start, end__lte=end).order_by('start')
 
-    def retrieve(self, request, pk=None, schedule_pk=None, show_pk=None):
+    def retrieve(self, request, *args, **kwargs):
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
-        if show_pk != None:
+        if show_pk is not None:
             timeslot = get_object_or_404(TimeSlot, pk=pk, show=show_pk)
         else:
             timeslot = get_object_or_404(TimeSlot, pk=pk)
@@ -553,18 +558,21 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
         serializer = TimeSlotSerializer(timeslot)
         return Response(serializer.data)
 
-    def create(self, request):
+    def create(self, request, *args, **kwargs):
         """
         Timeslots may only be created by adding/updating schedules
         TODO: Adding single timeslot which fits to schedule?
         """
         return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-    def update(self, request, pk=None, schedule_pk=None, show_pk=None):
+    def update(self, request, *args, **kwargs):
         """Link a playlist_id to a timeslot"""
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
+        schedule_pk = kwargs.get('schedule_pk')
 
         # Update is only allowed when calling /shows/1/schedules/1/timeslots/1 and if user owns the show
-        if schedule_pk == None or show_pk == None or not Show.is_editable(self, show_pk):
+        if schedule_pk is None or show_pk is None or not Show.is_editable(self, show_pk):
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
         timeslot = get_object_or_404(TimeSlot, pk=pk, schedule=schedule_pk, show=show_pk)
@@ -586,20 +594,21 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def destroy(self, request, pk=None, schedule_pk=None, show_pk=None):
+    def destroy(self, request, *args, **kwargs):
         """
         Delete a timeslot
         Only superusers may delete timeslots
         """
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
 
         # Only allow when calling endpoint starting with /shows/1/...
-        if show_pk == None:
+        if show_pk is None:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         if not request.user.is_superuser:
             return Response(status=status.HTTP_401_UNAUTHORIZED)
 
-        timeslot = get_object_or_404(TimeSlot, pk=pk)
         TimeSlot.objects.get(pk=pk).delete()
 
         return Response(status=status.HTTP_204_NO_CONTENT)
@@ -607,18 +616,18 @@ class APITimeSlotViewSet(viewsets.ModelViewSet):
 
 class APINoteViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/notes/                                  Returns all notes (GET) - POST not allowed at this level
-    /ap1/v1/notes/1                                 Returns a single note (if owned) (GET) - PUT/DELETE not allowed at this level
-    /api/v1/notes/?ids=1,2,3,4,5                    Returns given notes (if owned) (GET)
-    /api/v1/notes/?host=1                           Returns notes assigned to a given host (GET)
-    /api/v1/notes/?owner=1                          Returns notes editable by a given user (GET)
-    /api/v1/notes/?user=1                           Returns notes created by a given user (GET)
-    /api/v1/shows/1/notes                           Returns all notes of a show (GET) - POST not allowed at this level
-    /api/v1/shows/1/notes/1                         Returns a note by its ID (GET) - PUT/DELETE not allowed at this level
-    /api/v1/shows/1/timeslots/1/note/               Returns a note of the timeslot (GET) - POST not allowed at this level
-    /api/v1/shows/1/timeslots/1/note/1              Returns a note by its ID (GET) - PUT/DELETE not allowed at this level
-    /api/v1/shows/1/schedules/1/timeslots/1/note    Returns a note to the timeslot (GET, POST) - Only one note allowed per timeslot
-    /api/v1/shows/1/schedules/1/timeslots/1/note/1  Returns a note by its ID (GET, PUT, DELETE)
+    `/notes/` returns all notes (GET)
+    `/notes/{note_id}` returns a single note (if owned) (GET)
+    `/notes/?ids={note_id,note_id2}` returns given notes (if owned) (GET)
+    `/notes/?host={host_id}` returns notes assigned to a given host (GET)
+    `/notes/?owner={owner_id}` returns notes editable by a given user (GET)
+    `/notes/?user={user_id}` returns notes created by a given user (GET)
+    `/shows/{show_id}/notes` returns all notes of a show (GET)
+    `/shows/{show_id}/notes/{note_id}` returns a note by its ID (GET)
+    `/shows/{show_id}/timeslots/{timeslot_id}/note/` returns a note of the timeslot (GET)
+    `/shows/{show_id}/timeslots/{timeslot_id}/note/{note_id}` returns a note by its ID (GET)
+    `/shows/{show_id}/schedules/{schedule_id}/timeslots/{timeslot_id}/note` returns a note to the timeslot (GET, POST).
+    `/shows/{show_id}/schedules/{schedule_id}/timeslots/{timeslot_id}/note/{note_id}` returns a note by its ID (GET, PUT, DELETE)
 
     Superusers may access and update all notes
     """
@@ -629,12 +638,10 @@ class APINoteViewSet(viewsets.ModelViewSet):
     pagination_class = LimitOffsetPagination
 
     def get_queryset(self):
+        timeslot_pk = self.kwargs.get('timeslot_pk')
+        show_pk = self.kwargs.get('show_pk')
 
-        pk = self.kwargs['pk'] if 'pk' in self.kwargs else None
-        timeslot_pk = self.kwargs['timeslot_pk'] if 'timeslot_pk' in self.kwargs else None
-        show_pk = self.kwargs['show_pk'] if 'show_pk' in self.kwargs else None
-
-        '''Endpoints'''
+        # Endpoints
 
         #
         #     /shows/1/schedules/1/timeslots/1/note
@@ -642,7 +649,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
         #
         #     Return a note to the timeslot
         #
-        if show_pk != None and timeslot_pk != None:
+        if show_pk is not None and timeslot_pk is not None:
             notes = Note.objects.filter(show=show_pk, timeslot=timeslot_pk)
 
         #
@@ -650,7 +657,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
         #
         #     Returns notes to the show
         #
-        elif show_pk != None and timeslot_pk == None:
+        elif show_pk is not None and timeslot_pk is None:
             notes = Note.objects.filter(show=show_pk)
 
         #
@@ -661,33 +668,36 @@ class APINoteViewSet(viewsets.ModelViewSet):
         else:
             notes = Note.objects.all()
 
-        '''Filters'''
+        # Filters
 
-        if self.request.GET.get('ids') != None:
-            '''Filter notes by their IDs'''
+        if self.request.GET.get('ids') is not None:
+            # Filter notes by their IDs
             note_ids = self.request.GET.get('ids').split(',')
             notes = notes.filter(id__in=note_ids)
 
-        if self.request.GET.get('host') != None:
-            '''Filter notes by host'''
+        if self.request.GET.get('host') is not None:
+            # Filter notes by host
             notes = notes.filter(host=int(self.request.GET.get('host')))
 
-        if self.request.GET.get('owner') != None:
-            '''Filter notes by show owner: all notes the user may edit'''
+        if self.request.GET.get('owner') is not None:
+            # Filter notes by show owner: all notes the user may edit
             shows = Show.objects.filter(owners=int(self.request.GET.get('owner')))
             notes = notes.filter(show__in=shows)
 
-        if self.request.GET.get('user') != None:
-            '''Filter notes by their creator'''
+        if self.request.GET.get('user') is not None:
+            # Filter notes by their creator
             notes = notes.filter(user=int(self.request.GET.get('user')))
 
         return notes
 
-    def create(self, request, pk=None, timeslot_pk=None, schedule_pk=None, show_pk=None):
+    def create(self, request, *args, **kwargs):
         """Create a note"""
+        show_pk = kwargs.get('show_pk')
+        schedule_pk = kwargs.get('schedule_pk')
+        timeslot_pk = kwargs.get('timeslot_pk')
 
         # Only create a note if show_id, timeslot_id and schedule_id is given
-        if show_pk == None or schedule_pk == None or timeslot_pk == None:
+        if show_pk is None or schedule_pk is None or timeslot_pk is None:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         if not Show.is_editable(self, show_pk):
@@ -698,7 +708,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
         if serializer.is_valid():
 
             # Don't assign a host the user mustn't edit
-            if not Host.is_editable(self, request.data['host']) or request.data['host'] == None:
+            if not Host.is_editable(self, request.data['host']) or request.data['host'] is None:
                 serializer.validated_data['host'] = None
 
             serializer.save()
@@ -706,7 +716,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
 
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
 
-    def retrieve(self, request, pk=None, timeslot_pk=None, schedule_pk=None, show_pk=None):
+    def retrieve(self, request, *args, **kwargs):
         """
         Returns a single note
 
@@ -716,13 +726,17 @@ class APINoteViewSet(viewsets.ModelViewSet):
         /shows/1/timeslots/1/note/1
         /shows/1/schedules/1/timeslots/1/note/1
         """
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
+        schedule_pk = kwargs.get('schedule_pk')
+        timeslot_pk = kwargs.get('timeslot_pk')
 
         #
         #      /shows/1/notes/1
         #
         #      Returns a note to a show
         #
-        if show_pk != None and timeslot_pk == None and schedule_pk == None:
+        if show_pk is not None and timeslot_pk is None and schedule_pk is None:
             note = get_object_or_404(Note, pk=pk, show=show_pk)
 
         #
@@ -731,7 +745,7 @@ class APINoteViewSet(viewsets.ModelViewSet):
         #
         #     Return a note to a timeslot
         #
-        elif show_pk != None and timeslot_pk != None:
+        elif show_pk is not None and timeslot_pk is not None:
             note = get_object_or_404(Note, pk=pk, show=show_pk, timeslot=timeslot_pk)
 
         #
@@ -745,10 +759,14 @@ class APINoteViewSet(viewsets.ModelViewSet):
         serializer = NoteSerializer(note)
         return Response(serializer.data)
 
-    def update(self, request, pk=None, show_pk=None, schedule_pk=None, timeslot_pk=None):
+    def update(self, request, *args, **kwargs):
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
+        schedule_pk = kwargs.get('schedule_pk')
+        timeslot_pk = kwargs.get('timeslot_pk')
 
         # Allow PUT only when calling /shows/1/schedules/1/timeslots/1/note/1
-        if show_pk == None or schedule_pk == None or timeslot_pk == None:
+        if show_pk is None or schedule_pk is None or timeslot_pk is None:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         note = get_object_or_404(Note, pk=pk, timeslot=timeslot_pk, show=show_pk)
@@ -762,17 +780,22 @@ class APINoteViewSet(viewsets.ModelViewSet):
         if serializer.is_valid():
 
             # Don't assign a host the user mustn't edit. Reassign the original value instead
-            if not Host.is_editable(self, request.data['host']) and request.data['host'] != None:
+            if not Host.is_editable(self, request.data['host']) and request.data['host'] is not None:
                 serializer.validated_data['host'] = Host.objects.filter(pk=note.host_id)[0]
 
-            serializer.save();
+            serializer.save()
             return Response(serializer.data)
 
         return Response(status=status.HTTP_400_BAD_REQUEST)
 
-    def destroy(self, request, pk=None, show_pk=None, schedule_pk=None, timeslot_pk=None):
+    def destroy(self, request, *args, **kwargs):
         # Allow DELETE only when calling /shows/1/schedules/1/timeslots/1/note/1
-        if show_pk == None or schedule_pk == None or timeslot_pk == None:
+        pk = kwargs.get('pk')
+        show_pk = kwargs.get('show_pk')
+        schedule_pk = kwargs.get('schedule_pk')
+        timeslot_pk = kwargs.get('timeslot_pk')
+
+        if show_pk is None or schedule_pk is None or timeslot_pk is None:
             return Response(status=status.HTTP_400_BAD_REQUEST)
 
         note = get_object_or_404(Note, pk=pk)
@@ -786,17 +809,18 @@ class APINoteViewSet(viewsets.ModelViewSet):
 
 class APICategoryViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/categories/              Returns all categories (GET, POST)
-    /api/v1/categories/?active=true  Returns all active categories (GET)
-    /api/v1/categories/?active=false Returns all inactive categories (GET)
-    /api/v1/categories/1             Returns a category by its ID (GET, PUT, DELETE)
+    `/categories/` returns all categories (GET, POST)
+    `/categories/?active=true` returns all active categories (GET)
+    `/categories/?active=false` returns all inactive categories (GET)
+    `/categories/{category_id}` Returns a category by its ID (GET, PUT, DELETE)
     """
+
     queryset = Category.objects.all()
     serializer_class = CategorySerializer
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return Category.objects.filter(is_active=True)
@@ -809,10 +833,10 @@ class APICategoryViewSet(viewsets.ModelViewSet):
 
 class APITypeViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/types/              Returns all types (GET, POST)
-    /api/v1/types/?active=true  Returns all active types (GET)
-    /api/v1/types/?active=false Returns all active types (GET)
-    /api/v1/types/1             Returns a type by its ID (GET, PUT, DELETE)
+    `/types/` returns all types (GET, POST)
+    `/types/?active=true` returns all active types (GET)
+    `/types/?active=false` returns all inactive types (GET)
+    `/types/{type_id}` returns a type by its ID (GET, PUT, DELETE)
     """
 
     queryset = Type.objects.all()
@@ -820,7 +844,7 @@ class APITypeViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return Type.objects.filter(is_active=True)
@@ -833,9 +857,9 @@ class APITypeViewSet(viewsets.ModelViewSet):
 
 class APITopicViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/topics/             Returns all topics (GET, POST)
-    /api/v1/topics/?active=true Returns all active topics (GET)
-    /api/v1/topics/1            Returns a topic by its ID (GET, PUT, DELETE)
+    /topics/: Returns all topics (GET, POST)
+    /topics/?active=true Returns all active topics (GET)
+    /topics/1: Returns a topic by its ID (GET, PUT, DELETE)
     """
 
     queryset = Topic.objects.all()
@@ -843,7 +867,7 @@ class APITopicViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return Topic.objects.filter(is_active=True)
@@ -856,10 +880,10 @@ class APITopicViewSet(viewsets.ModelViewSet):
 
 class APIMusicFocusViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/musicfocus/              Returns all musicfocuses (GET, POST)
-    /api/v1/musicfocus/?active=true  Returns all active musicfocuses (GET)
-    /api/v1/musicfocus/?active=false Returns all inactive musicfocuses (GET)
-    /api/v1/musicfocus/1             Returns a musicfocus by its ID (GET, PUT, DELETE)
+    `/musicfocus/` returns all music focuses (GET, POST)
+    `/musicfocus/?active=true`: returns all active music focuses (GET)
+    `/musicfocus/?active=false`: returns all inactive music focuses (GET)
+    `/musicfocus/{music_focus_id}`: returns a music focus by its ID (GET, PUT, DELETE)
     """
 
     queryset = MusicFocus.objects.all()
@@ -867,7 +891,7 @@ class APIMusicFocusViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return MusicFocus.objects.filter(is_active=True)
@@ -880,10 +904,10 @@ class APIMusicFocusViewSet(viewsets.ModelViewSet):
 
 class APIFundingCategoryViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/fundingcategories/              Returns all fundingcategories (GET, POST)
-    /api/v1/fundingcategories/?active=true  Returns all active fundingcategories (GET)
-    /api/v1/fundingcategories/?active=false Returns all active fundingcategories (GET)
-    /api/v1/fundingcategories/1             Returns a fundingcategory by its ID (GET, PUT, DELETE)
+    `/fundingcategories/`: returns all funding categories (GET, POST)
+    `/fundingcategories/?active=true` returns all active funding categories (GET)
+    `/fundingcategories/?active=false` returns all inactive funding categories (GET)
+    `/fundingcategories/{funding_category_id}` returns a funding category by its ID (GET, PUT, DELETE)
     """
 
     queryset = FundingCategory.objects.all()
@@ -891,7 +915,7 @@ class APIFundingCategoryViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return FundingCategory.objects.filter(is_active=True)
@@ -904,10 +928,10 @@ class APIFundingCategoryViewSet(viewsets.ModelViewSet):
 
 class APILanguageViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/languages/              Returns all languages (GET, POST)
-    /api/v1/languages/?active=true  Returns all active languages (GET)
-    /api/v1/languages/?active=false Returns all active languages (GET)
-    /api/v1/languages/1             Returns a language by its ID (GET, PUT, DELETE)
+    `/languages/` returns all languages (GET, POST)
+    `/languages/?active=true' returns all active languages (GET)
+    `/languages/?active=false' returns all inactive languages (GET)
+    `/languages/{language_id}` returns a language by its ID (GET, PUT, DELETE)
     """
 
     queryset = Language.objects.all()
@@ -915,7 +939,7 @@ class APILanguageViewSet(viewsets.ModelViewSet):
     permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly]
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return Language.objects.filter(is_active=True)
@@ -928,10 +952,10 @@ class APILanguageViewSet(viewsets.ModelViewSet):
 
 class APIHostViewSet(viewsets.ModelViewSet):
     """
-    /api/v1/hosts/              Returns all hosts (GET, POST)
-    /api/v1/hosts/?active=true  Returns all active hosts (GET)
-    /api/v1/hosts/?active=false Returns all active hosts (GET)
-    /api/v1/hosts/1             Returns a host by its ID (GET, PUT, DELETE)
+    `/hosts/` returns all hosts (GET, POST)
+    `/hosts/?active=true` returns all active hosts (GET)
+    `/hosts/?active=false` returns all inactive hosts (GET)
+    `/hosts/1` returns a host by its ID (GET, PUT, DELETE)
     """
 
     queryset = Host.objects.all()
@@ -940,7 +964,7 @@ class APIHostViewSet(viewsets.ModelViewSet):
     pagination_class = LimitOffsetPagination
 
     def get_queryset(self):
-        '''Filters'''
+        """Filters"""
 
         if self.request.GET.get('active') == 'true':
             return Host.objects.filter(is_active=True)
-- 
GitLab