Commit c8e1dbbc by Chris Dodge

add queryable group lists

parent 3de3238b
""" API implementation for group-oriented interactions. """ """ API implementation for group-oriented interactions. """
import uuid import uuid
import json import json
from collections import OrderedDict
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
...@@ -33,56 +34,73 @@ def _generate_base_uri(request): ...@@ -33,56 +34,73 @@ def _generate_base_uri(request):
return resource_uri return resource_uri
@api_view(['POST']) @api_view(['GET', 'POST'])
@permission_classes((ApiKeyHeaderPermission,)) @permission_classes((ApiKeyHeaderPermission,))
def group_list(request): def group_list(request):
""" """
GET retrieves a list of groups in the system filtered by type
POST creates a new group in the system POST creates a new group in the system
""" """
response_data = {} if request.method == 'GET':
base_uri = _generate_base_uri(request) if not 'type' in request.GET:
# Group name must be unique, but we need to support dupes return Response({}, status=status.HTTP_400_BAD_REQUEST)
group = Group.objects.create(name=str(uuid.uuid4()))
original_group_name = request.DATA['name']
group.name = '{:04d}: {}'.format(group.id, original_group_name) response_data = []
group.record_active = True profiles = GroupProfile.objects.filter(group_type=request.GET['type'])
group.record_date_created = timezone.now() for profile in profiles:
group.record_date_modified = timezone.now() item_data = OrderedDict()
group.save() item_data['group_id'] = profile.group_id
item_data['group_type'] = profile.group_type
item_data['data'] = json.loads(profile.data)
response_data.append(item_data)
# Relationship model also allows us to use duplicate names return Response(response_data)
GroupRelationship.objects.create(name=original_group_name, group_id=group.id, parent_group=None) elif request.method == 'POST':
response_data = {}
base_uri = _generate_base_uri(request)
# Group name must be unique, but we need to support dupes
group = Group.objects.create(name=str(uuid.uuid4()))
original_group_name = request.DATA['name']
# allow for optional meta information about groups, this will end up in the GroupProfile table group.name = '{:04d}: {}'.format(group.id, original_group_name)
group_type = request.DATA.get('group_type') group.record_active = True
data = request.DATA.get('data') group.record_date_created = timezone.now()
group.record_date_modified = timezone.now()
group.save()
if group_type or data: # Relationship model also allows us to use duplicate names
profile, _ = GroupProfile.objects.get_or_create(group_id=group.id, group_type=group_type, data=data) GroupRelationship.objects.create(name=original_group_name, group_id=group.id, parent_group=None)
response_data = {'id': group.id, 'name': original_group_name} # allow for optional meta information about groups, this will end up in the GroupProfile table
base_uri = _generate_base_uri(request) group_type = request.DATA.get('group_type')
response_data['uri'] = '{}/{}'.format(base_uri, group.id) data = request.DATA.get('data')
response_status = status.HTTP_201_CREATED
return Response(response_data, status=response_status) if group_type or data:
profile, _ = GroupProfile.objects.get_or_create(group_id=group.id, group_type=group_type, data=data)
response_data = {'id': group.id, 'name': original_group_name}
base_uri = _generate_base_uri(request)
response_data['uri'] = '{}/{}'.format(base_uri, group.id)
response_status = status.HTTP_201_CREATED
return Response(response_data, status=response_status)
@api_view(['GET']) @api_view(['GET', 'POST'])
@permission_classes((ApiKeyHeaderPermission,)) @permission_classes((ApiKeyHeaderPermission,))
def group_detail(request, group_id): def group_detail(request, group_id):
""" """
GET retrieves an existing group from the system GET retrieves an existing group from the system
""" """
response_data = {} response_data = {}
base_uri = _generate_base_uri(request) base_uri = _generate_base_uri(request)
try: try:
existing_group = Group.objects.get(id=group_id) existing_group = Group.objects.get(id=group_id)
existing_group_relationship = GroupRelationship.objects.get(group_id=group_id) existing_group_relationship = GroupRelationship.objects.get(group_id=group_id)
except ObjectDoesNotExist: except ObjectDoesNotExist:
existing_group = None return Response({}, status.HTTP_404_NOT_FOUND)
existing_group_relationship = None
if existing_group and existing_group_relationship: if request.method == 'GET':
response_data['name'] = existing_group_relationship.name response_data['name'] = existing_group_relationship.name
response_data['id'] = existing_group.id response_data['id'] = existing_group.id
response_data['uri'] = base_uri response_data['uri'] = base_uri
...@@ -99,15 +117,28 @@ def group_detail(request, group_id): ...@@ -99,15 +117,28 @@ def group_detail(request, group_id):
response_data['group_type'] = existing_group_profile.group_type response_data['group_type'] = existing_group_profile.group_type
data = existing_group_profile.data data = existing_group_profile.data
if data: if data:
print '******* data = {0}'.format(data)
response_data['data'] = json.loads(data) response_data['data'] = json.loads(data)
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
response_status = status.HTTP_200_OK response_status = status.HTTP_200_OK
else:
response_status = status.HTTP_404_NOT_FOUND return Response(response_data, status=response_status)
return Response(response_data, status=response_status) elif request.method == 'POST':
# update GroupProfile data
group_type = request.DATA.get('group_type')
data = request.DATA.get('data')
if not group_type and not data:
return Response({}, status.HTTP_400_BAD_REQUEST)
profile, _ = GroupProfile.objects.get_or_create(group_id=group_id)
profile.group_type = group_type
profile.data = data
profile.save()
return Response({})
@api_view(['POST']) @api_view(['POST'])
......
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'CourseGroupRelationship'
db.create_table('api_manager_coursegrouprelationship', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Group'])),
))
db.send_create_signal('api_manager', ['CourseGroupRelationship'])
# Adding model 'GroupProfile'
db.create_table('auth_groupprofile', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Group'])),
('group_type', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, db_index=True)),
('data', self.gf('django.db.models.fields.TextField')(blank=True)),
))
db.send_create_signal('api_manager', ['GroupProfile'])
def backwards(self, orm):
# Deleting model 'CourseGroupRelationship'
db.delete_table('api_manager_coursegrouprelationship')
# Deleting model 'GroupProfile'
db.delete_table('auth_groupprofile')
models = {
'api_manager.coursegrouprelationship': {
'Meta': {'object_name': 'CourseGroupRelationship'},
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'api_manager.groupprofile': {
'Meta': {'object_name': 'GroupProfile', 'db_table': "'auth_groupprofile'"},
'data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
'group_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'api_manager.grouprelationship': {
'Meta': {'object_name': 'GroupRelationship'},
'group': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'parent_group': ('django.db.models.fields.related.ForeignKey', [], {'default': '0', 'related_name': "'child_groups'", 'null': 'True', 'blank': 'True', 'to': "orm['api_manager.GroupRelationship']"}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'record_date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 4, 21, 0, 0)'}),
'record_date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'api_manager.linkedgrouprelationship': {
'Meta': {'object_name': 'LinkedGroupRelationship'},
'from_group_relationship': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'from_group_relationships'", 'to': "orm['api_manager.GroupRelationship']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'record_date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2014, 4, 21, 0, 0)'}),
'record_date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'to_group_relationship': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'to_group_relationships'", 'to': "orm['api_manager.GroupRelationship']"})
},
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['api_manager']
\ No newline at end of file
...@@ -5,18 +5,16 @@ Run these tests @ Devstack: ...@@ -5,18 +5,16 @@ Run these tests @ Devstack:
rake fasttest_lms[common/djangoapps/api_manager/tests/test_group_views.py] rake fasttest_lms[common/djangoapps/api_manager/tests/test_group_views.py]
""" """
from random import randint from random import randint
import unittest
import uuid import uuid
import json import json
from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.test import TestCase, Client from django.test import TestCase, Client
from django.test.utils import override_settings from django.test.utils import override_settings
from api_manager.models import GroupRelationship from api_manager.models import GroupRelationship
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory
TEST_API_KEY = str(uuid.uuid4()) TEST_API_KEY = str(uuid.uuid4())
...@@ -119,6 +117,21 @@ class GroupsApiTests(TestCase): ...@@ -119,6 +117,21 @@ class GroupsApiTests(TestCase):
self.assertGreater(response.data['id'], 0) self.assertGreater(response.data['id'], 0)
group_id = response.data['id'] group_id = response.data['id']
# query for list of groups, but don't put the type filter (bad)
test_uri = self.base_groups_uri
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 400)
# try again with filter
test_uri = self.base_groups_uri + '?type=series'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['group_id'], group_id)
self.assertEqual(response.data[0]['group_type'], 'series')
self.assertEqual(response.data[0]['data']['display_name'], 'My first series')
# query the group detail
test_uri = self.base_groups_uri + '/' + str(group_id) test_uri = self.base_groups_uri + '/' + str(group_id)
response = self.do_get(test_uri) response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
...@@ -127,7 +140,35 @@ class GroupsApiTests(TestCase): ...@@ -127,7 +140,35 @@ class GroupsApiTests(TestCase):
self.assertEqual(response.data['uri'], confirm_uri) self.assertEqual(response.data['uri'], confirm_uri)
self.assertEqual(response.data['name'], self.test_group_name) self.assertEqual(response.data['name'], self.test_group_name)
self.assertEqual(response.data['group_type'], 'series') self.assertEqual(response.data['group_type'], 'series')
self.assertEqual(response.data['data']['display_name'],'My first series') self.assertEqual(response.data['data']['display_name'], 'My first series')
# update the profile
# first with missing data
response = self.do_post(test_uri, {})
self.assertEqual(response.status_code, 400)
data = {
'name': self.test_group_name,
'group_type': 'seriesX',
'data': json.dumps({'display_name': 'My updated series'})
}
response = self.do_post(test_uri, data)
self.assertEqual(response.status_code, 200)
# requery the filter
test_uri = self.base_groups_uri + '?type=series'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)
test_uri = self.base_groups_uri + '?type=seriesX'
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['group_id'], group_id)
self.assertEqual(response.data[0]['group_type'], 'seriesX')
self.assertEqual(response.data[0]['data']['display_name'], 'My updated series')
def test_group_detail_get_undefined(self): def test_group_detail_get_undefined(self):
test_uri = self.base_groups_uri + '/123456789' test_uri = self.base_groups_uri + '/123456789'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment