Commit 5200eea5 by Zia Fazal Committed by Jonathan Piacenti

is_private field and workgroup lookup

added is_private field to workgroup model and lookup of workgroup by
users

added organization field to Project

Fixed broken test
parent 7a5937f5
......@@ -358,12 +358,13 @@ class GroupsApiTests(ModuleStoreTestCase):
for num in range(0, 5):
local_username = self.test_username + str(randint(11, 99))
local_email = str(randint(11,99)) + self.test_email
if num == 3:
is_active = False
data = {
'email': self.test_email,
'email': local_email,
'username': local_username,
'password': self.test_password,
'first_name': self.test_first_name,
......@@ -373,6 +374,7 @@ class GroupsApiTests(ModuleStoreTestCase):
# associating a user with a group
response = self.do_post(self.base_users_uri, data)
self.assertEqual(response.status_code, 201)
user_id = response.data['id']
test_uri = self.base_groups_uri + '/' + str(group_id) + '/users'
response = self.do_post(test_uri, data={'user_id': user_id})
......
......@@ -13,6 +13,7 @@ from django.utils.translation import ugettext as _
from django.core.cache import cache
from django.test import TestCase, Client
from django.test.utils import override_settings
from projects.models import Project
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
......@@ -33,6 +34,7 @@ class SecureClient(Client):
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
@override_settings(EDX_API_KEY=TEST_API_KEY)
@override_settings(PASSWORD_MIN_LENGTH=4)
@override_settings(API_PAGE_SIZE=10)
@patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': True})
class UsersApiTests(TestCase):
......@@ -47,6 +49,25 @@ class UsersApiTests(TestCase):
self.test_last_name = str(uuid.uuid4())
self.test_city = str(uuid.uuid4())
self.test_course_data = '<html>{}</html>'.format(str(uuid.uuid4()))
self.course = CourseFactory.create()
self.course_content = ItemFactory.create(
category="videosequence",
parent_location=self.course.location,
data=self.test_course_data,
due="2016-05-16T14:30:00Z",
display_name="View_Sequence"
)
self.test_project = Project.objects.create(
course_id=self.course.id,
content_id=self.course_content.id
)
self.second_test_project = Project.objects.create(
course_id=self.course.id + 'b2',
content_id=self.course_content.id + 'b2'
)
self.client = SecureClient()
cache.clear()
......@@ -89,7 +110,6 @@ class UsersApiTests(TestCase):
user_id = response.data['id']
return user_id
@override_settings(API_PAGE_SIZE=10)
def test_user_list_get(self):
test_uri = '/api/users'
......@@ -946,3 +966,47 @@ class UsersApiTests(TestCase):
# test with invalid user
response = self.do_get('/api/users/4356340/organizations/')
self.assertEqual(response.status_code, 404)
def test_user_workgroups_list(self):
test_workgroups_uri = '/api/workgroups/'
user_id = self._create_test_user()
for i in xrange(1, 12):
project_id = self.test_project.id
if i > 7: # set to other project
project_id = self.second_test_project.id
data = {
'name': 'Workgroup ' + str(i),
'project': project_id
}
response = self.do_post(test_workgroups_uri, data)
self.assertEqual(response.status_code, 201)
test_uri = '{}{}/'.format(test_workgroups_uri, str(response.data['id']))
users_uri = '{}users/'.format(test_uri)
data = {"id": user_id}
response = self.do_post(users_uri, data)
self.assertEqual(response.status_code, 201)
test_uri = '/api/users/{}/workgroups/?page_size=10'.format(user_id)
response = self.do_get(test_uri)
self.assertEqual(response.data['count'], 11)
self.assertEqual(len(response.data['results']), 10)
self.assertEqual(response.data['num_pages'], 2)
# test with course_id filter
response = self.do_get('/api/users/{}/workgroups/?course_id={}'.format(user_id, self.course.id))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 7)
self.assertEqual(len(response.data['results']), 7)
self.assertIsNotNone(response.data['results'][0]['name'])
self.assertIsNotNone(response.data['results'][0]['project'])
# test with invalid user
response = self.do_get('/api/users/4356340/workgroups/')
self.assertEqual(response.status_code, 404)
# test with valid user but has no workgroup
another_user_id = self._create_test_user()
response = self.do_get('/api/users/{}/workgroups/'.format(another_user_id))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 0)
self.assertEqual(len(response.data['results']), 0)
......@@ -16,6 +16,7 @@ urlpatterns = patterns(
url(r'^(?P<user_id>[0-9]+)/groups/(?P<group_id>[0-9]+)$', users_views.UsersGroupsDetail.as_view(), name='users-groups-detail'),
url(r'^(?P<user_id>[0-9]+)/preferences$', users_views.UsersPreferences.as_view(), name='users-preferences-list'),
url(r'^(?P<user_id>[0-9]+)/organizations/$', users_views.UsersOrganizationsList.as_view(), name='users-organizations-list'),
url(r'^(?P<user_id>[0-9]+)/workgroups/$', users_views.UsersWorkgroupsList.as_view(), name='users-workgroups-list'),
)
urlpatterns = format_suffix_patterns(urlpatterns)
......@@ -17,6 +17,7 @@ from django.db.models import Q
from api_manager.permissions import SecureAPIView, SecureListAPIView
from api_manager.models import GroupProfile
from api_manager.organizations.serializers import OrganizationSerializer
from projects.serializers import BasicWorkgroupSerializer
from .serializers import UserSerializer
from courseware import module_render
......@@ -898,3 +899,30 @@ class UsersOrganizationsList(SecureListAPIView):
raise Http404
return user.organizations.all()
class UsersWorkgroupsList(SecureListAPIView):
"""
### The UsersWorkgroupsList view allows clients to retrieve a list of workgroups a user
belongs to
- URI: ```/api/users/{user_id}/workgroups/```
- GET: Provides paginated list of workgroups for a user
To filter the user's workgroup set by course
GET ```/api/users/{user_id}/workgroups/?course={course_id}```
"""
serializer_class = BasicWorkgroupSerializer
def get_queryset(self):
user_id = self.kwargs['user_id']
course_id = self.request.QUERY_PARAMS.get('course_id', None)
try:
user = User.objects.get(id=user_id)
except ObjectDoesNotExist:
raise Http404
queryset = user.workgroups.all()
if course_id:
queryset = queryset.filter(project__course_id=course_id)
return queryset
# -*- 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 field 'Project.organization'
db.add_column('projects_project', 'organization',
self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='projects', null=True, on_delete=models.SET_NULL, to=orm['api_manager.Organization']),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Project.organization'
db.delete_column('projects_project', 'organization_id')
models = {
'api_manager.organization': {
'Meta': {'object_name': 'Organization'},
'contact_email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'contact_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'contact_phone': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'organizations'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'workgroups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'organizations'", 'symmetrical': 'False', 'to': "orm['projects.Workgroup']"})
},
'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'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'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'})
},
'projects.project': {
'Meta': {'unique_together': "(('course_id', 'content_id'),)", 'object_name': 'Project'},
'content_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'projects'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['api_manager.Organization']"})
},
'projects.workgroup': {
'Meta': {'object_name': 'Workgroup'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'workgroups'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workgroups'", 'to': "orm['projects.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'workgroups'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'projects.workgrouppeerreview': {
'Meta': {'object_name': 'WorkgroupPeerReview'},
'answer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'question': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'reviewer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workgroup_peer_reviewees'", 'to': "orm['auth.User']"}),
'workgroup': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'peer_reviews'", 'to': "orm['projects.Workgroup']"})
},
'projects.workgroupreview': {
'Meta': {'object_name': 'WorkgroupReview'},
'answer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'question': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'reviewer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'workgroup': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'workgroup_reviews'", 'to': "orm['projects.Workgroup']"})
},
'projects.workgroupsubmission': {
'Meta': {'object_name': 'WorkgroupSubmission'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'document_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'document_mime_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'document_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'submissions'", 'to': "orm['auth.User']"}),
'workgroup': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'submissions'", 'to': "orm['projects.Workgroup']"})
},
'projects.workgroupsubmissionreview': {
'Meta': {'object_name': 'WorkgroupSubmissionReview'},
'answer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'question': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'reviewer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'submission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reviews'", 'to': "orm['projects.WorkgroupSubmission']"})
}
}
complete_apps = ['projects']
\ No newline at end of file
......@@ -14,6 +14,8 @@ class Project(TimeStampedModel):
"""
course_id = models.CharField(max_length=255)
content_id = models.CharField(max_length=255)
organization = models.ForeignKey('api_manager.Organization', blank=True, null=True, related_name="projects"
, on_delete=models.SET_NULL)
class Meta:
""" Meta class for defining additional model characteristics """
......
......@@ -49,7 +49,7 @@ class ProjectSerializer(serializers.HyperlinkedModelSerializer):
model = Project
fields = (
'id', 'url', 'created', 'modified', 'course_id', 'content_id',
'workgroups'
'organization', 'workgroups'
)
......@@ -125,3 +125,14 @@ class WorkgroupSerializer(serializers.HyperlinkedModelSerializer):
'groups', 'users', 'submissions',
'workgroup_reviews', 'peer_reviews'
)
class BasicWorkgroupSerializer(serializers.HyperlinkedModelSerializer):
""" Basic Workgroup Serializer to keep only basic fields """
class Meta:
""" Meta class for defining additional serializer characteristics """
model = Workgroup
fields = (
'id', 'url', 'created', 'modified', 'name', 'project',
)
......@@ -35,6 +35,7 @@ class ProjectsApiTests(TestCase):
def setUp(self):
self.test_server_prefix = 'https://testserver'
self.test_projects_uri = '/api/projects/'
self.test_organizations_uri = '/api/organizations/'
self.test_project_name = str(uuid.uuid4())
self.test_course_id = 'edx/demo/course'
......@@ -93,11 +94,19 @@ class ProjectsApiTests(TestCase):
return response
def test_projects_list_post(self):
data = {
'name': 'Test Organization'
}
response = self.do_post(self.test_organizations_uri, data)
self.assertEqual(response.status_code, 201)
test_org_id = response.data['id']
test_course_content_id = "i4x://blahblah1234"
data = {
'name': self.test_project_name,
'course_id': self.test_course_id,
'content_id': test_course_content_id
'content_id': test_course_content_id,
'organization': test_org_id
}
response = self.do_post(self.test_projects_uri, data)
self.assertEqual(response.status_code, 201)
......@@ -108,7 +117,7 @@ class ProjectsApiTests(TestCase):
str(response.data['id'])
)
self.assertEqual(response.data['url'], confirm_uri)
self.assertGreater(response.data['id'], 0)
self.assertEqual(response.data['organization'], test_org_id)
self.assertEqual(response.data['course_id'], self.test_course_id)
self.assertEqual(response.data['content_id'], test_course_content_id)
self.assertIsNotNone(response.data['workgroups'])
......
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