From d3ec0e899828a3704224e060e404ff45802af300 Mon Sep 17 00:00:00 2001 From: Gergely Polonkai Date: Tue, 20 Oct 2015 16:26:25 +0200 Subject: [PATCH] Pylint happiness! --- accounts/tests.py | 53 ++++- accounts/urls.py | 18 +- accounts/views.py | 19 +- api/serializers.py | 22 +- api/tests.py | 36 +++- api/urls.py | 5 +- api/views.py | 26 ++- booking/admin.py | 9 +- booking/ducklevel.py | 16 ++ booking/models.py | 117 +++++++++-- booking/templatetags/booking_tags.py | 30 ++- booking/tests.py | 298 ++++++++++++++++++--------- booking/urls.py | 26 ++- booking/views.py | 10 +- duckbook/urls.py | 36 ++-- 15 files changed, 548 insertions(+), 173 deletions(-) diff --git a/accounts/tests.py b/accounts/tests.py index cde47a9..7457558 100644 --- a/accounts/tests.py +++ b/accounts/tests.py @@ -1,22 +1,37 @@ +# -*- coding: utf-8 +""" +Account management module for the Duck Booking Tool backend +""" + from django.test import TestCase, Client from django_webtest import WebTest from django.contrib.auth.models import User from django.contrib.auth.forms import UserCreationForm class FrontTest(TestCase): + """ + Test front-end capabilities of the accounts module + """ + def setUp(self): self.client = Client() - self.admin = User.objects.create_user( - username = 'admin', - password = 'password') - self.admin.save() + self.admin = User.objects.create_user(username='admin', + password='password') def test_login_page(self): + """ + Test for the existence of the login page + """ + response = self.client.get('/accounts/login') self.assertEqual(response.status_code, 200) def test_login(self): + """ + Test login functionality + """ + response = self.client.post('/accounts/login', { 'next': '/', 'username': 'admin', @@ -25,17 +40,33 @@ class FrontTest(TestCase): self.assertRedirects(response, '/') def test_logout(self): - self.client.login(username = 'admin', password = 'aeou') + """ + Test the logout page + """ + + self.client.login(username='admin', password='aeou') response = self.client.get('/accounts/logout') self.assertRedirects(response, '/') def test_registration_page(self): + """ + Test for existence of the registration page + """ + response = self.client.get('/accounts/register') self.assertEqual(response.status_code, 200) class RegFormTest(WebTest): + """ + Test case for the registration form + """ + def test_valid_data(self): + """ + Test valid registration without actual HTTP requests + """ + form_data = { 'username': 'test', 'password1': 'password', @@ -51,6 +82,10 @@ class RegFormTest(WebTest): self.assertNotEqual(user.password, 'password') def test_empty(self): + """ + Test empty registration form + """ + form_data = {} form = UserCreationForm(form_data) self.assertFalse(form.is_valid()) @@ -61,11 +96,19 @@ class RegFormTest(WebTest): }) def test_form_error(self): + """ + Test incomplete registration + """ + page = self.app.get('/accounts/register') page = page.form.submit() self.assertContains(page, "This field is required.") def test_form_success(self): + """ + Test for successful registrations + """ + page = self.app.get('/accounts/register') page.form['username'] = 'test' page.form['password1'] = 'password' diff --git a/accounts/urls.py b/accounts/urls.py index 75154f8..26d40e3 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -1,24 +1,28 @@ -from django.conf.urls import patterns, url +# -*- coding: utf-8 +""" +URL patterns for the accounts module +""" + +from django.conf.urls import url from .views import RegistrationFormView -urlpatterns = patterns( - '', +urlpatterns = [ url( r'^register$', RegistrationFormView.as_view(), - name = 'register' + name='register' ), url( r'^login$', 'django.contrib.auth.views.login', {'template_name': 'accounts/login.html'}, - name = 'login' + name='login' ), url( r'^logout$', 'django.contrib.auth.views.logout', {'next_page': 'booking:list'}, - name = 'logout' + name='logout' ), -) +] diff --git a/accounts/views.py b/accounts/views.py index 76ccea6..30c84b3 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 +""" +Views for the accounts module +""" + from django.shortcuts import render from django.views import generic from django.http import HttpResponseRedirect @@ -5,14 +10,26 @@ from django.core.urlresolvers import reverse from django.contrib.auth.forms import UserCreationForm class RegistrationFormView(generic.View): + """ + Class to display the registration form + """ + form_class = UserCreationForm template_name = 'accounts/registration.html' def get(self, request): + """ + Implementation of the GET method + """ + form = self.form_class() - return render(request, self.template_name, { 'form': form }) + return render(request, self.template_name, {'form': form}) def post(self, request): + """ + Implementation of the POST method + """ + form = self.form_class(request.POST) if form.is_valid(): diff --git a/api/serializers.py b/api/serializers.py index 77a4846..96ef0cc 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,10 +1,18 @@ # -*- coding: utf-8 -*- +""" +Serializers for the Duck Booking Tool API +""" + from django.core.exceptions import ImproperlyConfigured from rest_framework import serializers from booking.models import Duck, Competence, DuckCompetence class NamespacedSerializer(serializers.HyperlinkedModelSerializer): + """ + HyperlinkedModelSerializer with URL namespace support + """ + def __init__(self, *args, **kwargs): if not hasattr(self.Meta, 'url_namespace') or self.Meta.url_namespace is None: raise ImproperlyConfigured("namespace must be set!") @@ -17,7 +25,7 @@ class NamespacedSerializer(serializers.HyperlinkedModelSerializer): if not self.url_namespace.endswith(':'): self.url_namespace += ':' - return super(NamespacedSerializer, self).__init__(*args, **kwargs) + super(NamespacedSerializer, self).__init__(*args, **kwargs) def build_url_field(self, field_name, model_class): field_class, field_kwargs = super(NamespacedSerializer, self) \ @@ -32,12 +40,20 @@ class NamespacedSerializer(serializers.HyperlinkedModelSerializer): return field_class, field_kwargs class CompetenceSerializer(NamespacedSerializer): + """ + Serializer for Competence objects + """ + class Meta: url_namespace = 'api' model = Competence fields = ('url', 'name',) class DuckCompetenceSerializer(NamespacedSerializer): + """ + Serializer for DuckCompetence objects + """ + comp = CompetenceSerializer() class Meta: @@ -46,6 +62,10 @@ class DuckCompetenceSerializer(NamespacedSerializer): fields = ('comp', 'up_minutes', 'down_minutes',) class DuckSerializer(NamespacedSerializer): + """ + Serializer for Duck objects + """ + competences = DuckCompetenceSerializer(many=True) class Meta: diff --git a/api/tests.py b/api/tests.py index bcca56e..45fa6d1 100644 --- a/api/tests.py +++ b/api/tests.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -from django.test import TestCase, Client +""" +Test cases for API calls +""" + from django.contrib.auth.models import User from django.conf import settings from django_webtest import WebTest @@ -10,6 +13,10 @@ from booking.ducklevel import level_to_up_minutes from booking.models import Species, Location, Duck, Competence, DuckCompetence class DuckClassTest(WebTest): + """ + Test case for duck related API calls + """ + csrf_checks = False def setUp(self): @@ -46,10 +53,18 @@ class DuckClassTest(WebTest): down_minutes=0) def test_book_nonlogged(self): + """ + Test booking without logging in + """ + page = self.app.post('/api/v1/ducks/1/book/', expect_errors=True) self.assertEqual(page.status_code, 403) def test_book_nonexist(self): + """ + Test booking a non-existing duck + """ + # Try to book a non-existing duck page = self.app.post( '/api/v1/ducks/9999/book/', @@ -71,6 +86,10 @@ class DuckClassTest(WebTest): self.assertEqual(404, page.status_code) def test_book_warn(self): + """ + Test duck booking for a competence the duck is not good at + """ + url = '/api/v1/ducks/%d/book/' % self.duck.pk comp_none = Competence.objects.create(name='test3', added_by=self.user) @@ -107,6 +126,10 @@ class DuckClassTest(WebTest): self.assertEquals(page_json['status'], 'ok') def test_book_good(self): + """ + Test duck booking for a competence the duck is good at + """ + test_data = { "competence": self.comp_good.pk } @@ -127,6 +150,10 @@ class DuckClassTest(WebTest): self.assertEqual('already-booked', page_json['status']) def test_duck_donation(self): + """ + Test duck donating functionality + """ + # Duck donation should not be allowed without logging in page = self.app.get('/api/v1/ducks/donate/', expect_errors=True) self.assertEquals(page.status_code, 403) @@ -135,16 +162,19 @@ class DuckClassTest(WebTest): page = self.app.post('/api/v1/ducks/donate/', expect_errors=True) self.assertEquals(page.status_code, 403) - page = self.app.post( + self.app.post( '/api/v1/ducks/donate/', params={ 'species': 1, 'color': '123456', }, user=self.user) - page_json = json.loads(page.content) def test_duck_details(self): + """ + Test duck details view + """ + url = '/api/v1/ducks/%d/' % self.duck.pk page = self.app.get(url) self.assertEqual(200, page.status_code) diff --git a/api/urls.py b/api/urls.py index daa482a..2615a01 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,4 +1,7 @@ -from django.conf.urls import patterns, url, include +# -*- coding: utf-8 +""" +URL definitions for version 1 of the Duck Booking Tool API +""" from rest_framework import routers diff --git a/api/views.py b/api/views.py index c4dfe7f..b876c29 100644 --- a/api/views.py +++ b/api/views.py @@ -1,22 +1,32 @@ # -*- coding: utf-8 -*- +""" +Views for the Duck Booking Tool API +""" + from django.conf import settings -from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 from rest_framework import viewsets from rest_framework.decorators import detail_route, list_route from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from .serializers import DuckSerializer, CompetenceSerializer, \ - DuckCompetenceSerializer -from booking.models import Duck, Competence, Booking, DuckCompetence +from .serializers import DuckSerializer, CompetenceSerializer +from booking.models import Duck, Competence, Booking class DuckViewSet(viewsets.ModelViewSet): + """ + View set for duck handling + """ + serializer_class = DuckSerializer queryset = Duck.objects.all() @detail_route(methods=['post'], permission_classes=[IsAuthenticated]) def book(self, request, pk=None): + """ + API call to book a duck + """ + duck = self.get_object() competence = get_object_or_404(Competence, pk=request.data['competence']) force = request.data.get('force', False) @@ -54,8 +64,16 @@ class DuckViewSet(viewsets.ModelViewSet): @list_route(methods=['post'], permission_classes=[IsAuthenticated]) def donate(self, request): + """ + API call to donate a new duck + """ + return Response({'Woot!'}) class CompetenceViewSet(viewsets.ModelViewSet): + """ + View set for competence handling + """ + serializer_class = CompetenceSerializer queryset = Competence.objects.all() diff --git a/booking/admin.py b/booking/admin.py index be4d63c..e5939a6 100644 --- a/booking/admin.py +++ b/booking/admin.py @@ -1,5 +1,12 @@ +# -*- coding: utf-8 +""" +Administration site definition for the Duck Booking Tool +""" + from django.contrib import admin -from booking.models import Species, Location, Competence, Duck, Booking, DuckCompetence, DuckName, DuckNameVote +from booking.models import Species, Location, Competence, Duck, \ + Booking, DuckCompetence, DuckName, \ + DuckNameVote admin.site.register(Species) admin.site.register(Location) diff --git a/booking/ducklevel.py b/booking/ducklevel.py index 8485523..d53b76a 100644 --- a/booking/ducklevel.py +++ b/booking/ducklevel.py @@ -1,19 +1,35 @@ +# -*- coding: utf-8 +""" +Duck level calculations +""" + from django.conf import settings import math def level_to_up_minutes(level): + """ + Convert duck level to up minutes + """ + if level == 0: return 0 return 2 * pow(10, level) def level_to_down_minutes(level): + """ + Convert duck level to down minutes + """ + if level == 0: return 0 return 20 * pow(10, level) def minutes_to_level(up_minutes, down_minutes): + """ + Convert booking minutes to duck level + """ minutes = up_minutes + down_minutes / 10 level = 0 if minutes <= 0 else min(settings.MAX_DUCK_LEVEL, math.floor(math.log10(minutes))) diff --git a/booking/models.py b/booking/models.py index 9d9d296..977835a 100644 --- a/booking/models.py +++ b/booking/models.py @@ -1,32 +1,45 @@ # -*- coding: utf-8 -*- +""" +Models for the Duck Booking Tool +""" -from django.db import models -from django.contrib.auth.models import User -from django.utils import timezone from django.conf import settings +from django.contrib.auth.models import User +from django.db import models +from django.utils import timezone +from django.utils.encoding import python_2_unicode_compatible from fuzzywuzzy import fuzz from .ducklevel import minutes_to_level +@python_2_unicode_compatible class Species(models.Model): - """Model to hold the Ducks’ species""" + """ + Model to hold the Ducks’ species + """ name = models.CharField(max_length=40, unique=True) def __str__(self): return self.name +@python_2_unicode_compatible class Location(models.Model): - """Model to hold the possible locations of the Ducks""" + """ + Model to hold the possible locations of the Ducks + """ name = models.CharField(max_length=50) def __str__(self): return self.name +@python_2_unicode_compatible class Competence(models.Model): - """Model to hold Duck competences""" + """ + Model to hold Duck competences + """ name = models.CharField(max_length=255, unique=True) added_at = models.DateTimeField(default=timezone.now) @@ -37,20 +50,27 @@ class Competence(models.Model): @classmethod def get_similar_comps(cls, name): + """ + Get competence names similar to name + """ + comps = cls.objects.values_list('name', flat=True) ret = () - for c in comps: - r = fuzz.ratio(name.lower(), c.lower()) + for competence in comps: + similarity = fuzz.ratio(name.lower(), competence.lower()) # This ratio is subject to change - if r > settings.MIN_FUZZY_SIMILARITY: - ret = ret + (c,) + if similarity > settings.MIN_FUZZY_SIMILARITY: + ret = ret + (competence,) return ret +@python_2_unicode_compatible class Duck(models.Model): - """Model to hold Duck data""" + """ + Model to hold Duck data + """ name = models.CharField(max_length=80, null=True, blank=True) color = models.CharField(max_length=6) @@ -72,6 +92,11 @@ class Duck(models.Model): return self.name def age(self): + """ + Get the age of the duck (time since the duck has been registered + in the tool) + """ + seconds_d = timezone.now() - self.donated_at seconds = seconds_d.total_seconds() @@ -81,6 +106,10 @@ class Duck(models.Model): return seconds def dpx(self): + """ + Get the Duck Popularity indeX for this duck + """ + all_time = Booking.total_booking_time() duck_time = Booking.duck_booking_time(self) @@ -90,18 +119,25 @@ class Duck(models.Model): return Booking.duck_booking_time(self) / Booking.total_booking_time() def booked_by(self): - l = self.booking_set.filter(end_ts=None) + """ + Get the user who is currently using the duck + """ - if len(l) == 0: + booking_list = self.booking_set.filter(end_ts=None) + + if len(booking_list) == 0: return None - if len(l) > 1: + if len(booking_list) > 1: raise RuntimeError(u"Duck is booked more than once!") - return l[0].user + return booking_list[0].user +@python_2_unicode_compatible class DuckName(models.Model): - """Model to hold name suggestions for Ducks""" + """ + Model to hold name suggestions for Ducks + """ duck = models.ForeignKey(Duck) name = models.CharField(max_length=60) @@ -110,16 +146,31 @@ class DuckName(models.Model): closed_by = models.ForeignKey(User, related_name='+') closed_at = models.DateTimeField(null=True) + def __str__(self): + return "{0}, suggested by {1}".format(self.name, + self.suggested_by) + +@python_2_unicode_compatible class DuckNameVote(models.Model): - """Model to hold votes to Duck names""" + """ + Model to hold votes to Duck names + """ duck_name = models.ForeignKey(DuckName) vote_timestamp = models.DateTimeField(default=timezone.now) voter = models.ForeignKey(User) upvote = models.BooleanField(default=True) + def __str__(self): + return "{0} voted {1} for {2}".format(self.voter, + "up" if upvote else "down", + self.duck_name) + +@python_2_unicode_compatible class DuckCompetence(models.Model): - """Duck competence governor table""" + """ + Duck competence governor table + """ duck = models.ForeignKey(Duck, related_name='competences') comp = models.ForeignKey(Competence, related_name='ducks') @@ -127,13 +178,26 @@ class DuckCompetence(models.Model): down_minutes = models.IntegerField(default=0) def level(self): + """ + Return the actual level of a duck + """ + return minutes_to_level(self.up_minutes, self.down_minutes) + def __str__(self): + return "{0} with +{1}/-{2} minutes in {3}".format(self.duck, + self.up_minutes, + self.down_minutes, + self.comp) + class Meta: unique_together = ('duck', 'comp') +@python_2_unicode_compatible class Booking(models.Model): - """Duck booking governor table""" + """ + Duck booking governor table + """ duck = models.ForeignKey(Duck) user = models.ForeignKey(User) @@ -144,6 +208,10 @@ class Booking(models.Model): @classmethod def total_booking_time(cls): + """ + Get the sum of booked hours for all ducks + """ + return cls.objects.filter( start_ts__isnull=False, end_ts__isnull=False).extra( @@ -154,10 +222,19 @@ class Booking(models.Model): @classmethod def duck_booking_time(cls, duck): + """ + Get the sum of booked hours of a duck + """ + return cls.objects.filter( start_ts__isnull=False, - end_ts__isnull=False, duck = duck).extra( + end_ts__isnull=False, duck=duck).extra( select={ 'amount': 'sum(strftime(%s, end_ts) - strftime(%s, start_ts))' }, select_params=('%s', '%s'))[0].amount + + def __str__(self): + return "{0} booked by {1} since {2}".format(self.duck, + self.user, + self.start_ts) diff --git a/booking/templatetags/booking_tags.py b/booking/templatetags/booking_tags.py index ec124af..d82dd77 100644 --- a/booking/templatetags/booking_tags.py +++ b/booking/templatetags/booking_tags.py @@ -1,17 +1,31 @@ +# -*- coding: utf-8 +""" +Template tags for the booking templates +""" + from django import template import math register = template.Library() -def is_number(s): +def is_number(string): + """ + Check if s is a number in string representation + """ + try: - float(s) + float(string) + return True except ValueError: return False @register.filter -def age_format(value, arg = None): +def age_format(value, arg=None): + """ + Create human readable string from the duck age + """ + if not is_number(value): return value @@ -30,7 +44,7 @@ def age_format(value, arg = None): remainder = remainder % 2592000 if months > 0: - if (ret != ""): + if ret != "": ret += " " ret += u"%d month%s" % (months, "" if months == 1 else "s") @@ -45,7 +59,7 @@ def age_format(value, arg = None): days = 1 if days > 0: - if (ret != ""): + if ret != "": ret += " " ret += u"%d day%s" % (days, "" if days == 1 else "s") @@ -60,7 +74,7 @@ def age_format(value, arg = None): remainder = remainder % 3600 if hours > 0: - if (ret != ""): + if ret != "": ret += " " ret += u"%d hour%s" % (hours, "" if hours == 1 else "s") @@ -68,7 +82,7 @@ def age_format(value, arg = None): minutes = math.floor(remainder / 60) if minutes > 0: - if (ret != ""): + if ret != "": ret += " " ret += u"%d minute%s" % (minutes, "" if minutes == 1 else "s") @@ -76,7 +90,7 @@ def age_format(value, arg = None): seconds = round(remainder % 60) if seconds > 0: - if (ret != ""): + if ret != "": ret += " " ret += u"%d second%s" % (seconds, "" if seconds == 1 else "s") diff --git a/booking/tests.py b/booking/tests.py index 015f32e..02dc18a 100644 --- a/booking/tests.py +++ b/booking/tests.py @@ -1,70 +1,96 @@ # -*- coding: utf-8 -*- -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse -from django.test import TestCase, Client -from django.utils import timezone +""" +Tests for the Duck Booking Tool frontend +""" import datetime +from django.conf import settings +from django.contrib.auth.models import User +from django.test import TestCase, Client +from django.utils import timezone + from .ducklevel import level_to_up_minutes, level_to_down_minutes, minutes_to_level from .templatetags import booking_tags from .models import Duck, Competence, DuckCompetence, Species, Location, Booking class FrontTest(TestCase): + """ + Test case for the front end + """ + def setUp(self): self.client = Client() def test_index_page(self): + """ + Test for the existence of the main page + """ + response = self.client.get('/') self.assertEqual(response.status_code, 200) def test_vocabulary_page(self): + """ + Test for the existence of the vocabulary page + """ + response = self.client.get('/vocabulary.html') self.assertEqual(response.status_code, 200) def test_terms_page(self): + """ + Test for the existence of the terms page + """ + response = self.client.get('/terms.html') self.assertEqual(response.status_code, 200) def test_disclaimer_page(self): + """ + Test for the existence of the disclaimer page + """ + response = self.client.get('/disclaimer.html') self.assertEqual(response.status_code, 200) class DuckCompLevelTest(TestCase): + """ + Test case for competence level calculation + """ + def setUp(self): user = User.objects.create_user(username='test', password='test') - species = Species(name='test species') - species.save() + species = Species.objects.create(name='test species') - location = Location(name='test location') - location.save() + location = Location.objects.create(name='test location') - duck = Duck( - species=species, - location=location, - donated_by=user) - duck.save() + duck = Duck.objects.create(species=species, + location=location, + donated_by=user) - comp = Competence( - name='testing', - added_by=user) - comp.save() + comp = Competence.objects.create(name='testing', + added_by=user) - self.duckcomp = DuckCompetence( - duck = duck, - comp = comp, - up_minutes = 0, - down_minutes =0) + self.duckcomp = DuckCompetence.objects.create(duck=duck, + comp=comp, + up_minutes=0, + down_minutes=0) def test_sane_max(self): + """ + Test if the MAX_DUCK_LEVEL setting has a sane value + """ + self.assertGreater( settings.MAX_DUCK_LEVEL, 0, - msg = "MAX_DUCK_LEVEL must be greater than zero!") + msg="MAX_DUCK_LEVEL must be greater than zero!") def test_max_minutes(self): - """Test if level can not go above settings.MAX_DUCK_LEVEL)""" + """ + Test if level can not go above settings.MAX_DUCK_LEVEL) + """ max_up_minutes = level_to_up_minutes(settings.MAX_DUCK_LEVEL) double_minutes = level_to_up_minutes(settings.MAX_DUCK_LEVEL * 2) @@ -86,6 +112,10 @@ class DuckCompLevelTest(TestCase): self.assertEqual(level, settings.MAX_DUCK_LEVEL) def test_conversions(self): + """ + Test minutes to level conversations + """ + for i in range(1, settings.MAX_DUCK_LEVEL): up_minutes = level_to_up_minutes(i) down_minutes = level_to_down_minutes(i) @@ -93,12 +123,16 @@ class DuckCompLevelTest(TestCase): up_level = minutes_to_level(up_minutes, 0) down_level = minutes_to_level(0, down_minutes) - self.assertEqual(up_level, i, msg = "Test failed for value %d" % i) + self.assertEqual(up_level, i, msg="Test failed for value %d" % i) self.assertEqual( down_level, i, - msg = "Test failed for value %d" % i) + msg="Test failed for value %d" % i) def test_level_to_minutes(self): + """ + Test level to minutes conversations + """ + self.assertEqual(level_to_up_minutes(0), 0) self.assertEqual(level_to_up_minutes(1), 20) self.assertEqual(level_to_up_minutes(2), 200) @@ -114,11 +148,19 @@ class DuckCompLevelTest(TestCase): self.assertEqual(level_to_down_minutes(5), 2000000) def test_no_comp(self): + """ + Test if level equals 0 if minutes count is 0 + """ + self.duckcomp.up_minutes = 0 self.duckcomp.down_minutes = 0 self.assertEquals(self.duckcomp.level(), 0) def test_comp_levels(self): + """ + Test competence level calculation + """ + self.duckcomp.down_minutes = 0 for lvl in range(1, settings.MAX_DUCK_LEVEL): @@ -127,16 +169,33 @@ class DuckCompLevelTest(TestCase): self.assertEqual(self.duckcomp.level(), lvl) def test_high_minutes(self): + """ + Test duck level calculation with a very high amount of minutes + """ + self.duckcomp.up_minutes = level_to_up_minutes(settings.MAX_DUCK_LEVEL) self.duckcomp.down_minutes = level_to_down_minutes(settings.MAX_DUCK_LEVEL) self.assertEqual(self.duckcomp.level(), settings.MAX_DUCK_LEVEL) class DuckAgeTest(TestCase): + """ + Tests related to duck age + """ + def test_duck_is_from_the_future(self): - future_duck = Duck(donated_at = timezone.now() + datetime.timedelta(days = 2)) + """ + Test if the duck came from the future (ie. donation time is in + the future) + """ + + future_duck = Duck(donated_at=timezone.now() + datetime.timedelta(days=2)) self.assertEqual(future_duck.age(), -1) def test_duck_age_formatter(self): + """ + Test duck age formatter + """ + self.assertEqual(booking_tags.age_format("aoeu"), "aoeu") self.assertEqual(booking_tags.age_format(0), "a few moments") self.assertEqual(booking_tags.age_format(1), "1 second") @@ -173,75 +232,105 @@ class DuckAgeTest(TestCase): self.assertEqual(booking_tags.age_format(63072000), "2 years") class BookingTimeTest(TestCase): - duck1 = None - duck2 = None + """ + Test case for calculating booking time and popularity + """ def setUp(self): user = User() user.save() - species = Species(name = 'duck') - species.save() + species = Species.objects.create(name='duck') + location = Location.objects.create(name='start') - location = Location(name = 'start') - location.save() + self.duck1 = Duck.objects.create(species=species, + location=location, + donated_by=user) - self.duck1 = Duck(species = species, location = location, donated_by = user) - self.duck1.save() - - competence = Competence(name = 'test', added_by = user) - competence.save() + competence = Competence.objects.create(name='test', + added_by=user) now = timezone.now() - booking = Booking(duck = self.duck1, start_ts = now - datetime.timedelta(days = 2), end_ts = now - datetime.timedelta(days = 1), user = user, comp_req = competence) - booking.save() + Booking.objects.create(duck=self.duck1, + start_ts=now - datetime.timedelta(days=2), + end_ts=now - datetime.timedelta(days=1), + user=user, + comp_req=competence) - self.duck2 = Duck(species = species, location = location, donated_by = user) - self.duck2.save() + self.duck2 = Duck.objects.create(species=species, + location=location, + donated_by=user) - booking = Booking(duck = self.duck2, start_ts = now - datetime.timedelta(days = 3), end_ts = now - datetime.timedelta(days = 2), user = user, comp_req = competence) - booking.save() + Booking.objects.create(duck=self.duck2, + start_ts=now - datetime.timedelta(days=3), + end_ts=now - datetime.timedelta(days=2), + user=user, + comp_req=competence) - booking = Booking(duck = self.duck2, start_ts = now - datetime.timedelta(days = 2), end_ts = now - datetime.timedelta(days = 1), user = user, comp_req = competence) - booking.save() + Booking.objects.create(duck=self.duck2, + start_ts=now - datetime.timedelta(days=2), + end_ts=now - datetime.timedelta(days=1), + user=user, + comp_req=competence) def test_total_booking_time(self): - self.assertEqual(Booking.total_booking_time(), 259200) + """ + Test total booking time + """ + + self.assertEqual(259200, Booking.total_booking_time()) def test_duck_booking_time(self): - self.assertEqual(Booking.duck_booking_time(self.duck1), 86400) - self.assertEqual(Booking.duck_booking_time(self.duck2), 172800) + """ + Test duck booking time + """ + + self.assertEqual(86400, Booking.duck_booking_time(self.duck1)) + self.assertEqual(172800, Booking.duck_booking_time(self.duck2)) def test_dpx(self): - self.assertEqual(self.duck1.dpx(), 1/3) - self.assertEqual(self.duck2.dpx(), 2/3) + """ + Test Duck Popularity indeX calculation + """ + + self.assertEqual(1/3, self.duck1.dpx()) + self.assertEqual(2/3, self.duck2.dpx()) class TestListing(TestCase): + """ + Test case for duck listing + """ + def setUp(self): self.client = Client() - species = Species() - species.save() + species = Species.objects.create() + loc = Location.objects.create() + user = User.objects.create_user(username='test', + password='test') - loc = Location() - loc.save() - - user = User() - user.save() - - self.duck = Duck(species = species, location = loc, donated_by = user) - self.duck.save() + self.duck = Duck.objects.create(species=species, + location=loc, + donated_by=user) def test_front_page(self): - response = self.client.get('/') - self.assertEqual(response.status_code, 200) + """ + Test existence of the front page + """ - self.assertEqual(len(response.context['duck_list']), 1) + response = self.client.get('/') + self.assertEqual(200, response.status_code) + + self.assertEqual(1, len(response.context['duck_list'])) class SimilarCompTest(TestCase): + """ + Test case for competence name fuzzy search + """ + def setUp(self): - admin = User(username = 'admin') - admin.save() + admin = User.objects.create_user(username='admin', + password='test') competence_list = ( 'Creativity', @@ -251,54 +340,75 @@ class SimilarCompTest(TestCase): 'TCSH', ) - for c in competence_list: - comp = Competence(name = c, added_by = admin) - comp.save() + for competence in competence_list: + Competence.objects.create(name=competence, + added_by=admin) def test_good_similar_competences(self): - l = Competence.get_similar_comps('perl') - self.assertEquals(len(l), 1) + """ + Test similar competence list with different inputs + """ - l = Competence.get_similar_comps('pzthon') - self.assertEquals(len(l), 1) + comp_list = Competence.get_similar_comps('perl') + self.assertEquals(1, len(comp_list)) - l = Competence.get_similar_comps(u'kreativitás') - self.assertEqual(len(l), 1) + comp_list = Competence.get_similar_comps('pzthon') + self.assertEquals(1, len(comp_list)) + + comp_list = Competence.get_similar_comps(u'kreativitás') + self.assertEqual(1, len(comp_list)) def test_bad_similar_competence(self): - l = Competence.get_similar_comps('development') - self.assertEqual(len(l), 0) + """ + Test similar competence list with a totally new and unmatching + competence name + """ + + comp_list = Competence.get_similar_comps('development') + self.assertEqual(0, len(comp_list)) class BookingTest(TestCase): + """ + Test duck booking functionality + """ + def setUp(self): - spec = Species.objects.create(name='test') - - loc = Location.objects.create(name='test') - + self.spec = Species.objects.create(name='test') + self.loc = Location.objects.create(name='test') self.user = User.objects.create_user(username='test') - - self.booked_duck = Duck.objects.create(species=spec, - location=loc, + self.booked_duck = Duck.objects.create(species=self.spec, + location=self.loc, donated_by=self.user) - self.comp = Competence.objects.create(name='test', added_by=self.user) - booking = Booking.objects.create(duck=self.booked_duck, - user=self.user, - comp_req=self.comp) - - self.unbooked_duck = Duck.objects.create(species=spec, - location=loc, - donated_by=self.user) + Booking.objects.create(duck=self.booked_duck, + user=self.user, + comp_req=self.comp) def test_booked_duck(self): + """ + Test if booked duck returns the booking user from booked_by() + """ + self.assertNotEqual(self.booked_duck.booked_by(), None) def test_unbooked_duck(self): - self.assertEqual(self.unbooked_duck.booked_by(), None) + """ + Test if unbooked duck returns None from booked_by() + """ + + unbooked_duck = Duck.objects.create(species=self.spec, + location=self.loc, + donated_by=self.user) + self.assertEqual(unbooked_duck.booked_by(), None) def test_multiple_booking(self): + """ + Test error presence in case of multiple bookings for the same + duck + """ + Booking.objects.create(duck=self.booked_duck, user=self.user, comp_req=self.comp) diff --git a/booking/urls.py b/booking/urls.py index 93528cc..c88ef9f 100644 --- a/booking/urls.py +++ b/booking/urls.py @@ -1,24 +1,28 @@ -from django.conf.urls import patterns, url +# -*- coding: utf-8 +""" +URL definitions for the Duck Booking Tool frontend +""" + +from django.conf.urls import url from django.views.generic.base import TemplateView from .views import DuckListView -urlpatterns = patterns( - '', - url(r'^$', DuckListView.as_view(), name = 'list'), +urlpatterns = [ + url(r'^$', DuckListView.as_view(), name='list'), url( r'^vocabulary.html$', - TemplateView.as_view(template_name = 'booking/vocabulary.html'), - name = 'vocabulary' + TemplateView.as_view(template_name='booking/vocabulary.html'), + name='vocabulary' ), url( r'^terms.html$', - TemplateView.as_view(template_name = 'booking/terms.html'), - name = 'terms' + TemplateView.as_view(template_name='booking/terms.html'), + name='terms' ), url( r'^disclaimer.html$', - TemplateView.as_view(template_name = 'booking/disclaimer.html'), - name = 'disclaimer' + TemplateView.as_view(template_name='booking/disclaimer.html'), + name='disclaimer' ), -) +] diff --git a/booking/views.py b/booking/views.py index ddb77c9..a85211e 100644 --- a/booking/views.py +++ b/booking/views.py @@ -1,9 +1,17 @@ -from django.shortcuts import render +# -*- coding: utf-8 +""" +Views for the Duck Booking Tool frontend +""" + from django.views import generic from .models import Duck class DuckListView(generic.ListView): + """ + View for duck listing (the main page) + """ + template_name = 'booking/duck_list.html' context_object_name = 'duck_list' diff --git a/duckbook/urls.py b/duckbook/urls.py index c22cfb5..bd223d6 100644 --- a/duckbook/urls.py +++ b/duckbook/urls.py @@ -1,26 +1,30 @@ -from django.conf import settings -from django.conf.urls import patterns, include, url -from django.contrib import admin -from django.views.decorators.cache import cache_page +# -*- coding: utf-8 +""" +Main URL definitions file +""" -from django_js_reverse.views import urls_js +from django.conf import settings +from django.conf.urls import include, url +from django.contrib import admin admin.autodiscover() -urlpatterns = patterns( - '', +urlpatterns = [ url( r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT} ), url( - r'^reverse.js$', - cache_page(3600)(urls_js), - name = 'js_reverse' - ), - url(r'^admin/', include(admin.site.urls)), - url(r'^accounts/', include('accounts.urls', namespace = 'accounts')), - url(r'^api/v1/', include('api.urls', namespace = 'api')), - url('', include('booking.urls', namespace = 'booking')), -) + r'^admin/', + include(admin.site.urls)), + url( + r'^accounts/', + include('accounts.urls', namespace='accounts')), + url( + r'^api/v1/', + include('api.urls', namespace='api')), + url( + '', + include('booking.urls', namespace='booking')), +]