parent
8e08cab3c4
commit
910d3b4beb
15 changed files with 310 additions and 36 deletions
|
@ -3,11 +3,13 @@ from django.conf.urls import url, include
|
|||
from rest_framework import routers
|
||||
|
||||
from . import views as api_v
|
||||
from ..utils import viewsets as utils_viewsets
|
||||
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
router.register(r'administrative-urls', api_v.AdminURLs, base_name='adminurls')
|
||||
router.register(r'admin-sections', utils_viewsets.AdminSectionViewSet)
|
||||
router.register(r'admin-entries', utils_viewsets.AdminEntryViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^auth/login/', api_v.Login.as_view()),
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from django.contrib.auth import user_logged_in
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from djoser.views import (LoginView, PasswordResetView,
|
||||
PasswordResetConfirmView)
|
||||
|
@ -39,31 +35,3 @@ class PasswordResetConfirm(PasswordResetConfirmView):
|
|||
response = super(PasswordResetConfirm, self).action(serializer)
|
||||
response.data = {}
|
||||
return response
|
||||
|
||||
|
||||
class AdminURLs(ViewSet):
|
||||
def get_view_name(self):
|
||||
return 'Admin URLs List'
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
urls = OrderedDict([
|
||||
('projects', [
|
||||
['project', 'projects', 'project'],
|
||||
['grant', 'projects', 'grant'],
|
||||
['grant-report', 'projects', 'grantreport'],
|
||||
]),
|
||||
|
||||
('collections', [
|
||||
['collection-type', 'collections_ccdb', 'collectiontype'],
|
||||
]),
|
||||
])
|
||||
|
||||
data = OrderedDict()
|
||||
for category, group in urls.items():
|
||||
paths = []
|
||||
for url in group:
|
||||
lookup = 'admin:{}_{}_changelist'.format(url[1], url[2])
|
||||
path = reverse(lookup, request=request)
|
||||
paths.append({'id': url[0], 'url': path})
|
||||
data[category] = paths
|
||||
return Response(data)
|
||||
|
|
7
ccdb/utils/admin.py
Normal file
7
ccdb/utils/admin.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import AdminSection, AdminEntry
|
||||
|
||||
|
||||
admin.site.register(AdminSection)
|
||||
admin.site.register(AdminEntry)
|
39
ccdb/utils/migrations/0001_initial.py
Normal file
39
ccdb/utils/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-08-29 04:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AdminEntry',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('package', models.CharField(max_length=255)),
|
||||
('model', models.CharField(max_length=255)),
|
||||
('sort', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AdminSection',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('sort', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='adminentry',
|
||||
name='section',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.AdminSection'),
|
||||
),
|
||||
]
|
72
ccdb/utils/migrations/0002_initial_admin_enties.py
Normal file
72
ccdb/utils/migrations/0002_initial_admin_enties.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
from collections import namedtuple
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
Section = namedtuple('Section', ['name', 'package', 'entries'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
def migrate(apps, schema_editor):
|
||||
AdminSection = apps.get_model('utils', 'AdminSection')
|
||||
AdminEntry = apps.get_model('utils', 'AdminEntry')
|
||||
|
||||
# Clean up any old stuff
|
||||
for model in [AdminEntry, AdminSection]:
|
||||
model.objects.all().delete()
|
||||
|
||||
data = [
|
||||
Section('Collections', 'collections_ccdb', [
|
||||
'collectiontype', 'collectionmethod', 'flaw', 'adfgpermit',
|
||||
'datasheetattachment', 'collectiontrap', 'collection'
|
||||
]),
|
||||
Section('Experiments', 'experiments', [
|
||||
'flaw', 'experiment', 'protocolattachment', 'treatmenttype',
|
||||
'treatment', 'treatmentreplicate', 'alivedeadcount'
|
||||
]),
|
||||
Section('Locations', 'locations', [
|
||||
'region', 'site', 'municipallocation', 'studylocation',
|
||||
'storagelocation'
|
||||
]),
|
||||
Section('Misc.', 'misc', [
|
||||
'measurementunit', 'measurementtype', 'container', 'material',
|
||||
'color'
|
||||
]),
|
||||
Section('Processing', 'processing', [
|
||||
'processtype', 'reagent', 'flaw'
|
||||
]),
|
||||
Section('Projects', 'projects', [
|
||||
'project', 'grant', 'grantreport'
|
||||
]),
|
||||
Section('Species', 'species', [
|
||||
'species', 'trapspecies', 'collectionspecies'
|
||||
]),
|
||||
Section('Users', 'users', [
|
||||
'user'
|
||||
]),
|
||||
Section('Utils', 'utils', [
|
||||
'adminsection', 'adminentry'
|
||||
]),
|
||||
]
|
||||
|
||||
for i, adminsection in enumerate(data):
|
||||
section = AdminSection.objects.create(name=adminsection.name,
|
||||
sort=i)
|
||||
for j, entry in enumerate(adminsection.entries):
|
||||
AdminEntry.objects.create(package=adminsection.package,
|
||||
model=entry, section=section, sort=j)
|
||||
|
||||
def rollback(apps, schema_editor):
|
||||
AdminSection = apps.get_model('utils', 'AdminSection')
|
||||
AdminEntry = apps.get_model('utils', 'AdminEntry')
|
||||
|
||||
for model in [AdminEntry, AdminSection]:
|
||||
model.objects.all().delete()
|
||||
|
||||
dependencies = [
|
||||
('utils', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate, rollback),
|
||||
]
|
26
ccdb/utils/models.py
Normal file
26
ccdb/utils/models.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from django.db import models
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
|
||||
class AdminSection(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
sort = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class AdminEntry(models.Model):
|
||||
package = models.CharField(max_length=255)
|
||||
model = models.CharField(max_length=255)
|
||||
section = models.ForeignKey(AdminSection)
|
||||
sort = models.IntegerField()
|
||||
|
||||
def admin_url(self, request=None):
|
||||
lookup = 'admin:{}_{}_changelist'.format(self.package, self.model)
|
||||
relative = reverse(lookup)
|
||||
return request.build_absolute_uri(relative)
|
||||
|
||||
def __str__(self):
|
||||
return "%s %s" % (self.package, self.model)
|
20
ccdb/utils/serializers.py
Normal file
20
ccdb/utils/serializers.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from .models import AdminSection, AdminEntry
|
||||
|
||||
|
||||
class AdminSectionSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AdminSection
|
||||
fields = ('id', 'name', 'sort')
|
||||
|
||||
|
||||
class AdminEntrySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AdminEntry
|
||||
fields = ('id', 'admin_url', 'package', 'model', 'section', 'sort')
|
||||
|
||||
admin_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_admin_url(self, obj):
|
||||
return obj.admin_url(self.context.get('request'))
|
0
ccdb/utils/tests/__init__.py
Normal file
0
ccdb/utils/tests/__init__.py
Normal file
21
ccdb/utils/tests/factories.py
Normal file
21
ccdb/utils/tests/factories.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from factory import DjangoModelFactory, Sequence, SubFactory
|
||||
|
||||
from ..models import AdminSection, AdminEntry
|
||||
|
||||
|
||||
class AdminSectionFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = AdminSection
|
||||
|
||||
name = Sequence(lambda n: 'section{}'.format(n))
|
||||
sort = Sequence(lambda n: n)
|
||||
|
||||
|
||||
class AdminEntryFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = AdminEntry
|
||||
|
||||
package = Sequence(lambda n: 'package{}'.format(n))
|
||||
model = Sequence(lambda n: 'section{}'.format(n))
|
||||
section = SubFactory(AdminSectionFactory)
|
||||
sort = Sequence(lambda n: n)
|
26
ccdb/utils/tests/test_models.py
Normal file
26
ccdb/utils/tests/test_models.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from ..models import AdminSection, AdminEntry
|
||||
from .factories import AdminSectionFactory, AdminEntryFactory
|
||||
|
||||
|
||||
class AdminSectionTestCase(TestCase):
|
||||
def test_creation(self):
|
||||
a = AdminSectionFactory()
|
||||
self.assertTrue(isinstance(a, AdminSection))
|
||||
self.assertEqual(a.__str__(), a.name)
|
||||
|
||||
|
||||
class AdminEntryTestCase(TestCase):
|
||||
def test_creation(self):
|
||||
a = AdminEntryFactory()
|
||||
self.assertTrue(isinstance(a, AdminEntry))
|
||||
self.assertEqual(a.__str__(), "%s %s" % (a.package, a.model))
|
||||
|
||||
def test_admin_url(self):
|
||||
a = AdminEntryFactory(package='utils', model='adminentry')
|
||||
request_factory = RequestFactory()
|
||||
request = request_factory.get('/')
|
||||
self.assertEqual('http://testserver/admin/utils/adminentry/',
|
||||
a.admin_url(request))
|
25
ccdb/utils/tests/test_serializers.py
Normal file
25
ccdb/utils/tests/test_serializers.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from .factories import AdminSectionFactory, AdminEntryFactory
|
||||
from ..serializers import AdminSectionSerializer, AdminEntrySerializer
|
||||
|
||||
|
||||
class AdminSectionTestCase(TestCase):
|
||||
def test_creation(self):
|
||||
a = AdminSectionFactory()
|
||||
serializer = AdminSectionSerializer(a)
|
||||
data = {'name': a.name, 'id': a.id, 'sort': a.sort}
|
||||
self.assertEqual(data, serializer.data)
|
||||
|
||||
|
||||
class AdminEntryTestCase(TestCase):
|
||||
def test_creation(self):
|
||||
a = AdminEntryFactory(package='utils', model='adminentry')
|
||||
request_factory = RequestFactory()
|
||||
request = request_factory.get('/')
|
||||
serializer = AdminEntrySerializer(a, context={'request': request})
|
||||
data = {'package': a.package, 'id': a.id, 'sort': a.sort,
|
||||
'model': a.model, 'section': a.section.id,
|
||||
'admin_url': a.admin_url(request)}
|
||||
self.assertEqual(data, serializer.data)
|
37
ccdb/utils/tests/test_viewsets.py
Normal file
37
ccdb/utils/tests/test_viewsets.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from .factories import AdminSectionFactory, AdminEntryFactory
|
||||
from ccdb.users.models import User
|
||||
|
||||
|
||||
class AdminTestCase(APITestCase):
|
||||
def setUp(self):
|
||||
self.username = 'abc'
|
||||
self.password = '123'
|
||||
self.user = User.objects.create(username=self.username,
|
||||
password=self.password)
|
||||
self.token = Token.objects.create(user=self.user)
|
||||
self.client.login(username=self.username, password=self.password)
|
||||
self.client.credentials(HTTP_AUTHORIZATION='Token %s' % self.token.key)
|
||||
|
||||
|
||||
class AdminSectionTestCase(AdminTestCase):
|
||||
def test_list(self):
|
||||
AdminSectionFactory.create_batch(5)
|
||||
url = reverse('api:v1:adminsection-list')
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['results']), 5)
|
||||
|
||||
|
||||
class AdminEntryTestCase(AdminTestCase):
|
||||
def test_list(self):
|
||||
AdminEntryFactory.create_batch(5, package='utils', model='adminentry')
|
||||
url = reverse('api:v1:adminentry-list')
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['results']), 5)
|
17
ccdb/utils/viewsets.py
Normal file
17
ccdb/utils/viewsets.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from .models import AdminSection, AdminEntry
|
||||
from .serializers import AdminSectionSerializer, AdminEntrySerializer
|
||||
|
||||
|
||||
class AdminSectionViewSet(viewsets.ModelViewSet):
|
||||
queryset = AdminSection.objects.all()
|
||||
serializer_class = AdminSectionSerializer
|
||||
|
||||
|
||||
class AdminEntryViewSet(viewsets.ModelViewSet):
|
||||
queryset = AdminEntry.objects.all()
|
||||
serializer_class = AdminEntrySerializer
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {'request': self.request}
|
|
@ -196,9 +196,23 @@ REST_FRAMEWORK = {
|
|||
],
|
||||
'DEFAULT_VERSIONING_CLASS':
|
||||
'rest_framework.versioning.NamespaceVersioning',
|
||||
'DEFAULT_PAGINATION_CLASS':
|
||||
'drf_ember_pagination.EmberPageNumberPagination',
|
||||
'PAGE_SIZE': 100,
|
||||
'EXCEPTION_HANDLER':
|
||||
'rest_framework_json_api.exceptions.exception_handler',
|
||||
'DEFAULT_PAGINATION_CLASS':
|
||||
'rest_framework_json_api.pagination.PageNumberPagination',
|
||||
'DEFAULT_PARSER_CLASSES': (
|
||||
'rest_framework_json_api.parsers.JSONParser',
|
||||
'rest_framework.parsers.FormParser',
|
||||
'rest_framework.parsers.MultiPartParser'
|
||||
),
|
||||
'DEFAULT_RENDERER_CLASSES': (
|
||||
'rest_framework_json_api.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
),
|
||||
'DEFAULT_METADATA_CLASS':
|
||||
'rest_framework_json_api.metadata.JSONAPIMetadata',
|
||||
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
|
||||
}
|
||||
|
||||
SITE_ID = 1
|
||||
|
|
|
@ -21,4 +21,4 @@ djangorestframework==3.4.4
|
|||
django-cors-headers==1.1.0
|
||||
django-filter==0.14.0
|
||||
djoser==0.5.0
|
||||
git+git://github.com/thermokarst/drf_ember_pagination.git
|
||||
djangorestframework-jsonapi==2.1.0
|
||||
|
|
Loading…
Add table
Reference in a new issue