Add missing docstrings

Add missing docstrings to modules, classes and functions. Refactor
existing docstrings that are not correctly formatted.

Issue: HIC-170
Change-Id: I766830259b897f8c2d931f3b53ce935e82b161fd
diff --git a/crashreport_stats/__init__.py b/crashreport_stats/__init__.py
index e69de29..21843fb 100644
--- a/crashreport_stats/__init__.py
+++ b/crashreport_stats/__init__.py
@@ -0,0 +1 @@
+"""Hiccup crashreport_stats application."""
diff --git a/crashreport_stats/admin.py b/crashreport_stats/admin.py
index db5b576..a5a720e 100644
--- a/crashreport_stats/admin.py
+++ b/crashreport_stats/admin.py
@@ -1,4 +1,4 @@
-"""Register models for admin site"""
+"""Register models for admin site."""
 from django.contrib import admin
 from crashreport_stats.models import (
     Version,
diff --git a/crashreport_stats/migrations/0001_initial.py b/crashreport_stats/migrations/0001_initial.py
index 2ff76d4..d332743 100644
--- a/crashreport_stats/migrations/0001_initial.py
+++ b/crashreport_stats/migrations/0001_initial.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 # Generated by Django 1.10.2 on 2017-05-09 13:01
+"""Initial database migration."""
 from __future__ import unicode_literals
 
 from django.db import migrations, models
@@ -10,6 +11,7 @@
 
 
 class Migration(migrations.Migration):
+    """Set up the initial database."""
 
     initial = True
 
diff --git a/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py b/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
index 29da259..aa14152 100644
--- a/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
+++ b/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
-#
-# Set the default values for the Version and VersionDaily models.
+
+"""Migrations to set default values for the Version and VersionDaily models."""
+
 from __future__ import unicode_literals
 
 from django.db import migrations, models
@@ -8,6 +9,7 @@
 
 
 class Migration(migrations.Migration):
+    """Set the default values for the Version and VersionDaily models."""
 
     dependencies = [("crashreport_stats", "0001_initial")]
 
diff --git a/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py b/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
index e3e8261..4d1613b 100644
--- a/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
+++ b/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
-#
-# Introducing the RadioVersion and RadioVersionDaily models
+
+"""Migrations to introduce the RadioVersion and RadioVersionDaily models."""
+
 from __future__ import unicode_literals
 
 from django.db import migrations, models
@@ -8,6 +9,7 @@
 
 
 class Migration(migrations.Migration):
+    """Introduce the RadioVersion and RadioVersionDaily models."""
 
     dependencies = [
         ("crashreport_stats", "0002_version_and_versiondaily_with_defaults")
diff --git a/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py b/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
index 7cbd433..bdaaa75 100644
--- a/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
+++ b/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
@@ -1,4 +1,4 @@
-"""Introduce the StatsMetadata model and remove broken default dates."""
+"""Migrations to introduce the StatsMetadata model."""
 from __future__ import unicode_literals
 
 from django.db import migrations, models
diff --git a/crashreport_stats/migrations/0005_remove_manual_default_value.py b/crashreport_stats/migrations/0005_remove_manual_default_value.py
index ebc9db5..9173480 100644
--- a/crashreport_stats/migrations/0005_remove_manual_default_value.py
+++ b/crashreport_stats/migrations/0005_remove_manual_default_value.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 # Generated by Django 1.10.2 on 2018-08-16 09:10
-"""Remove previously added default value for updated_at column."""
+"""Migrations to remove previously added default value for updated_at column."""
 from __future__ import unicode_literals
 
 from django.db import migrations, models
diff --git a/crashreport_stats/migrations/__init__.py b/crashreport_stats/migrations/__init__.py
index e69de29..4e9e400 100644
--- a/crashreport_stats/migrations/__init__.py
+++ b/crashreport_stats/migrations/__init__.py
@@ -0,0 +1 @@
+"""Database migrations for crashreport stats models."""
diff --git a/crashreport_stats/raw_querys.py b/crashreport_stats/raw_querys.py
index 8d8c77d..13e3cb5 100644
--- a/crashreport_stats/raw_querys.py
+++ b/crashreport_stats/raw_querys.py
@@ -1,7 +1,10 @@
+"""SQL queries for getting device statistics."""
+
 from django.conf import settings
 
 
 def execute_device_update_history_query(cursor, params):
+    """Query the device update history."""
     if (
         settings.DATABASES["default"]["ENGINE"]
         == "django.db.backends.postgresql_psycopg2"
@@ -12,6 +15,7 @@
 
 
 def execute_device_report_history(cursor, params):
+    """Query the device report history."""
     if (
         settings.DATABASES["default"]["ENGINE"]
         == "django.db.backends.postgresql_psycopg2"
@@ -22,6 +26,7 @@
 
 
 def sqlite_execute_device_update_history_query(cursor, params):
+    """Execute SQLite query for getting the device update history."""
     query = """
     SELECT
         min(crashreports_heartbeat.date) as update_date,
@@ -55,6 +60,7 @@
 
 
 def psql_execute_device_update_history_query(cursor, params):
+    """Execute PostgreSQL query for getting the device update history."""
     query = """
     SELECT
         min(crashreports_heartbeat.date) as update_date,
@@ -89,6 +95,7 @@
 
 
 def sqlite_execute_device_report_history(cursor, params):
+    """Execute SQLite query for getting the device report history."""
     query = """
     SELECT
       strftime("%%Y-%%m-%%d",crashreports_heartbeat.date) as date,
@@ -127,6 +134,7 @@
 
 
 def psql_execute_device_report_history(cursor, params):
+    """Execute Postgresql query for getting the device report history."""
     query = """
     SELECT
       crashreports_heartbeat.date::date as date,
diff --git a/crashreport_stats/templatetags/__init__.py b/crashreport_stats/templatetags/__init__.py
index e69de29..e208fef 100644
--- a/crashreport_stats/templatetags/__init__.py
+++ b/crashreport_stats/templatetags/__init__.py
@@ -0,0 +1 @@
+"""Django template tags for crashreport statistics."""
diff --git a/crashreport_stats/templatetags/crashreport_stats_tags.py b/crashreport_stats/templatetags/crashreport_stats_tags.py
index 22f475d..8e6f31a 100644
--- a/crashreport_stats/templatetags/crashreport_stats_tags.py
+++ b/crashreport_stats/templatetags/crashreport_stats_tags.py
@@ -1,3 +1,5 @@
+"""Django template tags for crashreport statistics."""
+
 from django import template
 from django.template import loader
 
@@ -8,6 +10,7 @@
 def device_overview(
     title="General Information", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
 ):
+    """Render general device information."""
     t = template.loader.get_template(
         "crashreport_stats/tags/device_overview.html"
     )
@@ -20,6 +23,7 @@
 def device_crashreport_table(
     title="Crashreports", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
 ):
+    """Render device crashreport table."""
     t = template.loader.get_template(
         "crashreport_stats/tags/device_crashreport_table.html"
     )
@@ -36,6 +40,7 @@
 def device_update_history(
     title="Update History", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
 ):
+    """Render device update history."""
     t = template.loader.get_template(
         "crashreport_stats/tags/device_update_history.html"
     )
@@ -52,6 +57,7 @@
 def device_report_history(
     title="Report History", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
 ):
+    """Render device report history."""
     t = template.loader.get_template(
         "crashreport_stats/tags/device_report_history.html"
     )
@@ -62,6 +68,7 @@
 
 @register.simple_tag
 def versions_table(title="FP2 OS Versions", is_official_release="1"):
+    """Render versions table."""
     t = template.loader.get_template(
         "crashreport_stats/tags/versions_table.html"
     )
@@ -78,6 +85,7 @@
 def versions_pie_chart(
     title="FP2 Version Distribution", is_official_release="1"
 ):
+    """Render versions pie chart."""
     t = template.loader.get_template(
         "crashreport_stats/tags/versions_pie_chart.html"
     )
@@ -94,6 +102,7 @@
 def versions_area_chart(
     title="FP2 Version Distribution", is_official_release="1"
 ):
+    """Render versions area chart."""
     t = template.loader.get_template(
         "crashreport_stats/tags/versions_area_chart.html"
     )
@@ -108,6 +117,7 @@
 
 @register.simple_tag
 def versions_bar_chart(title="Version Stability", is_official_release="1"):
+    """Render versions bar chart."""
     t = template.loader.get_template(
         "crashreport_stats/tags/versions_bar_chart.html"
     )
diff --git a/crashreport_stats/urls.py b/crashreport_stats/urls.py
index cedbd73..04b008f 100644
--- a/crashreport_stats/urls.py
+++ b/crashreport_stats/urls.py
@@ -1,28 +1,29 @@
+"""URLs for accessing the Hiccup statistics."""
+
 from django.conf.urls import url
 from . import views
 from . import rest_endpoints
 
 
 urlpatterns = [
+    # Single device statistics page
     url(r"^device/$", views.device_stats, name="hiccup_stats_device"),
+    # Home page with device search form
     url(r"^$", views.home, name="device"),
+    # Version statistics overview pages
     url(r"^versions/$", views.versions_overview, name="hiccup_stats_versions"),
     url(
         r"^versions/all/$",
         views.versions_all_overview,
         name="hiccup_stats_versions_all",
     ),
+    # Single device statistics API
     url(
         r"^api/v1/device_overview/(?P<uuid>[a-f0-9-]+)/$",
         rest_endpoints.DeviceStat.as_view(),
         name="hiccup_stats_api_v1_device_overview",
     ),
     url(
-        r"^api/v1/status/$",
-        rest_endpoints.Status.as_view(),
-        name="hiccup_stats_api_v1_status",
-    ),
-    url(
         r"^api/v1/device_update_history/(?P<uuid>[a-f0-9-]+)/$",
         rest_endpoints.DeviceUpdateHistory.as_view(),
         name="hiccup_stats_api_v1_device_update_history",
@@ -37,6 +38,7 @@
         rest_endpoints.LogFileDownload.as_view(),
         name="hiccup_stats_api_v1_logfile_download",
     ),
+    # Version statistics API
     url(
         r"^api/v1/versions/$",
         rest_endpoints.VersionListView.as_view(),
@@ -57,4 +59,10 @@
         rest_endpoints.RadioVersionDailyListView.as_view(),
         name="hiccup_stats_api_v1_radio_version_daily",
     ),
+    # General statistics API
+    url(
+        r"^api/v1/status/$",
+        rest_endpoints.Status.as_view(),
+        name="hiccup_stats_api_v1_status",
+    ),
 ]
diff --git a/crashreport_stats/views.py b/crashreport_stats/views.py
index 54a67e7..1cc19c9 100644
--- a/crashreport_stats/views.py
+++ b/crashreport_stats/views.py
@@ -1,3 +1,5 @@
+"""Views for the Hiccup statistics."""
+
 from crashreports.models import *
 from django.http import HttpResponse, Http404, HttpResponseRedirect
 from django.shortcuts import render
@@ -13,15 +15,19 @@
 
 
 def is_fairphone_staff(user):
+    """Check if the user is part of the FairphoneSoftwareTeam group."""
     return user.groups.filter(name="FairphoneSoftwareTeam").exists()
 
 
 class DeviceUUIDForm(forms.Form):
+    """Form for searching devices by UUID."""
+
     uuid = forms.CharField(label="Device UUID:", max_length=100)
 
 
 @user_passes_test(is_fairphone_staff)
 def device_stats(request):
+    """Respond with statistics for a specific device."""
     template = loader.get_template("crashreport_stats/device.html")
     uuid = request.GET.get("uuid", "NO_UUID")
     if not Device.objects.filter(uuid=uuid).exists():
@@ -31,19 +37,27 @@
 
 @user_passes_test(is_fairphone_staff)
 def versions_all_overview(request):
+    """Respond with the distribution of official release versions."""
     template = loader.get_template("crashreport_stats/versions.html")
     return HttpResponse(template.render({"is_official_release": "1"}, request))
 
 
 @user_passes_test(is_fairphone_staff)
 def versions_overview(request):
+    """Respond with the distribution of non-official release versions."""
     template = loader.get_template("crashreport_stats/versions.html")
     return HttpResponse(template.render({"is_official_release": "2"}, request))
 
 
 @user_passes_test(is_fairphone_staff)
 def home(request):
-    """ The home view allows to search for devices. """
+    """Respond with a form for searching devices by UUID.
+
+    When using a HTTP GET method, the search device form view is returned.
+    The response additionally includes possible results if a HTTP POST message
+    was sent. If a single device was found, a redirect to the device
+    statistics of that device is sent.
+    """
     devices = None
     if request.method == "POST":
         # create a form instance and populate it with data from the request:
diff --git a/crashreports/__init__.py b/crashreports/__init__.py
index e69de29..8e574c7 100644
--- a/crashreports/__init__.py
+++ b/crashreports/__init__.py
@@ -0,0 +1 @@
+"""Hiccup crashreports application."""
diff --git a/crashreports/admin.py b/crashreports/admin.py
index 97b32e0..c45eabd 100644
--- a/crashreports/admin.py
+++ b/crashreports/admin.py
@@ -1,3 +1,5 @@
+"""Admin interface for manipulating devices, crashreports and heartbeats."""
+
 from django.contrib import admin
 from crashreports.models import Crashreport
 from crashreports.models import Device
@@ -7,19 +9,27 @@
 
 @admin.register(Crashreport)
 class CrashreportAdmin(admin.ModelAdmin):
+    """Manage Crashreports as admin user."""
+
     pass
 
 
 @admin.register(HeartBeat)
 class CrashreportAdmin(admin.ModelAdmin):
+    """Manage HeartBeats as admin user."""
+
     pass
 
 
 @admin.register(LogFile)
 class CrashreportAdmin(admin.ModelAdmin):
+    """Manage LogFiles as admin user."""
+
     pass
 
 
 @admin.register(Device)
 class CrashreportAdmin(admin.ModelAdmin):
+    """Manage Devices as admin user."""
+
     pass
diff --git a/crashreports/migrations/0001_initial.py b/crashreports/migrations/0001_initial.py
index 4542b4b..e091ce5 100644
--- a/crashreports/migrations/0001_initial.py
+++ b/crashreports/migrations/0001_initial.py
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 # Generated by Django 1.10.2 on 2016-11-22 14:00
+"""Initial database migration."""
+
 from __future__ import unicode_literals
 
 import crashreports.models
@@ -11,6 +13,7 @@
 
 
 class Migration(migrations.Migration):
+    """Initial database migration."""
 
     initial = True
 
diff --git a/crashreports/migrations/0002_auto_20170502_1155.py b/crashreports/migrations/0002_auto_20170502_1155.py
index e6e520c..62c05f0 100644
--- a/crashreports/migrations/0002_auto_20170502_1155.py
+++ b/crashreports/migrations/0002_auto_20170502_1155.py
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 # Generated by Django 1.10.2 on 2017-05-02 09:55
+"""Add indices to database models."""
+
 from __future__ import unicode_literals
 
 from django.db import migrations, models
@@ -8,6 +10,7 @@
 
 
 class Migration(migrations.Migration):
+    """Add indices to database models."""
 
     dependencies = [("crashreports", "0001_initial")]
 
diff --git a/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py b/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
index 403593e..eee052b 100644
--- a/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
+++ b/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
@@ -1,12 +1,14 @@
 # -*- coding: utf-8 -*-
-#
-# Extend the Crashreport and Heartbeat models to support the radio version.
+
+"""Extend the Crashreport and Heartbeat models to support radio versions."""
+
 from __future__ import unicode_literals
 
 from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
+    """Extend the Crashreport and Heartbeat models to support radio versions."""
 
     dependencies = [("crashreports", "0002_auto_20170502_1155")]
 
diff --git a/crashreports/migrations/__init__.py b/crashreports/migrations/__init__.py
index e69de29..28aab96 100644
--- a/crashreports/migrations/__init__.py
+++ b/crashreports/migrations/__init__.py
@@ -0,0 +1 @@
+"""Database migrations for crashreport models."""
diff --git a/crashreports/models.py b/crashreports/models.py
index 5120322..45c9da6 100644
--- a/crashreports/models.py
+++ b/crashreports/models.py
@@ -1,4 +1,6 @@
 # -*- coding: utf-8 -*-
+"""Models for devices, heartbeats, crashreports and log files."""
+
 from django.db import models
 from django.db import transaction
 
@@ -9,7 +11,10 @@
 
 
 class Device(models.Model):
+    """A device representing a phone that has been registered on Hiccup."""
+
     def __str__(self):
+        """Return the UUID as string representation of a device."""
         return self.uuid
 
     # for every device there is a django user
@@ -37,6 +42,7 @@
 
     @transaction.atomic
     def get_crashreport_key(self):
+        """Get the next key for a crashreport and update the ID-counter."""
         ret = self.next_per_crashreport_key
         self.next_per_crashreport_key = self.next_per_crashreport_key + 1
         self.save()
@@ -44,6 +50,7 @@
 
     @transaction.atomic
     def get_heartbeat_key(self):
+        """Get the next key for a heartbeat and update the ID-counter."""
         ret = self.next_per_heartbeat_key
         self.next_per_heartbeat_key = self.next_per_heartbeat_key + 1
         self.save()
@@ -51,6 +58,15 @@
 
 
 def crashreport_file_name(instance, filename):
+    """Generate the full path for new uploaded log files.
+
+    Args:
+        instance: The log file instance.
+        filename: The name of the actual log file.
+
+    Returns: The generated path including the file name.
+
+    """
     return "/".join(
         [
             "crashreport_uploads",
@@ -63,6 +79,8 @@
 
 
 class Crashreport(models.Model):
+    """A crashreport that was sent by a device."""
+
     BOOT_REASON_UNKOWN = "UNKNOWN"
     BOOT_REASON_KEYBOARD_POWER_ON = "keyboard power on"
     BOOT_REASON_RTC_ALARM = "RTC alarm"
@@ -91,18 +109,20 @@
 
     @transaction.atomic
     def get_logfile_key(self):
+        """Get the next key for a log file and update the ID-counter."""
         ret = self.next_logfile_key
         self.next_logfile_key = self.next_logfile_key + 1
         self.save()
         return ret
 
     def save(self, *args, **kwargs):
+        """Save the crashreport and set its local ID if it was not set."""
         if not self.device_local_id:
             self.device_local_id = self.device.get_crashreport_key()
         super(Crashreport, self).save(*args, **kwargs)
 
     def _get_uuid(self):
-        "Returns the person's full name."
+        """Return the device UUID."""
         return self.device.uuid
 
     uuid = property(_get_uuid)
@@ -110,6 +130,8 @@
 
 # TODO remove logfile_type or make it meaningful
 class LogFile(models.Model):
+    """A log file that was sent along with a crashreport."""
+
     logfile_type = models.TextField(max_length=36, default="last_kmsg")
     crashreport = models.ForeignKey(
         Crashreport, related_name="logfiles", on_delete=models.CASCADE
@@ -119,12 +141,15 @@
     created_at = models.DateTimeField(auto_now_add=True)
 
     def save(self, *args, **kwargs):
+        """Save the log file and set its local ID if it was not set."""
         if not self.crashreport_local_id:
             self.crashreport_local_id = self.crashreport.get_logfile_key()
         super(LogFile, self).save(*args, **kwargs)
 
 
 class HeartBeat(models.Model):
+    """A heartbeat that was sent by a device."""
+
     device = models.ForeignKey(
         Device,
         db_index=True,
@@ -140,12 +165,13 @@
     created_at = models.DateTimeField(auto_now_add=True)
 
     def save(self, *args, **kwargs):
+        """Save the heartbeat and set its local ID if it was not set."""
         if not self.device_local_id:
             self.device_local_id = self.device.get_heartbeat_key()
         super(HeartBeat, self).save(*args, **kwargs)
 
     def _get_uuid(self):
-        "Returns the person's full name."
+        """Return the device UUID."""
         return self.device.uuid
 
     uuid = property(_get_uuid)
diff --git a/crashreports/permissions.py b/crashreports/permissions.py
index c1efe79..60adee0 100644
--- a/crashreports/permissions.py
+++ b/crashreports/permissions.py
@@ -1,8 +1,19 @@
+"""Authorization permission classes for accessing the API."""
+
 from crashreports.models import Device
 from rest_framework.permissions import BasePermission
 
 
 def user_owns_uuid(user, uuid):
+    """Determine whether a user is owning the device with the given UUID.
+
+    Args:
+        user: The user making the request.
+        uuid: The UUID of the device to be manipulated.
+
+    Returns: True if the user owns the device.
+
+    """
     try:
         device = Device.objects.get(user=user)
     except:
@@ -13,6 +24,18 @@
 
 
 def user_is_hiccup_staff(user):
+    """Determine whether a user is part of the Hiccup staff.
+
+    Returns true if either the user is part of the group
+    "FairphoneSoftwareTeam", or he/she has all permissions for manipulating
+    crashreports, heartbeats and logfiles.
+
+    Args:
+        user: The user making the request.
+
+    Returns: True if user is part of the Hiccup staff.
+
+    """
     if user.groups.filter(name="FairphoneSoftwareTeam").exists():
         return True
     else:
@@ -35,12 +58,18 @@
 
 
 class HasStatsAccess(BasePermission):
+    """Authorization requires to be part of the Hiccup staff."""
+
     def has_permission(self, request, view):
+        """Check if user is part of the Hiccup staff."""
         return user_is_hiccup_staff(request.user)
 
 
 class HasRightsOrIsDeviceOwnerDeviceCreation(BasePermission):
+    """Authorization requires to be part of Hiccup staff or device owner."""
+
     def has_permission(self, request, view):
+        """Return true if user is part of Hiccp staff or device owner."""
         if user_is_hiccup_staff(request.user):
             return True
 
diff --git a/crashreports/urls.py b/crashreports/urls.py
index b876aaf..e5489c4 100644
--- a/crashreports/urls.py
+++ b/crashreports/urls.py
@@ -1,3 +1,5 @@
+"""URLs for accessing devices, crashreports, logfiles and heartbeats."""
+
 from django.conf.urls import url
 from . import rest_api_devices
 from . import rest_api_crashreports
diff --git a/hiccup/__init__.py b/hiccup/__init__.py
index e69de29..02d7853 100644
--- a/hiccup/__init__.py
+++ b/hiccup/__init__.py
@@ -0,0 +1 @@
+"""Hiccup Django REST API Configuration."""
diff --git a/hiccup/allauth_adapters.py b/hiccup/allauth_adapters.py
index 4eb3a06..584fe54 100644
--- a/hiccup/allauth_adapters.py
+++ b/hiccup/allauth_adapters.py
@@ -1,14 +1,42 @@
+"""Allauth adapter for authenticating requests using Google OAuth."""
+
 from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
 from allauth.account.adapter import DefaultAccountAdapter
+from allauth.socialaccount.forms import SignupForm
+from allauth.socialaccount.models import SocialLogin
 from django.core.exceptions import PermissionDenied
 from django.contrib.auth.models import Group
+from django.http import HttpRequest
 
 
 class FairphoneAccountAdapter(DefaultSocialAccountAdapter):
+    """Account adapter for existing Google accounts."""
+
     def is_open_for_signup(self, request, sociallogin):
+        """Allow signup."""
         return True
 
-    def save_user(self, request, sociallogin, form=None):
+    def save_user(
+        self,
+        request: HttpRequest,
+        sociallogin: SocialLogin,
+        form: SignupForm = None,
+    ):
+        """Save a user to the database.
+
+        Additionally add the user to the FairphoneSoftwareTeam group if his
+        or her account was issued by Fairphone, i.e. ends with "@fairphone.com".
+
+        Args:
+            request: The HTTP request.
+            sociallogin:
+                SocialLogin instance representing a Google user that is in
+                the process of being logged in.
+            form: Request form (not used).
+
+        Returns: The newly created user from the local database.
+
+        """
         u = DefaultSocialAccountAdapter.save_user(
             self, request, sociallogin, form=None
         )
@@ -17,7 +45,24 @@
             g.user_set.add(u)
         return u
 
-    def populate_user(self, request, sociallogin, data):
+    def populate_user(
+        self, request: HttpRequest, sociallogin: SocialLogin, data: dict
+    ):
+        """Populate an already existing user instance.
+
+        The permission is denied if the Google account was not issued by
+        Fairphone, i.e. does not end with "@fairphone.com".
+
+        Args:
+            request: The HTTP request.
+            sociallogin:
+                SocialLogin instance representing a Google user that is in
+                the process of being logged in.
+            data: Common user data fields.
+
+        Returns: The user from the database.
+
+        """
         u = DefaultSocialAccountAdapter.populate_user(
             self, request, sociallogin, data
         )
@@ -27,5 +72,12 @@
 
 
 class FormAccountAdapter(DefaultAccountAdapter):
+    """Account adapter for signing up using a form.
+
+    Signup is not allowed using Hiccup, only existing Fairphone Google accounts
+    can be used.
+    """
+
     def is_open_for_signup(self, request):
+        """Do not allow signup."""
         return False
diff --git a/hiccup/wsgi.py b/hiccup/wsgi.py
index 025bc4e..9b53dae 100644
--- a/hiccup/wsgi.py
+++ b/hiccup/wsgi.py
@@ -1,5 +1,5 @@
-"""
-WSGI config for myproject project.
+"""WSGI config for Hiccup server project.
+
 It exposes the WSGI callable as a module-level variable named ``application``.
 For more information on this file, see
 https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/