Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds organization models, serializer and views #64

Merged
merged 1 commit into from
Feb 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions cadasta/accounts/migrations/0003_auto_20160204_1838.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-02-04 18:38
from __future__ import unicode_literals

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0002_user_verify_email_by'),
]

operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username'),
),
]
37 changes: 27 additions & 10 deletions cadasta/accounts/tests/test_urls.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@
from django.test import TestCase
from django.core.urlresolvers import reverse, resolve

from core.tests.url_utils import version_ns, version_url
from .. import views


class UserUrlsTest(TestCase):
def test_account_user(self):
self.assertEqual(reverse('accounts:user'), '/account/')
self.assertEqual(
reverse(version_ns('accounts:user')),
version_url('/account/')
)

resolved = resolve('/account/')
resolved = resolve(version_url('/account/'))
self.assertEqual(resolved.func.__name__, views.AccountUser.__name__)

def test_account_register(self):
self.assertEqual(reverse('accounts:register'), '/account/register/')
self.assertEqual(
reverse(version_ns('accounts:register')),
version_url('/account/register/')
)

resolved = resolve('/account/register/')
resolved = resolve(version_url('/account/register/'))
self.assertEqual(resolved.func.__name__, views.AccountRegister.__name__)

def test_account_login(self):
self.assertEqual(reverse('accounts:login'), '/account/login/')
self.assertEqual(
reverse(version_ns('accounts:login')),
version_url('/account/login/')
)

resolved = resolve('/account/login/')
resolved = resolve(version_url('/account/login/'))
self.assertEqual(resolved.func.__name__, views.AccountLogin.__name__)

def test_account_activate(self):
self.assertEqual(reverse('accounts:activate'), '/account/activate/')
self.assertEqual(
reverse(version_ns('accounts:activate')),
version_url('/account/activate/')
)

resolved = resolve('/account/activate/')
resolved = resolve(version_url('/account/activate/'))
self.assertEqual(resolved.func.__name__, views.AccountVerify.__name__)

def test_password_reset(self):
self.assertEqual(reverse('accounts:password_reset'), '/account/password/reset/')
self.assertEqual(
reverse(version_ns('accounts:password_reset')),
version_url('/account/password/reset/')
)

resolved = resolve('/account/password/reset/')
resolved = resolve(version_url('/account/password/reset/'))
self.assertEqual(resolved.func.__name__, views.PasswordReset.__name__)
24 changes: 10 additions & 14 deletions cadasta/accounts/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_keep_email_address(self):
'username': 'imagine71'
}

request = APIRequestFactory().put('/account/', data)
request = APIRequestFactory().put('/v1/account/', data)
force_authenticate(request, user=user)
response = AccountUser.as_view()(request).render()
self.assertEqual(response.status_code, 200)
Expand All @@ -66,7 +66,7 @@ def test_update_with_existing_email(self):
'username': user.username
}

request = APIRequestFactory().put('/account/', data)
request = APIRequestFactory().put('/v1/account/', data)
force_authenticate(request, user=user)
response = AccountUser.as_view()(request).render()
self.assertEqual(response.status_code, 400)
Expand All @@ -85,7 +85,7 @@ def test_update_username(self):
'username': 'john'
}

request = APIRequestFactory().put('/account/', data)
request = APIRequestFactory().put('/v1/account/', data)
force_authenticate(request, user=user)
response = AccountUser.as_view()(request).render()
self.assertEqual(response.status_code, 200)
Expand All @@ -107,7 +107,7 @@ def test_update_with_existing_username(self):
'username': 'boss'
}

request = APIRequestFactory().put('/account/', data)
request = APIRequestFactory().put('/v1/account/', data)
force_authenticate(request, user=user)
response = AccountUser.as_view()(request).render()
self.assertEqual(response.status_code, 400)
Expand All @@ -126,14 +126,10 @@ def test_user_signs_up(self):
'last_name': 'Lennon',
}

request = APIRequestFactory().post('/account/register/', data)
request = APIRequestFactory().post('/v1/account/register/', data)
response = AccountRegister.as_view()(request).render()
self.assertEqual(response.status_code, 201)
# self.assertIn('auth_token', response.content.decode("utf-8"))

self.assertEqual(User.objects.count(), 1)
# user = User.objects.first()
# self.assertTrue(user.is_authenticated())

