Commit c9de3875 by Zia Fazal Committed by Jonathan Piacenti

initial changes

completed tests and added type filter

removed organizations app

migration to delete tables
parent 31adeb0c
...@@ -1713,6 +1713,10 @@ class CoursesMetricsCompletionsLeadersList(SecureAPIView): ...@@ -1713,6 +1713,10 @@ class CoursesMetricsCompletionsLeadersList(SecureAPIView):
```/api/courses/{course_id}/metrics/completions/leaders/?count=6``` ```/api/courses/{course_id}/metrics/completions/leaders/?count=6```
To get only percentage of a user and course average skipleaders parameter can be used To get only percentage of a user and course average skipleaders parameter can be used
```/api/courses/{course_id}/metrics/completions/leaders/?user_id={user_id}&skipleaders=true``` ```/api/courses/{course_id}/metrics/completions/leaders/?user_id={user_id}&skipleaders=true```
To get data for one or more orgnaizations organizations filter can be applied
* organizations filter can be a single id or multiple ids separated by comma
```/api/courses/{course_id}/metrics/completions/leaders/?organizations={organization_id1},{organization_id2}```
### Use Cases/Notes: ### Use Cases/Notes:
* Example: Display leaders in terms of completions in a given course * Example: Display leaders in terms of completions in a given course
* Example: Display top 3 users leading in terms of completions in a given course * Example: Display top 3 users leading in terms of completions in a given course
......
...@@ -16,7 +16,8 @@ from django.test import Client ...@@ -16,7 +16,8 @@ from django.test import Client
from django.test.utils import override_settings from django.test.utils import override_settings
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from api_manager.models import GroupRelationship, GroupProfile, Organization from api_manager.models import GroupRelationship, GroupProfile
from organizations.models import Organization
from projects.models import Project from projects.models import Project
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
......
...@@ -13,7 +13,7 @@ from api_manager.courseware_access import get_course ...@@ -13,7 +13,7 @@ from api_manager.courseware_access import get_course
from api_manager.models import GroupRelationship, CourseGroupRelationship, GroupProfile, APIUser as User from api_manager.models import GroupRelationship, CourseGroupRelationship, GroupProfile, APIUser as User
from api_manager.permissions import SecureAPIView, SecureListAPIView from api_manager.permissions import SecureAPIView, SecureListAPIView
from api_manager.utils import str2bool, generate_base_uri from api_manager.utils import str2bool, generate_base_uri
from api_manager.organizations import serializers from organizations import serializers
from projects.serializers import BasicWorkgroupSerializer, GroupSerializer from projects.serializers import BasicWorkgroupSerializer, GroupSerializer
......
...@@ -6,7 +6,8 @@ import json ...@@ -6,7 +6,8 @@ import json
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from api_manager.models import GroupProfile, Organization from api_manager.models import GroupProfile
from organizations.models import Organization
......
...@@ -9,7 +9,8 @@ from django.contrib.auth.models import Group, User ...@@ -9,7 +9,8 @@ from django.contrib.auth.models import Group, User
from django.test import TestCase from django.test import TestCase
from api_manager.management.commands import migrate_orgdata from api_manager.management.commands import migrate_orgdata
from api_manager.models import GroupProfile, GroupRelationship, Organization from api_manager.models import GroupProfile, GroupRelationship
from organizations.models import Organization
class MigrateOrgDataTests(TestCase): class MigrateOrgDataTests(TestCase):
......
# -*- 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):
# Deleting model 'Organization'
db.delete_table('api_manager_organization')
# Removing M2M table for field users on 'Organization'
db.delete_table('api_manager_organization_users')
# Removing M2M table for field groups on 'Organization'
db.delete_table('api_manager_organization_groups')
# Removing M2M table for field workgroups on 'Organization'
db.delete_table('api_manager_organization_workgroups')
def backwards(self, orm):
# Adding model 'Organization'
db.create_table('api_manager_organization', (
('contact_email', self.gf('django.db.models.fields.EmailField')(max_length=255, null=True, blank=True)),
('contact_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('display_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)),
('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)),
('logo_url', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('contact_phone', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True)),
))
db.send_create_signal('api_manager', ['Organization'])
# Adding M2M table for field users on 'Organization'
db.create_table('api_manager_organization_users', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['api_manager.organization'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('api_manager_organization_users', ['organization_id', 'user_id'])
# Adding M2M table for field groups on 'Organization'
db.create_table('api_manager_organization_groups', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['api_manager.organization'], null=False)),
('group', models.ForeignKey(orm['auth.group'], null=False))
))
db.create_unique('api_manager_organization_groups', ['organization_id', 'group_id'])
# Adding M2M table for field workgroups on 'Organization'
db.create_table('api_manager_organization_workgroups', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['api_manager.organization'], null=False)),
('workgroup', models.ForeignKey(orm['projects.workgroup'], null=False))
))
db.create_unique('api_manager_organization_workgroups', ['organization_id', 'workgroup_id'])
models = {
'api_manager.coursecontentgrouprelationship': {
'Meta': {'unique_together': "(('course_id', 'content_id', 'group_profile'),)", 'object_name': 'CourseContentGroupRelationship'},
'content_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'group_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['api_manager.GroupProfile']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'api_manager.coursegrouprelationship': {
'Meta': {'object_name': 'CourseGroupRelationship'},
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'api_manager.coursemodulecompletion': {
'Meta': {'object_name': 'CourseModuleCompletion'},
'content_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'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'}),
'stage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'course_completions'", 'to': "orm['auth.User']"})
},
'api_manager.groupprofile': {
'Meta': {'object_name': 'GroupProfile', 'db_table': "'auth_groupprofile'"},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True'}),
'group_type': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'db_index': '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', 'null': 'True', 'blank': 'True'}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
'api_manager.grouprelationship': {
'Meta': {'object_name': 'GroupRelationship'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'group': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'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'})
},
'api_manager.linkedgrouprelationship': {
'Meta': {'object_name': 'LinkedGroupRelationship'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'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'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'record_active': ('django.db.models.fields.BooleanField', [], {'default': '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'})
},
'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'})
}
}
complete_apps = ['api_manager']
\ No newline at end of file
...@@ -8,7 +8,6 @@ from django.conf import settings ...@@ -8,7 +8,6 @@ from django.conf import settings
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from .utils import is_int from .utils import is_int
from projects.models import Workgroup
class GroupRelationship(TimeStampedModel): class GroupRelationship(TimeStampedModel):
...@@ -134,22 +133,6 @@ class CourseContentGroupRelationship(TimeStampedModel): ...@@ -134,22 +133,6 @@ class CourseContentGroupRelationship(TimeStampedModel):
unique_together = ("course_id", "content_id", "group_profile") unique_together = ("course_id", "content_id", "group_profile")
class Organization(TimeStampedModel):
"""
Main table representing the Organization concept. Organizations are
primarily a collection of Users.
"""
name = models.CharField(max_length=255)
display_name = models.CharField(max_length=255, null=True, blank=True)
contact_name = models.CharField(max_length=255, null=True, blank=True)
contact_email = models.EmailField(max_length=255, null=True, blank=True)
contact_phone = models.CharField(max_length=50, null=True, blank=True)
logo_url = models.CharField(max_length=255, blank=True, null=True)
workgroups = models.ManyToManyField(Workgroup, related_name="organizations")
users = models.ManyToManyField(User, related_name="organizations")
groups = models.ManyToManyField(Group, related_name="organizations")
class CourseModuleCompletion(TimeStampedModel): class CourseModuleCompletion(TimeStampedModel):
""" """
The CourseModuleCompletion model contains user, course, module information The CourseModuleCompletion model contains user, course, module information
......
...@@ -13,7 +13,7 @@ from django.conf.urls import include, patterns, url ...@@ -13,7 +13,7 @@ from django.conf.urls import include, patterns, url
from rest_framework.routers import SimpleRouter from rest_framework.routers import SimpleRouter
from api_manager.organizations.views import OrganizationsViewSet from organizations.views import OrganizationsViewSet
from api_manager.system import views as system_views from api_manager.system import views as system_views
from projects import views as project_views from projects import views as project_views
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
from rest_framework import serializers from rest_framework import serializers
from api_manager.models import APIUser from api_manager.models import APIUser
from api_manager.organizations.serializers import BasicOrganizationSerializer from organizations.serializers import BasicOrganizationSerializer
from student.models import UserProfile from student.models import UserProfile
class DynamicFieldsModelSerializer(serializers.ModelSerializer): class DynamicFieldsModelSerializer(serializers.ModelSerializer):
......
...@@ -50,7 +50,7 @@ from api_manager.courses.serializers import CourseModuleCompletionSerializer ...@@ -50,7 +50,7 @@ from api_manager.courses.serializers import CourseModuleCompletionSerializer
from api_manager.courseware_access import get_course, get_course_child, get_course_key, course_exists from api_manager.courseware_access import get_course, get_course_child, get_course_key, course_exists
from api_manager.permissions import SecureAPIView, SecureListAPIView, IdsInFilterBackend, HasOrgsFilterBackend from api_manager.permissions import SecureAPIView, SecureListAPIView, IdsInFilterBackend, HasOrgsFilterBackend
from api_manager.models import GroupProfile, APIUser as User from api_manager.models import GroupProfile, APIUser as User
from api_manager.organizations.serializers import OrganizationSerializer from organizations.serializers import OrganizationSerializer
from api_manager.utils import generate_base_uri, dict_has_items, extract_data_params from api_manager.utils import generate_base_uri, dict_has_items, extract_data_params
from projects.serializers import BasicWorkgroupSerializer from projects.serializers import BasicWorkgroupSerializer
from .serializers import UserSerializer, UserCountByCitySerializer, UserRolesSerializer from .serializers import UserSerializer, UserCountByCitySerializer, UserRolesSerializer
......
__author__ = 'zia'
"""
One-time data migration script -- should not need to run it again
"""
import logging
from django.core.management.base import BaseCommand
from django.db import connection, transaction
from organizations.models import Organization
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""
Moves existing organizations data from api_manager app to organizations app
"""
help = "Command to move existing organizations from api_manager app to organizations app"
def handle(self, *args, **options):
existing_entries = Organization.objects.all().count()
if existing_entries == 0:
try:
cursor = connection.cursor()
cursor.execute('INSERT INTO organizations_organization SELECT * from api_manager_organization')
log_msg = 'organizations entries moved from api_manager to organizations app'
self.print_message(log_msg)
cursor.execute('INSERT INTO organizations_organization_workgroups '
'SELECT * from api_manager_organization_workgroups')
log_msg = 'organization_workgroups entries moved from api_manager to organizations app'
self.print_message(log_msg)
cursor.execute('INSERT INTO organizations_organization_users '
'SELECT * from api_manager_organization_users')
log_msg = 'organization_users entries moved from api_manager to organizations app'
self.print_message(log_msg)
cursor.execute('INSERT INTO organizations_organization_groups '
'SELECT * from api_manager_organization_groups')
log_msg = 'organization_groups entries moved from api_manager to organizations app'
self.print_message(log_msg)
transaction.commit()
except Exception as e:
log_msg = e.message
self.print_message(log_msg)
else:
log_msg = 'oroganizations_organization is not empty. You might have already filled it.'
self.print_message(log_msg)
def print_message(self, msg):
print msg
log.info(msg)
import mock
from django.test import TestCase
from django.db import connection, transaction, models
from organizations.management.commands import move_organizations_entries
from organizations.models import Organization
from student.tests.factories import UserFactory, GroupFactory
from django.contrib.auth.models import Group, User
from projects.models import Workgroup, Project
from model_utils.models import TimeStampedModel
from south.db import db
@mock.patch.dict("django.conf.settings.FEATURES", {'ORGANIZATIONS_APP': True})
class MoveOrganizationEntriesTests(TestCase):
"""
Test suite for organization table data copy from api_manager to organizations
"""
def setUp(self):
# Create tables and add data
user1 = UserFactory()
user2 = UserFactory()
group1 = GroupFactory()
group2 = GroupFactory()
proj = Project()
proj.course_id = 'slashes:test+cs234+ct323'
proj.content_id = 'location:test+cs234+ct323+chapter+b145cc8196734885ac8835b841d486ee'
proj.save()
workgroup = Workgroup()
workgroup.name = 'Test workgroup'
workgroup.project = proj
workgroup.save()
attrs = {
'name': models.CharField(max_length=255),
'display_name': models.CharField(max_length=255, null=True, blank=True),
'contact_name': models.CharField(max_length=255, null=True, blank=True),
'contact_email': models.EmailField(max_length=255, null=True, blank=True),
'contact_phone': models.CharField(max_length=50, null=True, blank=True),
'logo_url': models.CharField(max_length=255, blank=True, null=True),
'workgroups': models.ManyToManyField(Workgroup, related_name="organizations"),
'users': models.ManyToManyField(User, related_name="organizations"),
'groups': models.ManyToManyField(Group, related_name="organizations"),
'__module__': 'api_manager.models'
}
self.organization = type("Organization", (TimeStampedModel,), attrs)
fields = [(f.name, f) for f in self.organization._meta.local_fields]
table_name = self.organization._meta.db_table
db.create_table(table_name, fields)
attrs = {
'organization_id': models.IntegerField(),
'user_id': models.IntegerField(),
'__module__': 'api_manager.models'
}
self.organization_users = type("organization_users", (models.Model,), attrs)
fields = [(f.name, f) for f in self.organization_users._meta.local_fields]
table_name = self.organization_users._meta.db_table
db.create_table(table_name, fields)
attrs = {
'organization_id': models.IntegerField(),
'group_id': models.IntegerField(),
'__module__': 'api_manager.models'
}
self.organization_groups = type("organization_groups", (models.Model,), attrs)
fields = [(f.name, f) for f in self.organization_groups._meta.local_fields]
table_name = self.organization_groups._meta.db_table
db.create_table(table_name, fields)
attrs = {
'organization_id': models.IntegerField(),
'workgroup_id': models.IntegerField(),
'__module__': 'api_manager.models'
}
self.organization_workgroups = type("organization_workgroups", (models.Model,), attrs)
fields = [(f.name, f) for f in self.organization_workgroups._meta.local_fields]
table_name = self.organization_workgroups._meta.db_table
db.create_table(table_name, fields)
for i in xrange(1, 9):
org = self.organization()
org.name = 'test_and_company {}'.format(i)
org.display_name = 'test display name {}'.format(i)
org.contact_name = 'test contact name {}'.format(i)
org.contact_email = 'test{}@test.com'.format(i)
org.save()
org.users.add(user1, user2)
org.groups.add(group1, group2)
org.workgroups.add(workgroup)
def test_organization_entries_copy(self):
"""
Test organization entries copy from api_manager app to organizations app
"""
# Run the command
move_organizations_entries.Command().handle()
total_orgs_old = self.organization.objects.all().count()
total_orgs_new = Organization.objects.all().count()
self.assertEqual(total_orgs_old, total_orgs_new)
total_org_users_old = 0
total_org_users_new = 0
for org in self.organization.objects.all():
total_org_users_old += org.users.all().count()
for org in Organization.objects.all():
total_org_users_new += org.users.all().count()
self.assertEqual(total_org_users_old, total_org_users_new)
total_org_groups_old = 0
total_org_groups_new = 0
for org in self.organization.objects.all():
total_org_groups_old += org.groups.all().count()
for org in Organization.objects.all():
total_org_groups_new += org.groups.all().count()
self.assertEqual(total_org_groups_old, total_org_groups_new)
total_org_workgroups_old = 0
total_org_workgroups_new = 0
for org in self.organization.objects.all():
total_org_workgroups_old += org.workgroups.all().count()
for org in Organization.objects.all():
total_org_workgroups_new += org.workgroups.all().count()
self.assertEqual(total_org_workgroups_old, total_org_workgroups_new)
# -*- 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 'Organization'
db.create_table('organizations_organization', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('created', self.gf('model_utils.fields.AutoCreatedField')(default=datetime.datetime.now)),
('modified', self.gf('model_utils.fields.AutoLastModifiedField')(default=datetime.datetime.now)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('display_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('contact_name', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
('contact_email', self.gf('django.db.models.fields.EmailField')(max_length=255, null=True, blank=True)),
('contact_phone', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True)),
('logo_url', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
))
db.send_create_signal('organizations', ['Organization'])
# Adding M2M table for field workgroups on 'Organization'
db.create_table('organizations_organization_workgroups', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['organizations.organization'], null=False)),
('workgroup', models.ForeignKey(orm['projects.workgroup'], null=False))
))
db.create_unique('organizations_organization_workgroups', ['organization_id', 'workgroup_id'])
# Adding M2M table for field users on 'Organization'
db.create_table('organizations_organization_users', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['organizations.organization'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('organizations_organization_users', ['organization_id', 'user_id'])
# Adding M2M table for field groups on 'Organization'
db.create_table('organizations_organization_groups', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('organization', models.ForeignKey(orm['organizations.organization'], null=False)),
('group', models.ForeignKey(orm['auth.group'], null=False))
))
db.create_unique('organizations_organization_groups', ['organization_id', 'group_id'])
def backwards(self, orm):
# Deleting model 'Organization'
db.delete_table('organizations_organization')
# Removing M2M table for field workgroups on 'Organization'
db.delete_table('organizations_organization_workgroups')
# Removing M2M table for field users on 'Organization'
db.delete_table('organizations_organization_users')
# Removing M2M table for field groups on 'Organization'
db.delete_table('organizations_organization_groups')
models = {
'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'})
},
'organizations.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'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'organizations'", 'symmetrical': 'False', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'logo_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': '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']"})
},
'projects.project': {
'Meta': {'unique_together': "(('course_id', 'content_id', 'organization'),)", '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['organizations.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', [], {'related_name': "'workgroups'", 'to': "orm['auth.User']", 'through': "orm['projects.WorkgroupUser']", 'blank': 'True', 'symmetrical': 'False', 'null': 'True'})
},
'projects.workgroupuser': {
'Meta': {'object_name': 'WorkgroupUser', 'db_table': "'projects_workgroup_users'"},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'workgroup': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['projects.Workgroup']"})
}
}
complete_apps = ['organizations']
\ No newline at end of file
"""
Django database models supporting the organizations app
"""
from django.contrib.auth.models import Group, User
from django.db import models
from model_utils.models import TimeStampedModel
from projects.models import Workgroup
class Organization(TimeStampedModel):
"""
Main table representing the Organization concept. Organizations are
primarily a collection of Users.
"""
name = models.CharField(max_length=255)
display_name = models.CharField(max_length=255, null=True, blank=True)
contact_name = models.CharField(max_length=255, null=True, blank=True)
contact_email = models.EmailField(max_length=255, null=True, blank=True)
contact_phone = models.CharField(max_length=50, null=True, blank=True)
logo_url = models.CharField(max_length=255, blank=True, null=True)
workgroups = models.ManyToManyField(Workgroup, related_name="organizations")
users = models.ManyToManyField(User, related_name="organizations")
groups = models.ManyToManyField(Group, related_name="organizations")
""" Django REST Framework Serializers """ """ Django REST Framework Serializers """
from django.contrib.auth.models import User
from rest_framework import serializers from rest_framework import serializers
from api_manager.models import Organization from organizations.models import Organization
class OrganizationSerializer(serializers.ModelSerializer): class OrganizationSerializer(serializers.ModelSerializer):
...@@ -13,8 +11,8 @@ class OrganizationSerializer(serializers.ModelSerializer): ...@@ -13,8 +11,8 @@ class OrganizationSerializer(serializers.ModelSerializer):
class Meta: class Meta:
""" Serializer/field specification """ """ Serializer/field specification """
model = Organization model = Organization
fields = ('url', 'id', 'name', 'display_name', 'contact_name', 'contact_email', 'contact_phone' fields = ('url', 'id', 'name', 'display_name', 'contact_name', 'contact_email', 'contact_phone',
, 'logo_url', 'workgroups', 'users', 'groups', 'created', 'modified') 'logo_url', 'workgroups', 'users', 'groups', 'created', 'modified')
read_only = ('url', 'id', 'created') read_only = ('url', 'id', 'created')
......
...@@ -267,11 +267,12 @@ class OrganizationsApiTests(ModuleStoreTestCase): ...@@ -267,11 +267,12 @@ class OrganizationsApiTests(ModuleStoreTestCase):
org_id = response.data['id'] org_id = response.data['id']
# create groups # create groups
max_groups, groups = 4, [] max_groups, groups, contactgroup_count = 4, [], 2
for i in xrange(1, max_groups + 1): for i in xrange(1, max_groups + 1):
grouptype = 'contactgroup' if i <= contactgroup_count else 'series'
data = { data = {
'name': '{} {}'.format('Test Group', i), 'name': '{} {}'.format('Test Group', i),
'type': 'contactgroup', 'type': grouptype,
'data': {'display_name': 'organization contacts group'} 'data': {'display_name': 'organization contacts group'}
} }
response = self.do_post(self.base_groups_uri, data) response = self.do_post(self.base_groups_uri, data)
...@@ -288,6 +289,11 @@ class OrganizationsApiTests(ModuleStoreTestCase): ...@@ -288,6 +289,11 @@ class OrganizationsApiTests(ModuleStoreTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), max_groups) self.assertEqual(len(response.data), max_groups)
# get organization groups with type filter
response = self.do_get('{}?type=contactgroup'.format(groups_uri))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), contactgroup_count)
# post an invalid group # post an invalid group
data = {"id": '45533333'} data = {"id": '45533333'}
response = self.do_post(groups_uri, data) response = self.do_post(groups_uri, data)
...@@ -337,7 +343,6 @@ class OrganizationsApiTests(ModuleStoreTestCase): ...@@ -337,7 +343,6 @@ class OrganizationsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data[0]['id'], self.test_user.id) self.assertEqual(response.data[0]['id'], self.test_user.id)
self.assertEqual(response.data[0]['course_count'], 2) self.assertEqual(response.data[0]['course_count'], 2)
def test_organizations_metrics_get(self): def test_organizations_metrics_get(self):
users = [] users = []
for i in xrange(1, 6): for i in xrange(1, 6):
...@@ -380,7 +385,6 @@ class OrganizationsApiTests(ModuleStoreTestCase): ...@@ -380,7 +385,6 @@ class OrganizationsApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['users_grade_average'], 0.838) self.assertEqual(response.data['users_grade_average'], 0.838)
self.assertEqual(response.data['users_grade_complete_count'], 4) self.assertEqual(response.data['users_grade_complete_count'], 4)
def test_organizations_metrics_get_courses_filter(self): def test_organizations_metrics_get_courses_filter(self):
users = [] users = []
for i in xrange(1, 12): for i in xrange(1, 12):
......
...@@ -11,7 +11,7 @@ from rest_framework.decorators import action ...@@ -11,7 +11,7 @@ from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from api_manager.courseware_access import get_course_key from api_manager.courseware_access import get_course_key
from api_manager.models import Organization from organizations.models import Organization
from api_manager.users.serializers import UserSerializer from api_manager.users.serializers import UserSerializer
from api_manager.groups.serializers import GroupSerializer from api_manager.groups.serializers import GroupSerializer
from api_manager.utils import str2bool from api_manager.utils import str2bool
...@@ -28,7 +28,7 @@ class OrganizationsViewSet(viewsets.ModelViewSet): ...@@ -28,7 +28,7 @@ class OrganizationsViewSet(viewsets.ModelViewSet):
serializer_class = OrganizationSerializer serializer_class = OrganizationSerializer
model = Organization model = Organization
@action(methods=['get',]) @action(methods=['get', ])
def metrics(self, request, pk): def metrics(self, request, pk):
""" """
Provide statistical information for the specified Organization Provide statistical information for the specified Organization
...@@ -95,7 +95,10 @@ class OrganizationsViewSet(viewsets.ModelViewSet): ...@@ -95,7 +95,10 @@ class OrganizationsViewSet(viewsets.ModelViewSet):
Add a Group to a organization or retrieve list of groups in organization Add a Group to a organization or retrieve list of groups in organization
""" """
if request.method == 'GET': if request.method == 'GET':
group_type = request.QUERY_PARAMS.get('type', None)
groups = Group.objects.filter(organizations=pk) groups = Group.objects.filter(organizations=pk)
if group_type:
groups = groups.filter(groupprofile__group_type=group_type)
response_data = [] response_data = []
if groups: if groups:
for group in groups: for group in groups:
......
...@@ -15,7 +15,7 @@ class Project(TimeStampedModel): ...@@ -15,7 +15,7 @@ class Project(TimeStampedModel):
course_id = models.CharField(max_length=255) course_id = models.CharField(max_length=255)
content_id = models.CharField(max_length=255) content_id = models.CharField(max_length=255)
organization = models.ForeignKey( organization = models.ForeignKey(
'api_manager.Organization', 'organizations.Organization',
blank=True, blank=True,
null=True, null=True,
related_name="projects", related_name="projects",
......
...@@ -712,6 +712,10 @@ if FEATURES.get('STUDENT_GRADEBOOK') and "'gradebook'" not in INSTALLED_APPS: ...@@ -712,6 +712,10 @@ if FEATURES.get('STUDENT_GRADEBOOK') and "'gradebook'" not in INSTALLED_APPS:
if FEATURES.get('STUDENT_PROGRESS') and "progress" not in INSTALLED_APPS: if FEATURES.get('STUDENT_PROGRESS') and "progress" not in INSTALLED_APPS:
INSTALLED_APPS += ('progress',) INSTALLED_APPS += ('progress',)
############# Organizations app #################
if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS:
INSTALLED_APPS += ('organizations',)
##### SET THE LIST OF ALLOWED IP ADDRESSES FOR THE API ###### ##### SET THE LIST OF ALLOWED IP ADDRESSES FOR THE API ######
API_ALLOWED_IP_ADDRESSES = ENV_TOKENS.get('API_ALLOWED_IP_ADDRESSES') API_ALLOWED_IP_ADDRESSES = ENV_TOKENS.get('API_ALLOWED_IP_ADDRESSES')
......
...@@ -432,6 +432,9 @@ FEATURES = { ...@@ -432,6 +432,9 @@ FEATURES = {
# In order to use the "progress", you must add it to the list of INSTALLED_APPS in # In order to use the "progress", you must add it to the list of INSTALLED_APPS in
# addition to setting the flag to True here. A reference is available in aws.py # addition to setting the flag to True here. A reference is available in aws.py
'STUDENT_PROGRESS': False, 'STUDENT_PROGRESS': False,
# Enable the Organizations,
'ORGANIZATIONS_APP': False,
} }
# Ignore static asset files on import which match this pattern # Ignore static asset files on import which match this pattern
......
...@@ -529,3 +529,7 @@ FEATURES['STUDENT_PROGRESS'] = True ...@@ -529,3 +529,7 @@ FEATURES['STUDENT_PROGRESS'] = True
if FEATURES.get('STUDENT_PROGRESS', False) and "'progress'" not in INSTALLED_APPS: if FEATURES.get('STUDENT_PROGRESS', False) and "'progress'" not in INSTALLED_APPS:
INSTALLED_APPS += ('progress',) INSTALLED_APPS += ('progress',)
############# Organizations app #################
if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS:
INSTALLED_APPS += ('organizations',)
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