INITIAL
This commit is contained in:
		
						commit
						7ba937ef66
					
				
					 66 changed files with 1823 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								ccdb/users/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ccdb/users/__init__.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
							
								
								
									
										37
									
								
								ccdb/users/admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ccdb/users/admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
from __future__ import absolute_import, unicode_literals
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
 | 
			
		||||
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
 | 
			
		||||
 | 
			
		||||
from .models import User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyUserChangeForm(UserChangeForm):
 | 
			
		||||
    class Meta(UserChangeForm.Meta):
 | 
			
		||||
        model = User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyUserCreationForm(UserCreationForm):
 | 
			
		||||
    error_message = UserCreationForm.error_messages.update({
 | 
			
		||||
        'duplicate_username': 'This username has already been taken.'
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    class Meta(UserCreationForm.Meta):
 | 
			
		||||
        model = User
 | 
			
		||||
 | 
			
		||||
    def clean_username(self):
 | 
			
		||||
        username = self.cleaned_data["username"]
 | 
			
		||||
        try:
 | 
			
		||||
            User.objects.get(username=username)
 | 
			
		||||
        except User.DoesNotExist:
 | 
			
		||||
            return username
 | 
			
		||||
        raise forms.ValidationError(self.error_messages['duplicate_username'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(User)
 | 
			
		||||
class UserAdmin(AuthUserAdmin):
 | 
			
		||||
    form = MyUserChangeForm
 | 
			
		||||
    add_form = MyUserCreationForm
 | 
			
		||||
							
								
								
									
										13
									
								
								ccdb/users/middleware.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								ccdb/users/middleware.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import pytz
 | 
			
		||||
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TimezoneMiddleware(object):
 | 
			
		||||
    def process_request(self, request):
 | 
			
		||||
        if not request.user.is_anonymous():
 | 
			
		||||
            tzname = request.user.timezone
 | 
			
		||||
            if tzname:
 | 
			
		||||
                timezone.activate(pytz.timezone(tzname))
 | 
			
		||||
            else:
 | 
			
		||||
                timezone.deactivate()
 | 
			
		||||
							
								
								
									
										44
									
								
								ccdb/users/migrations/0001_initial.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								ccdb/users/migrations/0001_initial.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.db import models, migrations
 | 
			
		||||
import django.utils.timezone
 | 
			
		||||
import django.contrib.auth.models
 | 
			
		||||
import django.core.validators
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('auth', '0006_require_contenttypes_0002'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='User',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
 | 
			
		||||
                ('password', models.CharField(max_length=128, verbose_name='password')),
 | 
			
		||||
                ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
 | 
			
		||||
                ('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status', default=False)),
 | 
			
		||||
                ('username', models.CharField(max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], verbose_name='username', error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True)),
 | 
			
		||||
                ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
 | 
			
		||||
                ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
 | 
			
		||||
                ('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)),
 | 
			
		||||
                ('is_staff', models.BooleanField(help_text='Designates whether the user can log into this admin site.', verbose_name='staff status', default=False)),
 | 
			
		||||
                ('is_active', models.BooleanField(help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active', default=True)),
 | 
			
		||||
                ('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)),
 | 
			
		||||
                ('groups', models.ManyToManyField(related_name='user_set', blank=True, verbose_name='groups', to='auth.Group', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_query_name='user')),
 | 
			
		||||
                ('user_permissions', models.ManyToManyField(related_name='user_set', blank=True, verbose_name='user permissions', to='auth.Permission', help_text='Specific permissions for this user.', related_query_name='user')),
 | 
			
		||||
                ('name', models.CharField(max_length=255, verbose_name='Name of User', blank=True)),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                'verbose_name': 'user',
 | 
			
		||||
                'abstract': False,
 | 
			
		||||
                'verbose_name_plural': 'users',
 | 
			
		||||
            },
 | 
			
		||||
            managers=[
 | 
			
		||||
                (b'objects', django.contrib.auth.models.UserManager()),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										26
									
								
								ccdb/users/migrations/0002_timezone.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								ccdb/users/migrations/0002_timezone.py
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										0
									
								
								ccdb/users/migrations/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								ccdb/users/migrations/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										21
									
								
								ccdb/users/models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								ccdb/users/models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
from django.contrib.auth.models import AbstractUser
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
import pytz
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class User(AbstractUser):
 | 
			
		||||
    name = models.CharField(_("Name of User"), blank=True, max_length=255)
 | 
			
		||||
    timezone = models.CharField(_("Current Timezone"), max_length=255,
 | 
			
		||||
        default="UTC", choices=[(x, x) for x in pytz.common_timezones],
 | 
			
		||||
        blank=False)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.username
 | 
			
		||||
 | 
			
		||||
    def get_absolute_url(self):
 | 
			
		||||
        return reverse('users:detail', kwargs={'username': self.username})
 | 
			
		||||
							
								
								
									
										0
									
								
								ccdb/users/tests/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								ccdb/users/tests/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										11
									
								
								ccdb/users/tests/factories.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ccdb/users/tests/factories.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
import factory
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserFactory(factory.django.DjangoModelFactory):
 | 
			
		||||
    username = factory.Sequence(lambda n: 'user-{0}'.format(n))
 | 
			
		||||
    email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n))
 | 
			
		||||
    password = factory.PostGenerationMethodCall('set_password', 'password')
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = 'users.User'
 | 
			
		||||
        django_get_or_create = ('username', )
 | 
			
		||||
							
								
								
									
										39
									
								
								ccdb/users/tests/test_admin.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								ccdb/users/tests/test_admin.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
from test_plus.test import TestCase
 | 
			
		||||
 | 
			
		||||
from ..admin import MyUserCreationForm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestMyUserCreationForm(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.user = self.make_user()
 | 
			
		||||
 | 
			
		||||
    def test_clean_username_success(self):
 | 
			
		||||
        # Instantiate the form with a new username
 | 
			
		||||
        form = MyUserCreationForm({
 | 
			
		||||
            'username': 'alamode',
 | 
			
		||||
            'password1': '123456',
 | 
			
		||||
            'password2': '123456',
 | 
			
		||||
        })
 | 
			
		||||
        # Run is_valid() to trigger the validation
 | 
			
		||||
        valid = form.is_valid()
 | 
			
		||||
        self.assertTrue(valid)
 | 
			
		||||
 | 
			
		||||
        # Run the actual clean_username method
 | 
			
		||||
        username = form.clean_username()
 | 
			
		||||
        self.assertEqual('alamode', username)
 | 
			
		||||
 | 
			
		||||
    def test_clean_username_false(self):
 | 
			
		||||
        # Instantiate the form with the same username as self.user
 | 
			
		||||
        form = MyUserCreationForm({
 | 
			
		||||
            'username': self.user.username,
 | 
			
		||||
            'password1': '123456',
 | 
			
		||||
            'password2': '123456',
 | 
			
		||||
        })
 | 
			
		||||
        # Run is_valid() to trigger the validation, which is going to fail
 | 
			
		||||
        # because the username is already taken
 | 
			
		||||
        valid = form.is_valid()
 | 
			
		||||
        self.assertFalse(valid)
 | 
			
		||||
 | 
			
		||||
        # The form.errors dict should contain a single error called 'username'
 | 
			
		||||
        self.assertTrue(len(form.errors) == 1)
 | 
			
		||||
        self.assertTrue('username' in form.errors)
 | 
			
		||||
							
								
								
									
										18
									
								
								ccdb/users/tests/test_models.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								ccdb/users/tests/test_models.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
from test_plus.test import TestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestUser(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.user = self.make_user()
 | 
			
		||||
 | 
			
		||||
    def test__str__(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.user.__str__(),
 | 
			
		||||
            "testuser"  # This is the default username for self.make_user()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_get_absolute_url(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.user.get_absolute_url(),
 | 
			
		||||
            '/users/testuser/'
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										66
									
								
								ccdb/users/tests/test_views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								ccdb/users/tests/test_views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
from django.test import RequestFactory
 | 
			
		||||
 | 
			
		||||
from test_plus.test import TestCase
 | 
			
		||||
 | 
			
		||||
from ..views import (
 | 
			
		||||
    UserRedirectView,
 | 
			
		||||
    UserUpdateView
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseUserTestCase(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.user = self.make_user()
 | 
			
		||||
        self.factory = RequestFactory()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestUserRedirectView(BaseUserTestCase):
 | 
			
		||||
    def test_get_redirect_url(self):
 | 
			
		||||
        # Instantiate the view directly. Never do this outside a test!
 | 
			
		||||
        view = UserRedirectView()
 | 
			
		||||
        # Generate a fake request
 | 
			
		||||
        request = self.factory.get('/fake-url')
 | 
			
		||||
        # Attach the user to the request
 | 
			
		||||
        request.user = self.user
 | 
			
		||||
        # Attach the request to the view
 | 
			
		||||
        view.request = request
 | 
			
		||||
        # Expect: '/users/testuser/', as that is the default username for
 | 
			
		||||
        #   self.make_user()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            view.get_redirect_url(),
 | 
			
		||||
            '/users/testuser/'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestUserUpdateView(BaseUserTestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        # call BaseUserTestCase.setUp()
 | 
			
		||||
        super(TestUserUpdateView, self).setUp()
 | 
			
		||||
        # Instantiate the view directly. Never do this outside a test!
 | 
			
		||||
        self.view = UserUpdateView()
 | 
			
		||||
        # Generate a fake request
 | 
			
		||||
        request = self.factory.get('/fake-url')
 | 
			
		||||
        # Attach the user to the request
 | 
			
		||||
        request.user = self.user
 | 
			
		||||
        # Attach the request to the view
 | 
			
		||||
        self.view.request = request
 | 
			
		||||
 | 
			
		||||
    def test_get_success_url(self):
 | 
			
		||||
        # Expect: '/users/testuser/', as that is the default username for
 | 
			
		||||
        #   self.make_user()
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.view.get_success_url(),
 | 
			
		||||
            '/users/testuser/'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # TODO: write this test
 | 
			
		||||
    # def test_get_redirected(self):
 | 
			
		||||
        # Expect '/users/testuser01/' to redirect, because that isn't the
 | 
			
		||||
        #   currently logged in user
 | 
			
		||||
 | 
			
		||||
    def test_get_object(self):
 | 
			
		||||
        # Expect: self.user, as that is the request's user object
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            self.view.get_object(),
 | 
			
		||||
            self.user
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										14
									
								
								ccdb/users/urls.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								ccdb/users/urls.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
from __future__ import absolute_import, unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.conf.urls import url
 | 
			
		||||
 | 
			
		||||
from . import views
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    url(regex=r'^$', view=views.UserListView.as_view(), name='list'),
 | 
			
		||||
    url(regex=r'^redirect/$', view=views.UserRedirectView.as_view(), name='redirect'),
 | 
			
		||||
    url(regex=r'^(?P<username>[\w.@+-]+)/$', view=views.UserDetailView.as_view(), name='detail'),
 | 
			
		||||
    url(regex=r'^(?P<username>[\w.@+-]+)/update/$', view=views.UserUpdateView.as_view(), name='update'),
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										51
									
								
								ccdb/users/views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								ccdb/users/views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
from __future__ import absolute_import, unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.views.generic import DetailView, ListView, RedirectView, UpdateView
 | 
			
		||||
from django.shortcuts import redirect
 | 
			
		||||
 | 
			
		||||
from braces.views import LoginRequiredMixin
 | 
			
		||||
 | 
			
		||||
from .models import User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserDetailView(LoginRequiredMixin, DetailView):
 | 
			
		||||
    model = User
 | 
			
		||||
    # These next two lines tell the view to index lookups by username
 | 
			
		||||
    slug_field = "username"
 | 
			
		||||
    slug_url_kwarg = "username"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
 | 
			
		||||
    permanent = False
 | 
			
		||||
 | 
			
		||||
    def get_redirect_url(self):
 | 
			
		||||
        return reverse("users:detail",
 | 
			
		||||
                       kwargs={"username": self.request.user.username})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
 | 
			
		||||
    fields = ['name', 'timezone']
 | 
			
		||||
    model = User
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, request, *args, **kwargs):
 | 
			
		||||
        if request.user.username != kwargs.pop("username", None):
 | 
			
		||||
            return redirect(reverse("users:detail",
 | 
			
		||||
                                    kwargs={"username": request.user.username}))
 | 
			
		||||
        return super(UserUpdateView, self).dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_success_url(self):
 | 
			
		||||
        return reverse("users:detail",
 | 
			
		||||
                       kwargs={"username": self.request.user.username})
 | 
			
		||||
 | 
			
		||||
    def get_object(self):
 | 
			
		||||
        # Only get the User record for the user making the request
 | 
			
		||||
        return User.objects.get(username=self.request.user.username)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class UserListView(LoginRequiredMixin, ListView):
 | 
			
		||||
    model = User
 | 
			
		||||
    # These next two lines tell the view to index lookups by username
 | 
			
		||||
    slug_field = "username"
 | 
			
		||||
    slug_url_kwarg = "username"
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue