Refactor: greenmine.base
parent
cda7e7d756
commit
d8516a20c7
|
@ -1,161 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.contrib.auth import logout, login, authenticate
|
|
||||||
from django.contrib.auth.views import login as auth_login, logout as auth_logout
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db.models import Q
|
|
||||||
from django import http
|
|
||||||
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
from rest_framework import views
|
||||||
from rest_framework import status, generics, viewsets, views
|
|
||||||
|
|
||||||
from haystack import query, inputs
|
|
||||||
from djmail.template_mail import MagicMailBuilder
|
|
||||||
|
|
||||||
from greenmine.base.serializers import (LoginSerializer, UserLogged,
|
|
||||||
UserSerializer, RoleSerializer,
|
|
||||||
SearchSerializer)
|
|
||||||
|
|
||||||
from greenmine.base.models import User, Role
|
|
||||||
from greenmine.base import exceptions as excp
|
|
||||||
from greenmine.scrum import models
|
|
||||||
|
|
||||||
|
|
||||||
class RolesViewSet(viewsets.ViewSet):
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
serializer_class = RoleSerializer
|
|
||||||
|
|
||||||
def list(self, request, pk=None):
|
|
||||||
queryset = Role.objects.all()
|
|
||||||
serializer = self.serializer_class(queryset, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
def retrieve(self, request, pk=None):
|
|
||||||
try:
|
|
||||||
role = Role.objects.get(pk=pk)
|
|
||||||
except Role.DoesNotExist:
|
|
||||||
raise excp.NotFound()
|
|
||||||
|
|
||||||
serializer = self.serializer_class(role)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
|
|
||||||
class UsersViewSet(viewsets.ViewSet):
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
|
|
||||||
def get_list_queryset(self):
|
|
||||||
own_projects = (models.Project.objects
|
|
||||||
.filter(members=self.request.user))
|
|
||||||
|
|
||||||
project = self.request.QUERY_PARAMS.get('project', None)
|
|
||||||
if project is not None:
|
|
||||||
own_projects = own_projects.filter(pk=project)
|
|
||||||
|
|
||||||
queryset = (User.objects.filter(projects__in=own_projects)
|
|
||||||
.order_by('username').distinct())
|
|
||||||
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def list(self, request, pk=None):
|
|
||||||
queryset = self.get_list_queryset()
|
|
||||||
serializer = UserSerializer(queryset, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
def retrieve(self, request, pk=None):
|
|
||||||
return Response({})
|
|
||||||
|
|
||||||
@action(methods=["POST"], permission_classes=[])
|
|
||||||
def password_recovery(self, request, pk=None):
|
|
||||||
username_or_email = request.DATA.get('username', None)
|
|
||||||
|
|
||||||
if not username_or_email:
|
|
||||||
return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
try:
|
|
||||||
queryset = User.objects.all()
|
|
||||||
user = queryset.get(Q(username=username_or_email) |
|
|
||||||
Q(email=username_or_email))
|
|
||||||
except User.DoesNotExist:
|
|
||||||
return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
user.token = str(uuid.uuid1())
|
|
||||||
user.save(update_fields=["token"])
|
|
||||||
|
|
||||||
mbuilder = MagicMailBuilder()
|
|
||||||
email = mbuilder.password_recovery(user.email, {"user": user})
|
|
||||||
|
|
||||||
return Response({"detail": "Mail sended successful!"})
|
|
||||||
|
|
||||||
|
|
||||||
class Login(viewsets.ViewSet):
|
|
||||||
permission_classes = (AllowAny,)
|
|
||||||
|
|
||||||
def create(self, request, **kwargs):
|
|
||||||
username = request.DATA.get('username', None)
|
|
||||||
password = request.DATA.get('password', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(username=username)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
return Response({"detail": "Invalid username or password"},
|
|
||||||
status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
if not user.check_password(password):
|
|
||||||
return Response({"detail": "Invalid username or password"},
|
|
||||||
status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
user = authenticate(username=username, password=password)
|
|
||||||
login(request, user)
|
|
||||||
|
|
||||||
serializer = UserSerializer(user)
|
|
||||||
response_data = serializer.data
|
|
||||||
response_data["token"] = request.session.session_key
|
|
||||||
|
|
||||||
return Response(response_data)
|
|
||||||
|
|
||||||
|
|
||||||
class Logout(viewsets.ViewSet):
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
|
|
||||||
def list(self, request, **kwargs):
|
|
||||||
return self.logout(request)
|
|
||||||
|
|
||||||
def create(self, request, **kwargs):
|
|
||||||
return self.logout(request)
|
|
||||||
|
|
||||||
def logout(self, request):
|
|
||||||
logout(request)
|
|
||||||
return Response({})
|
|
||||||
|
|
||||||
|
|
||||||
class Search(viewsets.ViewSet):
|
|
||||||
def list(self, request, **kwargs):
|
|
||||||
text = request.QUERY_PARAMS.get('text', "")
|
|
||||||
project_id = request.QUERY_PARAMS.get('project', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
project = self._get_project(project_id)
|
|
||||||
except (models.Project.DoesNotExist, TypeError):
|
|
||||||
raise excp.PermissionDenied({"detail": "Wrong project id"})
|
|
||||||
|
|
||||||
#if not text:
|
|
||||||
# raise excp.BadRequest("text parameter must be contains text")
|
|
||||||
|
|
||||||
queryset = query.SearchQuerySet()
|
|
||||||
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
|
||||||
queryset = queryset.filter(project_id=project_id)
|
|
||||||
|
|
||||||
return_data = SearchSerializer(queryset)
|
|
||||||
return Response(return_data.data)
|
|
||||||
|
|
||||||
def _get_project(self, project_id):
|
|
||||||
own_projects = (models.Project.objects
|
|
||||||
.filter(members=self.request.user))
|
|
||||||
|
|
||||||
return own_projects.get(pk=project_id)
|
|
||||||
|
|
||||||
|
|
||||||
class ApiRoot(views.APIView):
|
class ApiRoot(views.APIView):
|
||||||
|
|
|
@ -1,17 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db import models
|
|
||||||
from django.db.models.signals import post_save, m2m_changed
|
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from django.contrib.auth.models import UserManager, AbstractUser, Group
|
|
||||||
|
|
||||||
from greenmine.scrum.models import Project, UserStory, Task
|
|
||||||
from greenmine.base.notifications.models import WatcherMixin
|
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -30,45 +21,6 @@ def attach_uuid(sender, instance, **kwargs):
|
||||||
instance.uuid = unicode(uuid.uuid1())
|
instance.uuid = unicode(uuid.uuid1())
|
||||||
|
|
||||||
|
|
||||||
class User(WatcherMixin, AbstractUser):
|
|
||||||
color = models.CharField(max_length=9, null=False, blank=False, default="#669933",
|
|
||||||
verbose_name=_('color'))
|
|
||||||
description = models.TextField(null=False, blank=True,
|
|
||||||
verbose_name=_('description'))
|
|
||||||
photo = models.FileField(upload_to='files/msg', max_length=500, null=True, blank=True,
|
|
||||||
verbose_name=_('photo'))
|
|
||||||
default_language = models.CharField(max_length=20, null=False, blank=True, default='',
|
|
||||||
verbose_name=_('default language'))
|
|
||||||
default_timezone = models.CharField(max_length=20, null=False, blank=True, default='',
|
|
||||||
verbose_name=_('default timezone'))
|
|
||||||
token = models.CharField(max_length=200, null=False, blank=True, default='',
|
|
||||||
verbose_name=_('token'))
|
|
||||||
colorize_tags = models.BooleanField(null=False, blank=True, default=False,
|
|
||||||
verbose_name=_('colorize tags'))
|
|
||||||
objects = UserManager()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ["username"]
|
|
||||||
|
|
||||||
|
|
||||||
class Role(models.Model):
|
|
||||||
name = models.CharField(max_length=200, null=False, blank=False,
|
|
||||||
verbose_name=_('name'))
|
|
||||||
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
|
|
||||||
verbose_name=_('slug'))
|
|
||||||
permissions = models.ManyToManyField('auth.Permission',
|
|
||||||
related_name='roles',
|
|
||||||
verbose_name=_('permissions'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = u'role'
|
|
||||||
verbose_name_plural = u'roles'
|
|
||||||
ordering = ['slug']
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
# Patch api view for correctly return 401 responses on
|
# Patch api view for correctly return 401 responses on
|
||||||
# request is authenticated instead of 403
|
# request is authenticated instead of 403
|
||||||
from .monkey import patch_api_view; patch_api_view()
|
from .monkey import patch_api_view; patch_api_view()
|
||||||
|
|
|
@ -18,13 +18,4 @@ class DefaultRouter(routers.DefaultRouter):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SimpleRouter(routers.SimpleRouter):
|
__all__ = ["DefaultRouter"]
|
||||||
routes = [
|
|
||||||
routers.SimpleRouter.routes[0],
|
|
||||||
actions_router,
|
|
||||||
routers.SimpleRouter.routes[2],
|
|
||||||
routers.SimpleRouter.routes[1]
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["DefaultRouter", "SimpleRouter"]
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.db.models.loading import get_model
|
||||||
|
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from haystack import query, inputs
|
||||||
|
|
||||||
|
from greenmine.base import exceptions as excp
|
||||||
|
|
||||||
|
from .serializers import SearchSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class SearchViewSet(viewsets.ViewSet):
|
||||||
|
def list(self, request, **kwargs):
|
||||||
|
project_model = get_model("scrum", "Project")
|
||||||
|
text = request.QUERY_PARAMS.get('text', "")
|
||||||
|
project_id = request.QUERY_PARAMS.get('project', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
project = self._get_project(project_id)
|
||||||
|
except (project_model.DoesNotExist, TypeError):
|
||||||
|
raise excp.PermissionDenied({"detail": "Wrong project id"})
|
||||||
|
|
||||||
|
#if not text:
|
||||||
|
# raise excp.BadRequest("text parameter must be contains text")
|
||||||
|
|
||||||
|
queryset = query.SearchQuerySet()
|
||||||
|
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
||||||
|
queryset = queryset.filter(project_id=project_id)
|
||||||
|
|
||||||
|
return_data = SearchSerializer(queryset)
|
||||||
|
return Response(return_data.data)
|
||||||
|
|
||||||
|
def _get_project(self, project_id):
|
||||||
|
project_model = get_model("scrum", "Project")
|
||||||
|
own_projects = (project_model.objects
|
||||||
|
.filter(members=self.request.user))
|
||||||
|
|
||||||
|
return own_projects.get(pk=project_id)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class SearchSerializer(serializers.Serializer):
|
||||||
|
id = serializers.CharField(max_length=255)
|
||||||
|
model_name = serializers.CharField(max_length=255)
|
||||||
|
pk = serializers.IntegerField()
|
||||||
|
score = serializers.FloatField()
|
||||||
|
stored_fields = serializers.SerializerMethodField('get_stored_fields')
|
||||||
|
|
||||||
|
def get_stored_fields(self, obj):
|
||||||
|
return obj.get_stored_fields()
|
||||||
|
|
||||||
|
def restore_object(self, attrs, instance=None):
|
||||||
|
"""
|
||||||
|
Given a dictionary of deserialized field values, either update
|
||||||
|
an existing model instance, or create a new model instance.
|
||||||
|
"""
|
||||||
|
if instance is not None:
|
||||||
|
return instance
|
||||||
|
return attrs
|
|
@ -1,121 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.core import mail
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from greenqueue import send_task
|
|
||||||
|
|
||||||
|
|
||||||
class LowLevelEmailTests(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
mail.outbox = []
|
|
||||||
|
|
||||||
def test_send_one_mail(self):
|
|
||||||
send_task("send-mail", args=["subject", "template", ["hola@niwi.be"]])
|
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
|
||||||
|
|
||||||
def test_send_bulk_mail(self):
|
|
||||||
send_task("send-bulk-mail", args=[[
|
|
||||||
('s1', 't1', ['hola@niwi.be']),
|
|
||||||
('s2', 't2', ['hola@niwi.be']),
|
|
||||||
]])
|
|
||||||
|
|
||||||
self.assertEqual(len(mail.outbox), 2)
|
|
||||||
|
|
||||||
|
|
||||||
class UserMailTests(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.user1 = User.objects.create(
|
|
||||||
username='test1',
|
|
||||||
email='test1@test.com',
|
|
||||||
is_active=True,
|
|
||||||
is_staff=True,
|
|
||||||
is_superuser=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.user2 = User.objects.create(
|
|
||||||
username='test2',
|
|
||||||
email='test2@test.com',
|
|
||||||
is_active=True,
|
|
||||||
is_staff=False,
|
|
||||||
is_superuser=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.user1.set_password("test")
|
|
||||||
self.user2.set_password("test")
|
|
||||||
|
|
||||||
self.user1.save()
|
|
||||||
self.user2.save()
|
|
||||||
|
|
||||||
mail.outbox = []
|
|
||||||
|
|
||||||
def test_remember_password(self):
|
|
||||||
url = reverse("remember-password")
|
|
||||||
|
|
||||||
post_params = {'email': 'test2@test.com'}
|
|
||||||
response = self.client.post(url, post_params, follow=True)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
|
||||||
|
|
||||||
jdata = json.loads(response.content)
|
|
||||||
self.assertIn("valid", jdata)
|
|
||||||
self.assertTrue(jdata['valid'])
|
|
||||||
|
|
||||||
def test_remember_password_not_exists(self):
|
|
||||||
url = reverse("remember-password")
|
|
||||||
|
|
||||||
post_params = {'email': 'test2@testa.com'}
|
|
||||||
response = self.client.post(url, post_params, follow=True)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
self.assertEqual(len(mail.outbox), 0)
|
|
||||||
|
|
||||||
jdata = json.loads(response.content)
|
|
||||||
self.assertIn("valid", jdata)
|
|
||||||
self.assertFalse(jdata['valid'])
|
|
||||||
|
|
||||||
def test_send_recovery_password_by_staff(self):
|
|
||||||
url = reverse("users-recovery-password", args=[self.user2.pk])
|
|
||||||
|
|
||||||
ok = self.client.login(username="test1", password="test")
|
|
||||||
self.assertTrue(ok)
|
|
||||||
|
|
||||||
# pre test
|
|
||||||
self.assertTrue(self.user2.is_active)
|
|
||||||
self.assertEqual(self.user2.token, None)
|
|
||||||
|
|
||||||
response = self.client.get(url, follow=True)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
# expected redirect
|
|
||||||
self.assertEqual(response.redirect_chain, [('http://testserver/users/2/edit/', 302)])
|
|
||||||
|
|
||||||
# test mail sending
|
|
||||||
self.assertEqual(len(mail.outbox), 1)
|
|
||||||
self.assertEqual(mail.outbox[0].subject, "Greenmine: password recovery.")
|
|
||||||
|
|
||||||
# test user model modification
|
|
||||||
self.user2 = User.objects.get(pk=self.user2.pk)
|
|
||||||
self.assertTrue(self.user2.is_active)
|
|
||||||
self.assertFalse(self.user2.has_usable_password())
|
|
||||||
self.assertNotEqual(self.user2.token, None)
|
|
||||||
|
|
||||||
url = reverse('password-recovery', args=[self.user2.token])
|
|
||||||
|
|
||||||
post_params = {
|
|
||||||
'password': '123123',
|
|
||||||
'password2': '123123',
|
|
||||||
}
|
|
||||||
response = self.client.post(url, post_params, follow=True)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
# expected redirect
|
|
||||||
self.assertEqual(response.redirect_chain, [('http://testserver/login/', 302)])
|
|
||||||
|
|
||||||
ok = self.client.login(username="test2", password="123123")
|
|
||||||
self.assertTrue(ok)
|
|
|
@ -1,13 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from . import api, routers
|
|
||||||
|
|
||||||
|
|
||||||
router = routers.SimpleRouter(trailing_slash=False)
|
|
||||||
router.register(r"users", api.UsersViewSet, base_name="users")
|
|
||||||
router.register(r"roles", api.RolesViewSet, base_name="roles")
|
|
||||||
router.register(r"search", api.Search, base_name="search")
|
|
||||||
router.register(r"auth/login", api.Login, base_name="auth-login")
|
|
||||||
router.register(r"auth/logout", api.Logout, base_name="auth-logout")
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
|
|
@ -0,0 +1 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
|
@ -1,14 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.models import Group, Permission
|
from django.contrib.auth.models import Group, Permission
|
||||||
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
|
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
|
||||||
|
|
||||||
from greenmine.base.models import Role, User
|
from .models import Role, User
|
||||||
from greenmine.base.forms import UserChangeForm, UserCreationForm
|
from .forms import UserChangeForm, UserCreationForm
|
||||||
|
|
||||||
|
|
||||||
admin.site.unregister(Group)
|
admin.site.unregister(Group)
|
||||||
|
|
||||||
|
|
||||||
class RoleAdmin(admin.ModelAdmin):
|
class RoleAdmin(admin.ModelAdmin):
|
||||||
list_display = ["name"]
|
list_display = ["name"]
|
||||||
filter_horizontal = ('permissions',)
|
filter_horizontal = ('permissions',)
|
||||||
|
@ -22,8 +26,10 @@ class RoleAdmin(admin.ModelAdmin):
|
||||||
return super(RoleAdmin, self).formfield_for_manytomany(
|
return super(RoleAdmin, self).formfield_for_manytomany(
|
||||||
db_field, request=request, **kwargs)
|
db_field, request=request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Role, RoleAdmin)
|
admin.site.register(Role, RoleAdmin)
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(DjangoUserAdmin):
|
class UserAdmin(DjangoUserAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields': ('username', 'password')}),
|
(None, {'fields': ('username', 'password')}),
|
|
@ -0,0 +1,134 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.db.models.loading import get_model
|
||||||
|
from django.contrib.auth import logout, login, authenticate
|
||||||
|
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
|
from rest_framework import status, viewsets
|
||||||
|
|
||||||
|
from djmail.template_mail import MagicMailBuilder
|
||||||
|
|
||||||
|
from greenmine.base import exceptions as excp
|
||||||
|
|
||||||
|
from .serializers import (
|
||||||
|
LoginSerializer,
|
||||||
|
UserLogged,
|
||||||
|
UserSerializer,
|
||||||
|
RoleSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .models import User, Role
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class RolesViewSet(viewsets.ViewSet):
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
serializer_class = RoleSerializer
|
||||||
|
|
||||||
|
def list(self, request, pk=None):
|
||||||
|
queryset = Role.objects.all()
|
||||||
|
serializer = self.serializer_class(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def retrieve(self, request, pk=None):
|
||||||
|
try:
|
||||||
|
role = Role.objects.get(pk=pk)
|
||||||
|
except Role.DoesNotExist:
|
||||||
|
raise excp.NotFound()
|
||||||
|
|
||||||
|
serializer = self.serializer_class(role)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class UsersViewSet(viewsets.ViewSet):
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def get_list_queryset(self):
|
||||||
|
project_model = get_model("scrum", "Project")
|
||||||
|
own_projects = (project_model.objects
|
||||||
|
.filter(members=self.request.user))
|
||||||
|
|
||||||
|
project = self.request.QUERY_PARAMS.get('project', None)
|
||||||
|
if project is not None:
|
||||||
|
own_projects = own_projects.filter(pk=project)
|
||||||
|
|
||||||
|
queryset = (User.objects.filter(projects__in=own_projects)
|
||||||
|
.order_by('username').distinct())
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def list(self, request, pk=None):
|
||||||
|
queryset = self.get_list_queryset()
|
||||||
|
serializer = UserSerializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def retrieve(self, request, pk=None):
|
||||||
|
return Response({})
|
||||||
|
|
||||||
|
@action(methods=["POST"], permission_classes=[])
|
||||||
|
def password_recovery(self, request, pk=None):
|
||||||
|
username_or_email = request.DATA.get('username', None)
|
||||||
|
|
||||||
|
if not username_or_email:
|
||||||
|
return Response({"detail": "Invalid username or password"},
|
||||||
|
status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
try:
|
||||||
|
queryset = User.objects.all()
|
||||||
|
user = queryset.get(Q(username=username_or_email) |
|
||||||
|
Q(email=username_or_email))
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return Response({"detail": "Invalid username or password"},
|
||||||
|
status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
user.token = str(uuid.uuid1())
|
||||||
|
user.save(update_fields=["token"])
|
||||||
|
|
||||||
|
mbuilder = MagicMailBuilder()
|
||||||
|
email = mbuilder.password_recovery(user.email, {"user": user})
|
||||||
|
|
||||||
|
return Response({"detail": "Mail sended successful!"})
|
||||||
|
|
||||||
|
|
||||||
|
class LoginViewSet(viewsets.ViewSet):
|
||||||
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
def create(self, request, **kwargs):
|
||||||
|
username = request.DATA.get('username', None)
|
||||||
|
password = request.DATA.get('password', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(username=username)
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return Response({"detail": "Invalid username or password"},
|
||||||
|
status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
if not user.check_password(password):
|
||||||
|
return Response({"detail": "Invalid username or password"},
|
||||||
|
status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
user = authenticate(username=username, password=password)
|
||||||
|
login(request, user)
|
||||||
|
|
||||||
|
serializer = UserSerializer(user)
|
||||||
|
response_data = serializer.data
|
||||||
|
response_data["token"] = request.session.session_key
|
||||||
|
|
||||||
|
return Response(response_data)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoutViewSet(viewsets.ViewSet):
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def list(self, request, **kwargs):
|
||||||
|
return self.logout(request)
|
||||||
|
|
||||||
|
def create(self, request, **kwargs):
|
||||||
|
return self.logout(request)
|
||||||
|
|
||||||
|
def logout(self, request):
|
||||||
|
logout(request)
|
||||||
|
return Response({})
|
|
@ -22,3 +22,5 @@ class SessionAuthentication(BaseAuthentication):
|
||||||
|
|
||||||
return (user, None)
|
return (user, None)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"model": "base.role",
|
"model": "users.role",
|
||||||
"fields": {
|
"fields": {
|
||||||
"permissions": [
|
"permissions": [
|
||||||
[
|
[
|
||||||
|
@ -51,32 +51,32 @@
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"add_role",
|
"add_role",
|
||||||
"base",
|
"users",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"change_role",
|
"change_role",
|
||||||
"base",
|
"users",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"delete_role",
|
"delete_role",
|
||||||
"base",
|
"users",
|
||||||
"role"
|
"role"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"add_user",
|
"add_user",
|
||||||
"base",
|
"users",
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"change_user",
|
"change_user",
|
||||||
"base",
|
"users",
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"delete_user",
|
"delete_user",
|
||||||
"base",
|
"users",
|
||||||
"user"
|
"user"
|
||||||
],
|
],
|
||||||
[
|
[
|
|
@ -1,7 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"model": "base.user",
|
"model": "users.user",
|
||||||
"fields": {
|
"fields": {
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"first_name": "",
|
"first_name": "",
|
|
@ -1,6 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.forms import UserCreationForm as DjangoUserCreationForm, UserChangeForm as DjangoUserChangeForm
|
from django.contrib.auth.forms import (
|
||||||
from greenmine.base.models import User
|
UserCreationForm as DjangoUserCreationForm,
|
||||||
|
UserChangeForm as DjangoUserChangeForm
|
||||||
|
)
|
||||||
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
class UserCreationForm(DjangoUserCreationForm):
|
class UserCreationForm(DjangoUserCreationForm):
|
|
@ -0,0 +1,46 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.contrib.auth.models import UserManager, AbstractUser
|
||||||
|
|
||||||
|
from greenmine.base.notifications.models import WatcherMixin
|
||||||
|
|
||||||
|
|
||||||
|
class User(WatcherMixin, AbstractUser):
|
||||||
|
color = models.CharField(max_length=9, null=False, blank=False, default="#669933",
|
||||||
|
verbose_name=_('color'))
|
||||||
|
description = models.TextField(null=False, blank=True,
|
||||||
|
verbose_name=_('description'))
|
||||||
|
photo = models.FileField(upload_to='files/msg', max_length=500, null=True, blank=True,
|
||||||
|
verbose_name=_('photo'))
|
||||||
|
default_language = models.CharField(max_length=20, null=False, blank=True, default='',
|
||||||
|
verbose_name=_('default language'))
|
||||||
|
default_timezone = models.CharField(max_length=20, null=False, blank=True, default='',
|
||||||
|
verbose_name=_('default timezone'))
|
||||||
|
token = models.CharField(max_length=200, null=False, blank=True, default='',
|
||||||
|
verbose_name=_('token'))
|
||||||
|
colorize_tags = models.BooleanField(null=False, blank=True, default=False,
|
||||||
|
verbose_name=_('colorize tags'))
|
||||||
|
objects = UserManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["username"]
|
||||||
|
|
||||||
|
|
||||||
|
class Role(models.Model):
|
||||||
|
name = models.CharField(max_length=200, null=False, blank=False,
|
||||||
|
verbose_name=_('name'))
|
||||||
|
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
|
||||||
|
verbose_name=_('slug'))
|
||||||
|
permissions = models.ManyToManyField('auth.Permission',
|
||||||
|
related_name='roles',
|
||||||
|
verbose_name=_('permissions'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = u'role'
|
||||||
|
verbose_name_plural = u'roles'
|
||||||
|
ordering = ['slug']
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from greenmine.base.models import User, Role
|
from .models import User, Role
|
||||||
|
|
||||||
|
|
||||||
class UserLogged(object):
|
class UserLogged(object):
|
||||||
|
@ -70,23 +70,3 @@ class RoleSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Role
|
model = Role
|
||||||
fields = ('id', 'name', 'slug', 'permissions',)
|
fields = ('id', 'name', 'slug', 'permissions',)
|
||||||
|
|
||||||
|
|
||||||
class SearchSerializer(serializers.Serializer):
|
|
||||||
id = serializers.CharField(max_length=255)
|
|
||||||
model_name = serializers.CharField(max_length=255)
|
|
||||||
pk = serializers.IntegerField()
|
|
||||||
score = serializers.FloatField()
|
|
||||||
stored_fields = serializers.SerializerMethodField('get_stored_fields')
|
|
||||||
|
|
||||||
def get_stored_fields(self, obj):
|
|
||||||
return obj.get_stored_fields()
|
|
||||||
|
|
||||||
def restore_object(self, attrs, instance=None):
|
|
||||||
"""
|
|
||||||
Given a dictionary of deserialized field values, either update
|
|
||||||
an existing model instance, or create a new model instance.
|
|
||||||
"""
|
|
||||||
if instance is not None:
|
|
||||||
return instance
|
|
||||||
return attrs
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -* coding: utf-8 -*-
|
# -* coding: utf-8 -*-
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from picklefield.fields import PickledObjectField
|
from picklefield.fields import PickledObjectField
|
||||||
|
@ -21,7 +22,7 @@ class Document(models.Model):
|
||||||
project = models.ForeignKey('scrum.Project', null=False, blank=False,
|
project = models.ForeignKey('scrum.Project', null=False, blank=False,
|
||||||
related_name='documents',
|
related_name='documents',
|
||||||
verbose_name=_('project'))
|
verbose_name=_('project'))
|
||||||
owner = models.ForeignKey('base.User', null=False, blank=False,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
||||||
related_name='owned_documents',
|
related_name='owned_documents',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
attached_file = models.FileField(max_length=1000, null=True, blank=True,
|
attached_file = models.FileField(max_length=1000, null=True, blank=True,
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from greenmine.base import routers
|
|
||||||
|
|
||||||
from . import api
|
|
||||||
|
|
||||||
|
|
||||||
router = routers.SimpleRouter(trailing_slash=False)
|
|
||||||
router.register(r"documents", api.DocumentsViewSet, base_name="documents")
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ class Question(models.Model):
|
||||||
|
|
||||||
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
||||||
verbose_name=_('ref'))
|
verbose_name=_('ref'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True, default=None,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
|
||||||
related_name='owned_questions',
|
related_name='owned_questions',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
status = models.ForeignKey('QuestionStatus', null=False, blank=False,
|
status = models.ForeignKey('QuestionStatus', null=False, blank=False,
|
||||||
|
@ -60,14 +61,14 @@ class Question(models.Model):
|
||||||
verbose_name=_('milestone'))
|
verbose_name=_('milestone'))
|
||||||
finished_date = models.DateTimeField(null=True, blank=True,
|
finished_date = models.DateTimeField(null=True, blank=True,
|
||||||
verbose_name=_('finished date'))
|
verbose_name=_('finished date'))
|
||||||
assigned_to = models.ForeignKey('base.User', null=True, blank=True, default=None,
|
assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
|
||||||
related_name='questions_assigned_to_me',
|
related_name='questions_assigned_to_me',
|
||||||
verbose_name=_('assigned_to'))
|
verbose_name=_('assigned_to'))
|
||||||
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
||||||
verbose_name=_('created date'))
|
verbose_name=_('created date'))
|
||||||
modified_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
modified_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
||||||
verbose_name=_('modified date'))
|
verbose_name=_('modified date'))
|
||||||
watchers = models.ManyToManyField('base.User', null=True, blank=True,
|
watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='watched_questions',
|
related_name='watched_questions',
|
||||||
verbose_name=_('watchers'))
|
verbose_name=_('watchers'))
|
||||||
tags = PickledObjectField(null=False, blank=True,
|
tags = PickledObjectField(null=False, blank=True,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
|
||||||
from rest_framework.urlpatterns import format_suffix_patterns
|
|
||||||
|
|
||||||
from . import api
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = format_suffix_patterns(patterns('',
|
|
||||||
url(r'^questions/$', api.QuestionList.as_view(), name='question-list'),
|
|
||||||
url(r'^questions/(?P<pk>[0-9]+)/$', api.QuestionDetail.as_view(), name='question-detail'),
|
|
||||||
))
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.db.models import Q
|
||||||
from rest_framework import mixins, viewsets
|
from rest_framework import mixins, viewsets
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
from greenmine.base.models import *
|
from greenmine.base.users.models import *
|
||||||
from greenmine.base.notifications.api import NotificationSenderMixin
|
from greenmine.base.notifications.api import NotificationSenderMixin
|
||||||
|
|
||||||
from greenmine.scrum.serializers import *
|
from greenmine.scrum.serializers import *
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.timezone import now
|
||||||
|
|
||||||
from django.contrib.webdesign import lorem_ipsum
|
from django.contrib.webdesign import lorem_ipsum
|
||||||
|
|
||||||
from greenmine.base.models import User, Role
|
from greenmine.base.users.models import User, Role
|
||||||
from greenmine.scrum.models import *
|
from greenmine.scrum.models import *
|
||||||
from greenmine.questions.models import *
|
from greenmine.questions.models import *
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db.models.loading import get_model
|
from django.db.models.loading import get_model
|
||||||
|
|
||||||
|
@ -174,11 +176,11 @@ class Points(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Membership(models.Model):
|
class Membership(models.Model):
|
||||||
user = models.ForeignKey('base.User', null=False, blank=False,
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
||||||
related_name="memberships")
|
related_name="memberships")
|
||||||
project = models.ForeignKey('Project', null=False, blank=False,
|
project = models.ForeignKey('Project', null=False, blank=False,
|
||||||
related_name="memberships")
|
related_name="memberships")
|
||||||
role = models.ForeignKey('base.Role', null=False, blank=False,
|
role = models.ForeignKey('users.Role', null=False, blank=False,
|
||||||
related_name="memberships")
|
related_name="memberships")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -198,10 +200,10 @@ class Project(models.Model, WatchedMixin):
|
||||||
verbose_name=_('created date'))
|
verbose_name=_('created date'))
|
||||||
modified_date = models.DateTimeField(auto_now=True, null=False, blank=False,
|
modified_date = models.DateTimeField(auto_now=True, null=False, blank=False,
|
||||||
verbose_name=_('modified date'))
|
verbose_name=_('modified date'))
|
||||||
owner = models.ForeignKey('base.User', null=False, blank=False,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
||||||
related_name='owned_projects',
|
related_name='owned_projects',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
members = models.ManyToManyField('base.User', related_name='projects', through='Membership',
|
members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='projects', through='Membership',
|
||||||
verbose_name=_('members'))
|
verbose_name=_('members'))
|
||||||
public = models.BooleanField(default=True, null=False, blank=True,
|
public = models.BooleanField(default=True, null=False, blank=True,
|
||||||
verbose_name=_('public'))
|
verbose_name=_('public'))
|
||||||
|
@ -264,12 +266,12 @@ class Project(models.Model, WatchedMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_roles(self):
|
def list_roles(self):
|
||||||
role_model = get_model('base', 'Role')
|
role_model = get_model('users', 'Role')
|
||||||
return role_model.objects.filter(id__in=list(self.memberships.values_list('role', flat=True)))
|
return role_model.objects.filter(id__in=list(self.memberships.values_list('role', flat=True)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_users(self):
|
def list_users(self):
|
||||||
user_model = get_model('base', 'User')
|
user_model = get_user_model()
|
||||||
return user_model.objects.filter(id__in=list(self.memberships.values_list('user', flat=True)))
|
return user_model.objects.filter(id__in=list(self.memberships.values_list('user', flat=True)))
|
||||||
|
|
||||||
def update_role_points(self):
|
def update_role_points(self):
|
||||||
|
@ -298,7 +300,7 @@ class Milestone(models.Model, WatchedMixin):
|
||||||
verbose_name=_('name'))
|
verbose_name=_('name'))
|
||||||
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
|
slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
|
||||||
verbose_name=_('slug'))
|
verbose_name=_('slug'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True, related_name='owned_milestones',
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name='owned_milestones',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
project = models.ForeignKey('Project', null=False, blank=False, related_name='milestones',
|
project = models.ForeignKey('Project', null=False, blank=False, related_name='milestones',
|
||||||
verbose_name=_('project'))
|
verbose_name=_('project'))
|
||||||
|
@ -403,7 +405,7 @@ class RolePoints(models.Model):
|
||||||
user_story = models.ForeignKey('UserStory', null=False, blank=False,
|
user_story = models.ForeignKey('UserStory', null=False, blank=False,
|
||||||
related_name='role_points',
|
related_name='role_points',
|
||||||
verbose_name=_('user story'))
|
verbose_name=_('user story'))
|
||||||
role = models.ForeignKey('base.Role', null=False, blank=False,
|
role = models.ForeignKey('users.Role', null=False, blank=False,
|
||||||
related_name='role_points',
|
related_name='role_points',
|
||||||
verbose_name=_('role'))
|
verbose_name=_('role'))
|
||||||
points = models.ForeignKey('Points', null=False, blank=False,
|
points = models.ForeignKey('Points', null=False, blank=False,
|
||||||
|
@ -425,7 +427,7 @@ class UserStory(WatchedMixin, models.Model):
|
||||||
project = models.ForeignKey('Project', null=False, blank=False,
|
project = models.ForeignKey('Project', null=False, blank=False,
|
||||||
related_name='user_stories',
|
related_name='user_stories',
|
||||||
verbose_name=_('project'))
|
verbose_name=_('project'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='owned_user_stories',
|
related_name='owned_user_stories',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
status = models.ForeignKey('UserStoryStatus', null=False, blank=False,
|
status = models.ForeignKey('UserStoryStatus', null=False, blank=False,
|
||||||
|
@ -447,7 +449,7 @@ class UserStory(WatchedMixin, models.Model):
|
||||||
verbose_name=_('subject'))
|
verbose_name=_('subject'))
|
||||||
description = models.TextField(null=False, blank=True,
|
description = models.TextField(null=False, blank=True,
|
||||||
verbose_name=_('description'))
|
verbose_name=_('description'))
|
||||||
watchers = models.ManyToManyField('base.User', null=True, blank=True,
|
watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='watched_us',
|
related_name='watched_us',
|
||||||
verbose_name=_('watchers'))
|
verbose_name=_('watchers'))
|
||||||
client_requirement = models.BooleanField(default=False, null=False, blank=True,
|
client_requirement = models.BooleanField(default=False, null=False, blank=True,
|
||||||
|
@ -504,7 +506,7 @@ class UserStory(WatchedMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Attachment(models.Model):
|
class Attachment(models.Model):
|
||||||
owner = models.ForeignKey('base.User', null=False, blank=False,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
||||||
related_name='change_attachments',
|
related_name='change_attachments',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
project = models.ForeignKey('Project', null=False, blank=False,
|
project = models.ForeignKey('Project', null=False, blank=False,
|
||||||
|
@ -541,7 +543,7 @@ class Task(models.Model, WatchedMixin):
|
||||||
verbose_name=_('user story'))
|
verbose_name=_('user story'))
|
||||||
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
||||||
verbose_name=_('ref'))
|
verbose_name=_('ref'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True, default=None,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
|
||||||
related_name='owned_tasks',
|
related_name='owned_tasks',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
status = models.ForeignKey('TaskStatus', null=False, blank=False,
|
status = models.ForeignKey('TaskStatus', null=False, blank=False,
|
||||||
|
@ -563,10 +565,10 @@ class Task(models.Model, WatchedMixin):
|
||||||
verbose_name=_('subject'))
|
verbose_name=_('subject'))
|
||||||
description = models.TextField(null=False, blank=True,
|
description = models.TextField(null=False, blank=True,
|
||||||
verbose_name=_('description'))
|
verbose_name=_('description'))
|
||||||
assigned_to = models.ForeignKey('base.User', blank=True, null=True, default=None,
|
assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, default=None,
|
||||||
related_name='user_storys_assigned_to_me',
|
related_name='user_storys_assigned_to_me',
|
||||||
verbose_name=_('assigned to'))
|
verbose_name=_('assigned to'))
|
||||||
watchers = models.ManyToManyField('base.User', null=True, blank=True,
|
watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='watched_tasks',
|
related_name='watched_tasks',
|
||||||
verbose_name=_('watchers'))
|
verbose_name=_('watchers'))
|
||||||
tags = PickledObjectField(null=False, blank=True,
|
tags = PickledObjectField(null=False, blank=True,
|
||||||
|
@ -624,7 +626,7 @@ class Issue(models.Model, WatchedMixin):
|
||||||
verbose_name=_('uuid'))
|
verbose_name=_('uuid'))
|
||||||
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None,
|
||||||
verbose_name=_('ref'))
|
verbose_name=_('ref'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True, default=None,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
|
||||||
related_name='owned_issues',
|
related_name='owned_issues',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
status = models.ForeignKey('IssueStatus', null=False, blank=False,
|
status = models.ForeignKey('IssueStatus', null=False, blank=False,
|
||||||
|
@ -655,10 +657,10 @@ class Issue(models.Model, WatchedMixin):
|
||||||
verbose_name=_('subject'))
|
verbose_name=_('subject'))
|
||||||
description = models.TextField(null=False, blank=True,
|
description = models.TextField(null=False, blank=True,
|
||||||
verbose_name=_('description'))
|
verbose_name=_('description'))
|
||||||
assigned_to = models.ForeignKey('base.User', blank=True, null=True, default=None,
|
assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, default=None,
|
||||||
related_name='issues_assigned_to_me',
|
related_name='issues_assigned_to_me',
|
||||||
verbose_name=_('assigned to'))
|
verbose_name=_('assigned to'))
|
||||||
watchers = models.ManyToManyField('base.User', null=True, blank=True,
|
watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='watched_issues',
|
related_name='watched_issues',
|
||||||
verbose_name=_('watchers'))
|
verbose_name=_('watchers'))
|
||||||
tags = PickledObjectField(null=False, blank=True,
|
tags = PickledObjectField(null=False, blank=True,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from greenmine.base.models import *
|
from greenmine.base.users.models import *
|
||||||
from greenmine.scrum.models import *
|
from greenmine.scrum.models import *
|
||||||
from picklefield.fields import dbsafe_encode, dbsafe_decode
|
from picklefield.fields import dbsafe_encode, dbsafe_decode
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.translation import ugettext
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
from greenmine.base import signals
|
from greenmine.base import signals
|
||||||
from greenmine.base.utils.auth import set_token
|
from greenmine.base.users.utils import set_token
|
||||||
from greenmine.base.mail.tasks import send_mail, send_bulk_mail
|
from greenmine.base.mail.tasks import send_mail, send_bulk_mail
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
|
||||||
from rest_framework.urlpatterns import format_suffix_patterns
|
|
||||||
|
|
||||||
from greenmine.scrum import api
|
|
||||||
from greenmine.base import routers
|
|
||||||
|
|
||||||
|
|
||||||
router = routers.DefaultRouter(trailing_slash=False)
|
|
||||||
router.register(r"projects", api.ProjectViewSet, base_name="projects")
|
|
||||||
router.register(r"milestones", api.MilestoneViewSet, base_name="milestones")
|
|
||||||
router.register(r"userstories", api.UserStoryViewSet, base_name="userstories")
|
|
||||||
router.register(r"issue-attachments", api.IssuesAttachmentViewSet, base_name="issue-attachments")
|
|
||||||
router.register(r"task-attachments", api.TasksAttachmentViewSet, base_name="task-attachments")
|
|
||||||
router.register(r"tasks", api.TaskViewSet, base_name="tasks")
|
|
||||||
router.register(r"issues", api.IssueViewSet, base_name="issues")
|
|
||||||
router.register(r"severities", api.SeverityViewSet, base_name="severities")
|
|
||||||
router.register(r"issue-statuses", api.IssueStatusViewSet, base_name="issue-statuses")
|
|
||||||
router.register(r"task-statuses", api.TaskStatusViewSet, base_name="task-statuses")
|
|
||||||
router.register(r"userstory-statuses", api.UserStoryStatusViewSet, base_name="userstory-statuses")
|
|
||||||
router.register(r"priorities", api.PriorityViewSet, base_name="priorities")
|
|
||||||
router.register(r"issue-types", api.IssueTypeViewSet, base_name="issue-types")
|
|
||||||
router.register(r"points", api.PointsViewSet, base_name="points")
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
|
||||||
|
|
||||||
#urlpatterns = format_suffix_patterns(patterns('',
|
|
||||||
# url(r'^projects/$', api.ProjectList.as_view(), name='project-list'),
|
|
||||||
# url(r'^projects/(?P<pk>[0-9]+)/$', api.ProjectDetail.as_view(), name='project-detail'),
|
|
||||||
# url(r'^milestones/$', api.MilestoneList.as_view(), name='milestone-list'),
|
|
||||||
# url(r'^milestones/(?P<pk>[0-9]+)/$', api.MilestoneDetail.as_view(), name='milestone-detail'),
|
|
||||||
# url(r'^user-stories/$', api.UserStoryList.as_view(), name='user-story-list'),
|
|
||||||
# url(r'^user-stories/(?P<pk>[0-9]+)/$', api.UserStoryDetail.as_view(), name='user-story-detail'),
|
|
||||||
# url(r'^user-stories/points/$', api.PointsList.as_view(), name='points-list'),
|
|
||||||
# url(r'^user-stories/points/(?P<pk>[0-9]+)/$', api.PointsDetail.as_view(), name='points-detail'),
|
|
||||||
# url(r'^user-stories/statuses/$', api.UserStoryStatusList.as_view(), name='user-story-status-list'),
|
|
||||||
# url(r'^user-stories/statuses/(?P<pk>[0-9]+)/$', api.UserStoryStatusDetail.as_view(), name='user-story-status-detail'),
|
|
||||||
# url(r'^issues/$', api.IssueList.as_view(), name='issues-list'),
|
|
||||||
# url(r'^issues/(?P<pk>[0-9]+)/$', api.IssueDetail.as_view(), name='issues-detail'),
|
|
||||||
# url(r'^issues/attachments/$', api.IssuesAttachmentList.as_view(), name='issues-attachment-list'),
|
|
||||||
# url(r'^issues/attachments/(?P<pk>[0-9]+)/$', api.IssuesAttachmentDetail.as_view(), name='issues-attachment-detail'),
|
|
||||||
# url(r'^issues/statuses/$', api.IssueStatusList.as_view(), name='issues-status-list'),
|
|
||||||
# url(r'^issues/statuses/(?P<pk>[0-9]+)/$', api.IssueStatusDetail.as_view(), name='issues-status-detail'),
|
|
||||||
# url(r'^issues/types/$', api.IssueTypeList.as_view(), name='issues-type-list'),
|
|
||||||
# url(r'^issues/types/(?P<pk>[0-9]+)/$', api.IssueTypeDetail.as_view(), name='issues-type-detail'),
|
|
||||||
# url(r'^tasks/$', api.TaskList.as_view(), name='tasks-list'),
|
|
||||||
# url(r'^tasks/(?P<pk>[0-9]+)/$', api.TaskDetail.as_view(), name='tasks-detail'),
|
|
||||||
# url(r'^tasks/attachments/$', api.TasksAttachmentList.as_view(), name='tasks-attachment-list'),
|
|
||||||
# url(r'^tasks/attachments/(?P<pk>[0-9]+)/$', api.TasksAttachmentDetail.as_view(), name='tasks-attachment-detail'),
|
|
||||||
# url(r'^severities/$', api.SeverityList.as_view(), name='severity-list'),
|
|
||||||
# url(r'^severities/(?P<pk>[0-9]+)/$', api.SeverityDetail.as_view(), name='severity-detail'),
|
|
||||||
# url(r'^tasks/statuses/$', api.TaskStatusList.as_view(), name='tasks-status-list'),
|
|
||||||
# url(r'^tasks/statuses/(?P<pk>[0-9]+)/$', api.TaskStatusDetail.as_view(), name='tasks-status-detail'),
|
|
||||||
# url(r'^priorities/$', api.PriorityList.as_view(), name='priority-list'),
|
|
||||||
# url(r'^priorities/(?P<pk>[0-9]+)/$', api.PriorityDetail.as_view(), name='priority-detail'),
|
|
||||||
#))
|
|
|
@ -191,8 +191,10 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|
||||||
'greenmine.base',
|
'greenmine.base',
|
||||||
'greenmine.base.mail',
|
|
||||||
'greenmine.base.notifications',
|
'greenmine.base.notifications',
|
||||||
|
'greenmine.base.users',
|
||||||
|
'greenmine.base.mail',
|
||||||
|
'greenmine.base.searches',
|
||||||
'greenmine.scrum',
|
'greenmine.scrum',
|
||||||
'greenmine.wiki',
|
'greenmine.wiki',
|
||||||
'greenmine.questions',
|
'greenmine.questions',
|
||||||
|
@ -258,7 +260,7 @@ LOGGING = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'base.User'
|
AUTH_USER_MODEL = 'users.User'
|
||||||
FORMAT_MODULE_PATH = 'greenmine.base.formats'
|
FORMAT_MODULE_PATH = 'greenmine.base.formats'
|
||||||
DATE_INPUT_FORMATS = (
|
DATE_INPUT_FORMATS = (
|
||||||
'%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y', '%b %d %Y',
|
'%Y-%m-%d', '%m/%d/%Y', '%d/%m/%Y', '%b %d %Y',
|
||||||
|
@ -289,7 +291,7 @@ HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
'greenmine.base.auth.SessionAuthentication',
|
'greenmine.base.users.auth.SessionAuthentication',
|
||||||
),
|
),
|
||||||
'FILTER_BACKEND': 'greenmine.base.filters.SimpleFilterBackend',
|
'FILTER_BACKEND': 'greenmine.base.filters.SimpleFilterBackend',
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,65 @@ from django.contrib import admin
|
||||||
|
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
|
from greenmine.base import routers
|
||||||
from greenmine.base.api import ApiRoot
|
from greenmine.base.api import ApiRoot
|
||||||
|
from greenmine.base.users.api import (
|
||||||
|
LoginViewSet,
|
||||||
|
LogoutViewSet,
|
||||||
|
RolesViewSet,
|
||||||
|
UsersViewSet
|
||||||
|
)
|
||||||
|
from greenmine.base.searches.api import SearchViewSet
|
||||||
|
from greenmine.scrum.api import (
|
||||||
|
MilestoneViewSet,
|
||||||
|
PriorityViewSet,
|
||||||
|
ProjectViewSet,
|
||||||
|
SeverityViewSet,
|
||||||
|
UserStoryStatusViewSet,
|
||||||
|
UserStoryViewSet,
|
||||||
|
TaskStatusViewSet,
|
||||||
|
TaskViewSet,
|
||||||
|
TasksAttachmentViewSet,
|
||||||
|
PointsViewSet,
|
||||||
|
IssueStatusViewSet,
|
||||||
|
IssueTypeViewSet,
|
||||||
|
IssueViewSet,
|
||||||
|
IssuesAttachmentViewSet
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
router = routers.DefaultRouter(trailing_slash=False)
|
||||||
|
# greenmine.base.users
|
||||||
|
router.register(r"users", UsersViewSet, base_name="users")
|
||||||
|
router.register(r"roles", RolesViewSet, base_name="roles")
|
||||||
|
router.register(r"auth/login", LoginViewSet, base_name="auth-login")
|
||||||
|
router.register(r"auth/logout", LogoutViewSet, base_name="auth-logout")
|
||||||
|
# greenmine.base.searches
|
||||||
|
router.register(r"search", SearchViewSet, base_name="search")
|
||||||
|
# greenmine.scrum
|
||||||
|
router.register(r"projects", ProjectViewSet, base_name="projects")
|
||||||
|
router.register(r"milestones", MilestoneViewSet, base_name="milestones")
|
||||||
|
router.register(r"userstories", UserStoryViewSet, base_name="userstories")
|
||||||
|
router.register(r"issue-attachments", IssuesAttachmentViewSet, base_name="issue-attachments")
|
||||||
|
router.register(r"task-attachments", TasksAttachmentViewSet, base_name="task-attachments")
|
||||||
|
router.register(r"tasks", TaskViewSet, base_name="tasks")
|
||||||
|
router.register(r"issues", IssueViewSet, base_name="issues")
|
||||||
|
router.register(r"severities", SeverityViewSet, base_name="severities")
|
||||||
|
router.register(r"issue-statuses", IssueStatusViewSet, base_name="issue-statuses")
|
||||||
|
router.register(r"task-statuses", TaskStatusViewSet, base_name="task-statuses")
|
||||||
|
router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userstory-statuses")
|
||||||
|
router.register(r"priorities", PriorityViewSet, base_name="priorities")
|
||||||
|
router.register(r"issue-types", IssueTypeViewSet, base_name="issue-types")
|
||||||
|
router.register(r"points", PointsViewSet, base_name="points")
|
||||||
|
#greenmine.issues
|
||||||
|
#greenmine.wiki
|
||||||
|
#greenmine.documents
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^api/v1/', include('greenmine.base.urls')),
|
url(r'^api/v1$', ApiRoot.as_view()),
|
||||||
url(r'^api/v1/', include('greenmine.scrum.urls')),
|
url(r'^api/v1/', include(router.urls)),
|
||||||
url(r'^api/v1/', include('greenmine.wiki.urls')),
|
# TODO: Refactor to use ViewSet
|
||||||
|
#url(r'^api/v1/', include('greenmine.wiki.urls')),
|
||||||
# TODO: Finish the documents and questions app
|
# TODO: Finish the documents and questions app
|
||||||
#url(r'^api/v1/', include('greenmine.questions.urls')),
|
#url(r'^api/v1/', include('greenmine.questions.urls')),
|
||||||
#url(r'^api/v1/', include('greenmine.documents.urls')),
|
#url(r'^api/v1/', include('greenmine.documents.urls')),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,10 +13,10 @@ class WikiPage(models.Model):
|
||||||
verbose_name=_('slug'))
|
verbose_name=_('slug'))
|
||||||
content = models.TextField(null=False, blank=True,
|
content = models.TextField(null=False, blank=True,
|
||||||
verbose_name=_('content'))
|
verbose_name=_('content'))
|
||||||
owner = models.ForeignKey('base.User', null=True, blank=True,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='owned_wiki_pages',
|
related_name='owned_wiki_pages',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
watchers = models.ManyToManyField('base.User', null=True, blank=True,
|
watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True,
|
||||||
related_name='watched_wiki_pages',
|
related_name='watched_wiki_pages',
|
||||||
verbose_name=_('watchers'))
|
verbose_name=_('watchers'))
|
||||||
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
||||||
|
@ -42,7 +43,7 @@ class WikiPageAttachment(models.Model):
|
||||||
wikipage = models.ForeignKey('WikiPage', null=False, blank=False,
|
wikipage = models.ForeignKey('WikiPage', null=False, blank=False,
|
||||||
related_name='attachments',
|
related_name='attachments',
|
||||||
verbose_name=_('wiki page'))
|
verbose_name=_('wiki page'))
|
||||||
owner = models.ForeignKey('base.User', null=False, blank=False,
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
|
||||||
related_name='owned_wiki_attachments',
|
related_name='owned_wiki_attachments',
|
||||||
verbose_name=_('owner'))
|
verbose_name=_('owner'))
|
||||||
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
|
||||||
from rest_framework.urlpatterns import format_suffix_patterns
|
|
||||||
from greenmine.wiki import api
|
|
||||||
|
|
||||||
urlpatterns = format_suffix_patterns(patterns('',
|
|
||||||
url(r'^pages$', api.WikiPageList.as_view(), name='wiki-page-list'),
|
|
||||||
url(r'^pages/(?P<projectid>\d+)-(?P<slug>[\w\-\d]+)$', api.WikiPageDetail.as_view(), name='wiki-page-detail'),
|
|
||||||
#url(r'^wiki_page_attachments/$', api.WikiPageAttachmentList.as_view(), name='wiki-page-attachment-list'),
|
|
||||||
#url(r'^wiki_page_attachments/(?P<pk>[0-9]+)/$', api.WikiPageAttachmentDetail.as_view(), name='wiki-page-attachment-detail'),
|
|
||||||
))
|
|
||||||
|
|
Loading…
Reference in New Issue