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