def test_user_signs_up_with_invalid(self):
"""The server should respond with an 404 error code when a user tries
Expand All @@ -145,7 +141,7 @@ def test_user_signs_up_with_invalid(self):
'last_name': 'Lennon',
}

request = APIRequestFactory().post('/account/register/', data)
request = APIRequestFactory().post('/v1/account/register/', data)
response = AccountRegister.as_view()(request).render()
self.assertEqual(response.status_code, 400)
self.assertEqual(User.objects.count(), 0)
Expand All @@ -161,7 +157,7 @@ def setUp(self):

def test_successful_login(self):
"""The view should return a token to authenticate API calls"""
request = APIRequestFactory().post('/account/login/', {
request = APIRequestFactory().post('/v1/account/login/', {
'username': 'imagine71',
'password': 'iloveyoko79'
})
Expand All @@ -171,7 +167,7 @@ def test_successful_login(self):

def test_unsuccessful_login(self):
"""The view should return a token to authenticate API calls"""
request = APIRequestFactory().post('/account/login/', {
request = APIRequestFactory().post('/v1/account/login/', {
'username': 'imagine71',
'password': 'iloveyoko78'
})
Expand All @@ -185,7 +181,7 @@ def test_login_with_unverified_email(self):
self.user.verify_email_by = datetime.now()
self.user.save()

request = APIRequestFactory().post('/account/login/', {
request = APIRequestFactory().post('/v1/account/login/', {
'username': 'imagine71',
'password': 'iloveyoko79'
})
Expand All @@ -204,7 +200,7 @@ def test_activate_account(self):
user.last_login = datetime.now()
user.save()

request = APIRequestFactory().post('/account/activate/', {
request = APIRequestFactory().post('/v1/account/activate/', {
'uid': encode_uid(user.pk),
'token': token
})
Expand Down
7 changes: 3 additions & 4 deletions cadasta/accounts/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from django.conf.urls import patterns, url
from django.conf.urls import url

from . import views

urlpatterns = patterns(
'',
urlpatterns = [
url(r'^$', views.AccountUser.as_view(), name='user'),
url(r'^register/$', views.AccountRegister.as_view(), name='register'),
url(r'^login/$', views.AccountLogin.as_view(), name='login'),
url(r'^activate/$', views.AccountVerify.as_view(), name='activate'),
url(r'^password/reset/$', views.PasswordReset.as_view(), name='password_reset'),
)
]
3 changes: 0 additions & 3 deletions cadasta/accounts/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from django.contrib.auth import user_logged_in
from django.utils.translation import ugettext as _

from rest_framework.serializers import ValidationError
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import AllowAny
from rest_framework import status
from rest_framework.authtoken.models import Token

from djoser import views as djoser_views
from djoser import utils as djoser_utils
Expand All @@ -24,7 +22,6 @@ class AccountUser(djoser_utils.SendEmailViewMixin, djoser_views.UserView):
plain_body_template_name = 'change_email.txt'
serializer_class = serializers.UserSerializer


def get_email_context(self, user):
context = super(AccountUser, self).get_email_context(user)
context['url'] = settings.get('ACTIVATION_URL').format(**context)
Expand Down
13 changes: 13 additions & 0 deletions cadasta/config/permissions/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"clause": [
{
"effect": "allow",
"object": ["*"],
"action": ["org.list"]
}, {
"effect": "allow",
"object": ["organization/*"],
"action": ["org.view"]
}
]
}
13 changes: 13 additions & 0 deletions cadasta/config/permissions/org-admin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"clause": [
{
"effect": "allow",
"object": ["*"],
"action": ["org.*"]
}, {
"effect": "allow",
"object": ["organization/*"],
"action": ["org.*"]
}
]
}
9 changes: 9 additions & 0 deletions cadasta/config/permissions/superuser.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"clause": [
{
"effect": "allow",
"object": ["organization/*"],
"action": ["org.*"]
}
]
}
10 changes: 10 additions & 0 deletions cadasta/config/settings/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@
'django.contrib.staticfiles',
'corsheaders',

'crispy_forms',
'rest_framework',
'rest_framework.authtoken',
'djoser',
'tutelary',

'core',
'accounts',
'organization'
)

MIDDLEWARE_CLASSES = (
Expand All @@ -53,12 +57,16 @@
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'audit_log.middleware.UserLoggingMiddleware',
)

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
'DEFAULT_VERSION': 'v1',
'EXCEPTION_HANDLER': 'core.views.exception_handler'
}

ROOT_URLCONF = 'config.urls'
Expand All @@ -79,6 +87,8 @@
},
]

AUTHENTICATION_BACKENDS = ['core.backends.Auth']

DJOSER = {
'SITE_NAME': 'Cadasta',
'SET_PASSWORD_RETYPE': True,
Expand Down
8 changes: 7 additions & 1 deletion cadasta/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
"""
from django.conf.urls import include, url

urlpatterns = [
urls = [
url(r'^account/', include('accounts.urls', namespace='accounts')),
url(r'^account/', include('djoser.urls.authtoken')),

url(r'^organizations/', include('organization.urls', namespace='organization')),
]

urlpatterns = [
url(r'^v1/', include(urls, namespace='v1'))
]
6 changes: 6 additions & 0 deletions cadasta/core/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from tutelary.backends import Backend as TutelaryBackend
from django.contrib.auth.backends import ModelBackend


class Auth(TutelaryBackend, ModelBackend):
pass
9 changes: 9 additions & 0 deletions cadasta/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import json


class JsonValidationError(BaseException):
def __init__(self, errors):
self.errors = errors

def __str__(self):
return json.dumps(self.errors)
12 changes: 12 additions & 0 deletions cadasta/core/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.db.models.query import QuerySet


class DetailSerializer:
def __init__(self, *args, **kwargs):
detail = kwargs.pop('detail', False)
super(DetailSerializer, self).__init__(*args, **kwargs)

is_list = type(self.instance) in [list, QuerySet]
if is_list and not detail:
for field_name in self.Meta.detail_only_fields:
self.fields.pop(field_name)
12 changes: 12 additions & 0 deletions cadasta/core/tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.test import TestCase

from ..exceptions import JsonValidationError


class JsonValidationErrorTest(TestCase):
def test_to_string(self):
exc = JsonValidationError({
'field': "Some error"
})

assert str(exc) == '{"field": "Some error"}'
11 changes: 8 additions & 3 deletions cadasta/core/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from .testcases import AbstractModelTestCase
from django.test import TestCase
from ..models import RandomIDModel


class RandomIDModelTest(AbstractModelTestCase):
class MyTestModel(RandomIDModel):
class Meta:
app_label = 'core'


class RandomIDModelTest(TestCase):
abstract_model = RandomIDModel

def test_save(self):
instance = self.model()
instance = MyTestModel()
instance.save()
self.assertIsNotNone(instance.id)
Loading