Refactoring base views (update to new viewsets)
parent
9eb35a898a
commit
a022085497
|
@ -1,111 +1,95 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import uuid
|
||||||
|
|
||||||
from django.contrib.auth import logout, login, authenticate
|
from django.contrib.auth import logout, login, authenticate
|
||||||
from django.contrib.auth.views import login as auth_login, logout as auth_logout
|
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 django import http
|
||||||
|
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.decorators import action
|
||||||
from rest_framework.parsers import JSONParser
|
|
||||||
from rest_framework.reverse import reverse
|
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework import status
|
from rest_framework import status, generics, viewsets
|
||||||
from rest_framework import generics
|
from rest_framework import exceptions as excp
|
||||||
|
|
||||||
|
import django_filters
|
||||||
from haystack import query, inputs
|
from haystack import query, inputs
|
||||||
|
from djmail.template_mail import MagicMailBuilder
|
||||||
|
|
||||||
|
from greenmine.base.serializers import (LoginSerializer, UserLogged,
|
||||||
|
UserSerializer, RoleSerializer)
|
||||||
|
|
||||||
from greenmine.base.serializers import LoginSerializer, UserLogged, UserSerializer, RoleSerializer
|
|
||||||
from greenmine.base.serializers import SearchSerializer
|
from greenmine.base.serializers import SearchSerializer
|
||||||
from greenmine.base.models import User, Role
|
from greenmine.base.models import User, Role
|
||||||
from greenmine.scrum import models
|
from greenmine.scrum import models
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
import django_filters
|
|
||||||
|
|
||||||
|
|
||||||
class ApiRoot(APIView):
|
class RolesViewSet(viewsets.ViewSet):
|
||||||
def get(self, request, format=None):
|
permission_classes = (IsAuthenticated,)
|
||||||
return Response({
|
|
||||||
'login': reverse('login', request=request, format=format),
|
|
||||||
'logout': reverse('logout', request=request, format=format),
|
|
||||||
'projects': reverse('project-list', request=request, format=format),
|
|
||||||
'milestones': reverse('milestone-list', request=request, format=format),
|
|
||||||
'user-stories': reverse('user-story-list', request=request, format=format),
|
|
||||||
'user-stories/statuses': reverse('user-story-status-list', request=request, format=format),
|
|
||||||
'user-stories/points': reverse('points-list', request=request, format=format),
|
|
||||||
'issues/attachments': reverse('issues-attachment-list', request=request, format=format),
|
|
||||||
'issues/statuses': reverse('issues-status-list', request=request, format=format),
|
|
||||||
'issues/types': reverse('issues-type-list', request=request, format=format),
|
|
||||||
'issues': reverse('issues-list', request=request, format=format),
|
|
||||||
'tasks': reverse('tasks-list', request=request, format=format),
|
|
||||||
'tasks/statuses': reverse('tasks-status-list', request=request, format=format),
|
|
||||||
'tasks/attachments': reverse('tasks-attachment-list', request=request, format=format),
|
|
||||||
'severities': reverse('severity-list', request=request, format=format),
|
|
||||||
'priorities': reverse('priority-list', request=request, format=format),
|
|
||||||
'documents': reverse('document-list', request=request, format=format),
|
|
||||||
'questions': reverse('question-list', request=request, format=format),
|
|
||||||
'wiki/pages': reverse('wiki-page-list', request=request, format=format),
|
|
||||||
'users': reverse('user-list', request=request, format=format),
|
|
||||||
'roles': reverse('user-roles', request=request, format=format),
|
|
||||||
'search': reverse('search', request=request, format=format),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class RoleList(generics.ListCreateAPIView):
|
|
||||||
model = Role
|
|
||||||
serializer_class = RoleSerializer
|
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):
|
||||||
|
role = Role.objects.get(pk=pk)
|
||||||
|
serializer = self.serializer_class(role)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class UsersViewSet(viewsets.ViewSet):
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_list_queryset(self):
|
||||||
return self.model.objects.all()
|
own_projects = (models.Project.objects
|
||||||
|
.filter(members=self.request.user))
|
||||||
|
|
||||||
|
|
||||||
class RoleDetail(generics.RetrieveAPIView):
|
|
||||||
model = Role
|
|
||||||
serializer_class = RoleSerializer
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
|
|
||||||
|
|
||||||
class UserFilter(django_filters.FilterSet):
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['is_active']
|
|
||||||
|
|
||||||
|
|
||||||
class UserList(generics.ListCreateAPIView):
|
|
||||||
model = User
|
|
||||||
serializer_class = UserSerializer
|
|
||||||
filter_class = UserFilter
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
projects = models.Project.objects.filter(members=self.request.user)
|
|
||||||
|
|
||||||
#Project filtering
|
|
||||||
project = self.request.QUERY_PARAMS.get('project', None)
|
project = self.request.QUERY_PARAMS.get('project', None)
|
||||||
if project is not None:
|
if project is not None:
|
||||||
projects = projects.filter(id=project)
|
own_projects = own_projects.filter(pk=project)
|
||||||
|
|
||||||
return super(UserList, self).get_queryset().filter(projects__in=projects)\
|
queryset = (User.objects.filter(projects__in=own_projects)
|
||||||
.order_by('id').distinct()
|
.order_by('username').distinct())
|
||||||
|
|
||||||
def pre_save(self, obj):
|
return queryset
|
||||||
pass
|
|
||||||
|
|
||||||
|
def list(self, request, pk=None):
|
||||||
|
queryset = self.get_list_queryset()
|
||||||
|
serializer = UserSerializer(queryset, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
|
def retrieve(self, request, pk=None):
|
||||||
model = User
|
return Response({})
|
||||||
serializer_class = UserSerializer
|
|
||||||
permission_classes = (IsAuthenticated,)
|
|
||||||
|
|
||||||
import uuid
|
@action(methods=["POST"], permission_classes=[])
|
||||||
from django.db.models import Q
|
def login(self, request, pk=None):
|
||||||
from djmail.template_mail import MagicMailBuilder
|
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)
|
||||||
|
|
||||||
class RecoveryPassword(APIView):
|
if not user.check_password(password):
|
||||||
def post(self, request):
|
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)
|
||||||
|
|
||||||
|
@action(methods=["POST"], permission_classes=[])
|
||||||
|
def password_recovery(self, request, pk=None):
|
||||||
username_or_email = request.DATA.get('username', None)
|
username_or_email = request.DATA.get('username', None)
|
||||||
|
|
||||||
if not username_or_email:
|
if not username_or_email:
|
||||||
|
@ -126,63 +110,31 @@ class RecoveryPassword(APIView):
|
||||||
|
|
||||||
return Response({"detail": "Mail sended successful!"})
|
return Response({"detail": "Mail sended successful!"})
|
||||||
|
|
||||||
|
@action(methods=["GET", "POST"])
|
||||||
class Login(APIView):
|
def logout(self, request, pk=None)
|
||||||
def post(self, request, format=None):
|
|
||||||
username = request.DATA.get('username', None)
|
|
||||||
password = request.DATA.get('password', None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(username=username)
|
|
||||||
if user.check_password(password):
|
|
||||||
user = authenticate(username=username, password=password)
|
|
||||||
login(request, user)
|
|
||||||
|
|
||||||
return_data = LoginSerializer(UserLogged(**{
|
|
||||||
'token': request.session.session_key,
|
|
||||||
'username': request.user.username,
|
|
||||||
'first_name': request.user.first_name,
|
|
||||||
'last_name': request.user.last_name,
|
|
||||||
'email': request.user.email,
|
|
||||||
'last_login': request.user.last_login,
|
|
||||||
'color': request.user.color,
|
|
||||||
'description': request.user.description,
|
|
||||||
'default_language': request.user.default_language,
|
|
||||||
'default_timezone': request.user.default_timezone,
|
|
||||||
'colorize_tags': request.user.colorize_tags,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return Response(return_data.data)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return Response({"detail": "Invalid username or password"}, status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Logout(APIView):
|
|
||||||
def post(self, request, format=None):
|
|
||||||
logout(request)
|
logout(request)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
class Search(APIView):
|
class Search(viewsets.ViewSet):
|
||||||
def get(self, request, format=None):
|
def list(self, request, **kwargs):
|
||||||
text = request.QUERY_PARAMS.get('text', None)
|
text = request.QUERY_PARAMS.get('text', None)
|
||||||
project = request.QUERY_PARAMS.get('project', None)
|
project_id = request.QUERY_PARAMS.get('project', None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
project = self._get_project(project_id)
|
||||||
|
except models.Project.DoesNotExist:
|
||||||
|
raise excp.PermissionDenied({"detail": "Wrong project id"})
|
||||||
|
|
||||||
if text and project:
|
|
||||||
#TODO: permission check
|
|
||||||
queryset = query.SearchQuerySet()
|
queryset = query.SearchQuerySet()
|
||||||
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
queryset = queryset.filter(text=inputs.AutoQuery(text))
|
||||||
queryset = queryset.filter(project_id=project)
|
queryset = queryset.filter(project_id=project_id)
|
||||||
|
|
||||||
return_data = SearchSerializer(queryset)
|
return_data = SearchSerializer(queryset)
|
||||||
return Response(return_data.data)
|
return Response(return_data.data)
|
||||||
|
|
||||||
return Response({"detail": "Parameter text can't be empty"}, status.HTTP_400_BAD_REQUEST)
|
def _get_project(self, project_id):
|
||||||
|
own_projects = (models.Project.objects
|
||||||
|
.filter(members=self.request.user))
|
||||||
|
|
||||||
|
|
||||||
|
return own_projects.get(pk=project_id)
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2011 Andrei Antoukh <niwi@niwi.be>
|
|
||||||
# License: BSD-3
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from base64 import b64encode, b64decode
|
|
||||||
|
|
||||||
try:
|
|
||||||
import cPickle as pickle
|
|
||||||
except ImportError:
|
|
||||||
import pickle
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger("niwi")
|
|
||||||
|
|
||||||
|
|
||||||
class DictField(models.Field):
|
|
||||||
""" Dictionary pickled field. """
|
|
||||||
__metaclass__ = models.SubfieldBase
|
|
||||||
__prefix__ = "pickle_dict::"
|
|
||||||
__pickleproto__ = -1
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if isinstance(value, dict):
|
|
||||||
return value
|
|
||||||
if isinstance(value, (str, unicode)) and value.startswith(self.__prefix__):
|
|
||||||
local_value = value[len(self.__prefix__):]
|
|
||||||
return pickle.loads(b64decode(str(local_value)))
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def get_db_prep_value(self, value, connection, prepared=False):
|
|
||||||
if value is not None:
|
|
||||||
if isinstance(value, dict):
|
|
||||||
value = self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
|
||||||
else:
|
|
||||||
raise TypeError('This field can only store dictionaries.')
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_internal_type(self):
|
|
||||||
return 'TextField'
|
|
||||||
|
|
||||||
def value_to_string(self, obj):
|
|
||||||
if not obj:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
value = getattr(obj, self.attname)
|
|
||||||
assert isinstance(value, dict)
|
|
||||||
return self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
|
||||||
return self.token.join(map(unicode, value))
|
|
||||||
|
|
||||||
def south_field_triple(self):
|
|
||||||
from south.modelsinspector import introspector
|
|
||||||
field_class = "django.db.models.fields.TextField"
|
|
||||||
args, kwargs = introspector(self)
|
|
||||||
return (field_class, args, kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ListField(models.Field):
|
|
||||||
""" Pickled list field. """
|
|
||||||
__metaclass__ = models.SubfieldBase
|
|
||||||
__prefix__ = "pickle_list::"
|
|
||||||
__pickleproto__ = -1
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if isinstance(value, (list, tuple)):
|
|
||||||
return value
|
|
||||||
|
|
||||||
if isinstance(value, (str, unicode)) and value.startswith(self.__prefix__):
|
|
||||||
local_value = value[len(self.__prefix__):]
|
|
||||||
return pickle.loads(b64decode(str(local_value)))
|
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_db_prep_value(self, value, connection, prepared=False):
|
|
||||||
if value is not None:
|
|
||||||
if isinstance(value, (list, tuple)):
|
|
||||||
value = self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
|
||||||
else:
|
|
||||||
raise TypeError('This field can only store list or tuple objects')
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def get_internal_type(self):
|
|
||||||
return 'TextField'
|
|
||||||
|
|
||||||
def value_to_string(self, obj):
|
|
||||||
if not obj:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
value = getattr(obj, self.attname)
|
|
||||||
assert isinstance(value, (list, tuple))
|
|
||||||
return self.__prefix__ + b64encode(pickle.dumps(value, protocol=self.__pickleproto__))
|
|
||||||
return self.token.join(map(unicode, value))
|
|
||||||
|
|
||||||
def south_field_triple(self):
|
|
||||||
from south.modelsinspector import introspector
|
|
||||||
field_class = "django.db.models.fields.TextField"
|
|
||||||
args, kwargs = introspector(self)
|
|
||||||
return (field_class, args, kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class CSVField(models.TextField):
|
|
||||||
__metaclass__ = models.SubfieldBase
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.token = kwargs.pop('token', ',')
|
|
||||||
super(CSVField, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
if isinstance(value, list):
|
|
||||||
return value
|
|
||||||
return value.split(self.token)
|
|
||||||
|
|
||||||
def get_db_prep_value(self, value):
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
assert(isinstance(value, list) or isinstance(value, tuple))
|
|
||||||
return self.token.join([unicode(s) for s in value])
|
|
||||||
|
|
||||||
def value_to_string(self, obj):
|
|
||||||
value = self._get_val_from_obj(obj)
|
|
||||||
return self.get_db_prep_value(value)
|
|
||||||
|
|
||||||
def south_field_triple(self):
|
|
||||||
from south.modelsinspector import introspector
|
|
||||||
field_class = "django.db.models.fields.TextField"
|
|
||||||
args, kwargs = introspector(self)
|
|
||||||
return (field_class, args, kwargs)
|
|
|
@ -18,6 +18,7 @@ class GreenmineSessionMiddleware(SessionMiddleware):
|
||||||
request.session = engine.SessionStore(session_key)
|
request.session = engine.SessionStore(session_key)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
COORS_ALLOWED_ORIGINS = getattr(settings, 'COORS_ALLOWED_ORIGINS', '*')
|
COORS_ALLOWED_ORIGINS = getattr(settings, 'COORS_ALLOWED_ORIGINS', '*')
|
||||||
COORS_ALLOWED_METHODS = getattr(settings, 'COORS_ALLOWED_METHODS',
|
COORS_ALLOWED_METHODS = getattr(settings, 'COORS_ALLOWED_METHODS',
|
||||||
['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE', 'PATCH'])
|
['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE', 'PATCH'])
|
||||||
|
|
|
@ -47,6 +47,9 @@ class User(WatcherMixin, AbstractUser):
|
||||||
verbose_name=_('colorize tags'))
|
verbose_name=_('colorize tags'))
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["username"]
|
||||||
|
|
||||||
|
|
||||||
class Role(models.Model):
|
class Role(models.Model):
|
||||||
name = models.CharField(max_length=200, null=False, blank=False,
|
name = models.CharField(max_length=200, null=False, blank=False,
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from rest_framework.urlpatterns import format_suffix_patterns
|
from rest_framework.urlpatterns import format_suffix_patterns
|
||||||
|
from rest_framework import routers
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from greenmine.base import api
|
from greenmine.base import api
|
||||||
|
|
||||||
|
# Special router for actions.
|
||||||
|
actions_router = routers.Route(url=r'^{prefix}/actions/{methodname}{trailing_slash}$',
|
||||||
|
mapping={'{httpmethod}': '{methodname}'},
|
||||||
|
name='{basename}-{methodnamehyphen}',
|
||||||
|
initkwargs={})
|
||||||
|
|
||||||
urlpatterns = format_suffix_patterns(patterns('',
|
router = routers.DefaultRouter(trailing_slash=False)
|
||||||
url(r'^auth/login/$', api.Login.as_view(), name='login'),
|
router.routes.append(actions_router)
|
||||||
url(r'^auth/logout/$', api.Logout.as_view(), name='logout'),
|
router.register("users", api.UsersViewSet, base_name="users")
|
||||||
url(r'^users/$', api.UserList.as_view(), name="user-list"),
|
router.register("roles", api.RolesViewSet, base_name="roles")
|
||||||
url(r'^users/(?P<pk>[0-9]+)/$', api.UserDetail.as_view(), name="user-detail"),
|
router.register("search", api.Search, base_name="search")
|
||||||
url(r'^roles/$', api.RoleList.as_view(), name="roles"),
|
|
||||||
url(r'^roles/(?P<pk>[0-9]+)/$', api.RoleDetail.as_view(), name='role-detail'),
|
urlpatterns = router.urls
|
||||||
url(r'^search/$', api.Search.as_view(), name="search"),
|
|
||||||
url(r'^$', api.ApiRoot.as_view(), name='api_root'),
|
|
||||||
))
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
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 picklefield.fields import PickledObjectField
|
||||||
from greenmine.base.utils.slug import slugify_uniquely as slugify
|
from greenmine.base.utils.slug import slugify_uniquely as slugify
|
||||||
from greenmine.base.fields import DictField
|
|
||||||
|
|
||||||
|
|
||||||
class Document(models.Model):
|
class Document(models.Model):
|
||||||
|
@ -27,7 +27,7 @@ class Document(models.Model):
|
||||||
attached_file = models.FileField(max_length=1000, null=True, blank=True,
|
attached_file = models.FileField(max_length=1000, null=True, blank=True,
|
||||||
upload_to='documents',
|
upload_to='documents',
|
||||||
verbose_name=_('attached_file'))
|
verbose_name=_('attached_file'))
|
||||||
tags = DictField(null=False, blank=True,
|
tags = PickledObjectField(null=False, blank=True,
|
||||||
verbose_name=_('tags'))
|
verbose_name=_('tags'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -6,7 +6,6 @@ from django.utils import timezone
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
from greenmine.base.utils.slug import slugify_uniquely, ref_uniquely
|
||||||
from greenmine.base.fields import DictField
|
|
||||||
from greenmine.scrum.models import Project
|
from greenmine.scrum.models import Project
|
||||||
|
|
||||||
from picklefield.fields import PickledObjectField
|
from picklefield.fields import PickledObjectField
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib import admin
|
||||||
admin.autodiscover()
|
admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^api/', include('greenmine.base.urls')),
|
url(r'^api/core/', include('greenmine.base.urls')),
|
||||||
url(r'^api/scrum/', include('greenmine.scrum.urls')),
|
url(r'^api/scrum/', include('greenmine.scrum.urls')),
|
||||||
url(r'^api/documents/', include('greenmine.documents.urls')),
|
url(r'^api/documents/', include('greenmine.documents.urls')),
|
||||||
url(r'^api/questions/', include('greenmine.questions.urls')),
|
url(r'^api/questions/', include('greenmine.questions.urls')),
|
||||||
|
|
|
@ -16,7 +16,7 @@ git+git://github.com/toastdriven/django-haystack.git
|
||||||
django-picklefield==0.3.0
|
django-picklefield==0.3.0
|
||||||
django-reversion==1.7
|
django-reversion==1.7
|
||||||
django-sampledatahelper==0.0.1
|
django-sampledatahelper==0.0.1
|
||||||
djangorestframework==2.2.5
|
djangorestframework==2.3.6
|
||||||
gunicorn==17.5
|
gunicorn==17.5
|
||||||
kombu==2.5.12
|
kombu==2.5.12
|
||||||
mimeparse==0.1.3
|
mimeparse==0.1.3
|
||||||
|
|
Loading…
Reference in New Issue