Commit 71aacb36 by Matt Drayer Committed by Jonathan Piacenti

mattdrayer/api-migrate_transitional_ids: Cleaned up two additional tables

merged with master

changes after matts suggestions
parent 0da87a9d
""" Django REST Framework Serializers """ """ Django REST Framework Serializers """
from api_manager.models import CourseModuleCompletion
from api_manager.utils import generate_base_uri from api_manager.utils import generate_base_uri
from rest_framework import serializers from rest_framework import serializers
class CourseModuleCompletionSerializer(serializers.ModelSerializer):
""" Serializer for CourseModuleCompletion model interactions """
user_id = serializers.Field(source='user_id')
class Meta:
""" Serializer/field specification """
model = CourseModuleCompletion
fields = ('id', 'user_id', 'course_id', 'content_id', 'stage', 'created', 'modified')
read_only = ('id', 'created')
class GradeSerializer(serializers.Serializer): class GradeSerializer(serializers.Serializer):
""" Serializer for model interactions """ """ Serializer for model interactions """
grade = serializers.Field() grade = serializers.Field()
......
...@@ -39,13 +39,14 @@ from xmodule.modulestore.django import modulestore ...@@ -39,13 +39,14 @@ from xmodule.modulestore.django import modulestore
from api_manager.courseware_access import get_course, get_course_child, get_course_leaf_nodes, get_course_key, \ from api_manager.courseware_access import get_course, get_course_child, get_course_leaf_nodes, get_course_key, \
course_exists, get_modulestore, get_course_descriptor, get_aggregate_exclusion_user_ids course_exists, get_modulestore, get_course_descriptor, get_aggregate_exclusion_user_ids
from api_manager.models import CourseGroupRelationship, CourseContentGroupRelationship, GroupProfile, \ from api_manager.models import CourseGroupRelationship, CourseContentGroupRelationship, GroupProfile
CourseModuleCompletion from progress.models import CourseModuleCompletion
from api_manager.permissions import SecureAPIView, SecureListAPIView from api_manager.permissions import SecureAPIView, SecureListAPIView
from api_manager.users.serializers import UserSerializer, UserCountByCitySerializer from api_manager.users.serializers import UserSerializer, UserCountByCitySerializer
from api_manager.utils import generate_base_uri, str2bool, get_time_series_data, parse_datetime from api_manager.utils import generate_base_uri, str2bool, get_time_series_data, parse_datetime
from .serializers import CourseModuleCompletionSerializer, CourseSerializer from .serializers import CourseSerializer
from .serializers import GradeSerializer, CourseLeadersSerializer, CourseCompletionsLeadersSerializer from .serializers import GradeSerializer, CourseLeadersSerializer, CourseCompletionsLeadersSerializer
from progress.serializers import CourseModuleCompletionSerializer
......
...@@ -8,6 +8,7 @@ from django.core.management.base import BaseCommand ...@@ -8,6 +8,7 @@ from django.core.management.base import BaseCommand
from django.test import RequestFactory from django.test import RequestFactory
from api_manager import models as api_models from api_manager import models as api_models
from progress.models import CourseModuleCompletion
from api_manager.courseware_access import get_course, get_course_child from api_manager.courseware_access import get_course, get_course_child
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
...@@ -46,7 +47,7 @@ class Command(BaseCommand): ...@@ -46,7 +47,7 @@ class Command(BaseCommand):
pass # If the key conversion fails it was either a new-style key or junk data pass # If the key conversion fails it was either a new-style key or junk data
ccg.save() ccg.save()
course_module_completions = api_models.CourseModuleCompletion.objects.all() course_module_completions = CourseModuleCompletion.objects.all()
for cmc in course_module_completions: for cmc in course_module_completions:
course_id = cmc.course_id course_id = cmc.course_id
course_descriptor, course_key, course_content = get_course(request, request.user, course_id) course_descriptor, course_key, course_content = get_course(request, request.user, course_id)
......
...@@ -4,7 +4,7 @@ One-time data migration script -- shoulen't need to run it again ...@@ -4,7 +4,7 @@ One-time data migration script -- shoulen't need to run it again
import logging import logging
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from progress.models import CourseModuleCompletion
from api_manager import models as api_models from api_manager import models as api_models
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -53,7 +53,7 @@ class Command(BaseCommand): ...@@ -53,7 +53,7 @@ class Command(BaseCommand):
log.warning('Complete!') log.warning('Complete!')
log.warning('Migrating Course Module Completions...') log.warning('Migrating Course Module Completions...')
course_module_completions = api_models.CourseModuleCompletion.objects.all() course_module_completions = CourseModuleCompletion.objects.all()
for cmc in course_module_completions: for cmc in course_module_completions:
cmc.course_id = _migrate_course_id(cmc.course_id) cmc.course_id = _migrate_course_id(cmc.course_id)
cmc.content_id = _migrate_content_id(cmc.content_id) cmc.content_id = _migrate_content_id(cmc.content_id)
......
...@@ -5,7 +5,7 @@ import logging ...@@ -5,7 +5,7 @@ import logging
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from api_manager import models as api_models from progress.models import CourseModuleCompletion
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -17,7 +17,7 @@ class Command(BaseCommand): ...@@ -17,7 +17,7 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
log.warning('Migrating Course Module Completions Stage Field...') log.warning('Migrating Course Module Completions Stage Field...')
course_module_completions = api_models.CourseModuleCompletion.objects.all() course_module_completions = CourseModuleCompletion.objects.all()
for cmc in course_module_completions: for cmc in course_module_completions:
if cmc.stage is not None: if cmc.stage is not None:
cmc.stage = cmc.stage.replace("i4x://", "") cmc.stage = cmc.stage.replace("i4x://", "")
......
...@@ -8,6 +8,7 @@ import uuid ...@@ -8,6 +8,7 @@ import uuid
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from api_manager import models as api_models from api_manager import models as api_models
from progress.models import CourseModuleCompletion
from api_manager.management.commands import migrate_courseids from api_manager.management.commands import migrate_courseids
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
...@@ -66,14 +67,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase): ...@@ -66,14 +67,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
group_profile = api_models.GroupProfile.objects.create(group=group) group_profile = api_models.GroupProfile.objects.create(group=group)
course_group = api_models.CourseGroupRelationship.objects.create(course_id=self.old_style_course_id, group=group) course_group = api_models.CourseGroupRelationship.objects.create(course_id=self.old_style_course_id, group=group)
course_content_group = api_models.CourseContentGroupRelationship.objects.create(course_id=self.old_style_course_id, content_id=self.old_style_content_id, group_profile=group_profile) course_content_group = api_models.CourseContentGroupRelationship.objects.create(course_id=self.old_style_course_id, content_id=self.old_style_content_id, group_profile=group_profile)
course_module_completion = api_models.CourseModuleCompletion.objects.create(user=user, course_id=self.old_style_course_id, content_id=self.old_style_content_id) course_module_completion = CourseModuleCompletion.objects.create(user=user, course_id=self.old_style_course_id, content_id=self.old_style_content_id)
user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True) user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True)
group2 = Group.objects.create(name='Test Group2') group2 = Group.objects.create(name='Test Group2')
group_profile2 = api_models.GroupProfile.objects.create(group=group2) group_profile2 = api_models.GroupProfile.objects.create(group=group2)
course_group2 = api_models.CourseGroupRelationship.objects.create(course_id=self.new_style_course_id2, group=group2) course_group2 = api_models.CourseGroupRelationship.objects.create(course_id=self.new_style_course_id2, group=group2)
course_content_group2 = api_models.CourseContentGroupRelationship.objects.create(course_id=self.new_style_course_id2, content_id=self.new_style_content_id2, group_profile=group_profile2) course_content_group2 = api_models.CourseContentGroupRelationship.objects.create(course_id=self.new_style_course_id2, content_id=self.new_style_content_id2, group_profile=group_profile2)
course_module_completion2 = api_models.CourseModuleCompletion.objects.create(user=user2, course_id=self.new_style_course_id2, content_id=self.new_style_content_id2) course_module_completion2 = CourseModuleCompletion.objects.create(user=user2, course_id=self.new_style_course_id2, content_id=self.new_style_content_id2)
# Run the data migration # Run the data migration
...@@ -95,10 +96,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase): ...@@ -95,10 +96,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
self.assertEqual(updated_course_content_group.content_id, self.new_style_content_id2) self.assertEqual(updated_course_content_group.content_id, self.new_style_content_id2)
print "Course Content Group Data Migration Passed" print "Course Content Group Data Migration Passed"
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion.id)
self.assertEqual(updated_course_module_completion.course_id, self.new_style_course_id) self.assertEqual(updated_course_module_completion.course_id, self.new_style_course_id)
self.assertEqual(updated_course_module_completion.content_id, self.new_style_content_id) self.assertEqual(updated_course_module_completion.content_id, self.new_style_content_id)
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion2.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion2.id)
self.assertEqual(updated_course_module_completion.course_id, self.new_style_course_id2) self.assertEqual(updated_course_module_completion.course_id, self.new_style_course_id2)
self.assertEqual(updated_course_module_completion.content_id, self.new_style_content_id2) self.assertEqual(updated_course_module_completion.content_id, self.new_style_content_id2)
print "Course Module Completion Data Migration Passed" print "Course Module Completion Data Migration Passed"
...@@ -8,6 +8,7 @@ import uuid ...@@ -8,6 +8,7 @@ import uuid
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from api_manager import models as api_models from api_manager import models as api_models
from progress.models import CourseModuleCompletion
from api_manager.management.commands import migrate_courseids_v2 from api_manager.management.commands import migrate_courseids_v2
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
...@@ -41,14 +42,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase): ...@@ -41,14 +42,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
group_profile = api_models.GroupProfile.objects.create(group=group) group_profile = api_models.GroupProfile.objects.create(group=group)
course_group = api_models.CourseGroupRelationship.objects.create(course_id=self.bad_style_course_id, group=group) course_group = api_models.CourseGroupRelationship.objects.create(course_id=self.bad_style_course_id, group=group)
course_content_group = api_models.CourseContentGroupRelationship.objects.create(course_id=self.bad_style_course_id, content_id=self.bad_style_content_id, group_profile=group_profile) course_content_group = api_models.CourseContentGroupRelationship.objects.create(course_id=self.bad_style_course_id, content_id=self.bad_style_content_id, group_profile=group_profile)
course_module_completion = api_models.CourseModuleCompletion.objects.create(user=user, course_id=self.bad_style_course_id, content_id=self.bad_style_content_id) course_module_completion = CourseModuleCompletion.objects.create(user=user, course_id=self.bad_style_course_id, content_id=self.bad_style_content_id)
user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True) user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True)
group2 = Group.objects.create(name='Test Group2') group2 = Group.objects.create(name='Test Group2')
group_profile2 = api_models.GroupProfile.objects.create(group=group2) group_profile2 = api_models.GroupProfile.objects.create(group=group2)
course_group2 = api_models.CourseGroupRelationship.objects.create(course_id=self.bad_style_course_id2, group=group2) course_group2 = api_models.CourseGroupRelationship.objects.create(course_id=self.bad_style_course_id2, group=group2)
course_content_group2 = api_models.CourseContentGroupRelationship.objects.create(course_id=self.bad_style_course_id2, content_id=self.bad_style_content_id2, group_profile=group_profile2) course_content_group2 = api_models.CourseContentGroupRelationship.objects.create(course_id=self.bad_style_course_id2, content_id=self.bad_style_content_id2, group_profile=group_profile2)
course_module_completion2 = api_models.CourseModuleCompletion.objects.create(user=user2, course_id=self.bad_style_course_id2, content_id=self.bad_style_content_id2, stage=self.bad_style_content_id2) course_module_completion2 = CourseModuleCompletion.objects.create(user=user2, course_id=self.bad_style_course_id2, content_id=self.bad_style_content_id2, stage=self.bad_style_content_id2)
# Run the data migration # Run the data migration
...@@ -70,10 +71,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase): ...@@ -70,10 +71,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
self.assertEqual(updated_course_content_group.content_id, self.good_style_content_id2) self.assertEqual(updated_course_content_group.content_id, self.good_style_content_id2)
print "Course Content Group Data Migration Passed" print "Course Content Group Data Migration Passed"
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion.id)
self.assertEqual(updated_course_module_completion.course_id, self.good_style_course_id) self.assertEqual(updated_course_module_completion.course_id, self.good_style_course_id)
self.assertEqual(updated_course_module_completion.content_id, self.good_style_content_id) self.assertEqual(updated_course_module_completion.content_id, self.good_style_content_id)
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion2.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion2.id)
self.assertEqual(updated_course_module_completion.course_id, self.good_style_course_id2) self.assertEqual(updated_course_module_completion.course_id, self.good_style_course_id2)
self.assertEqual(updated_course_module_completion.content_id, self.good_style_content_id2) self.assertEqual(updated_course_module_completion.content_id, self.good_style_content_id2)
self.assertEqual(updated_course_module_completion.stage, self.good_style_content_id2) self.assertEqual(updated_course_module_completion.stage, self.good_style_content_id2)
......
...@@ -5,7 +5,7 @@ Run these tests @ Devstack: ...@@ -5,7 +5,7 @@ Run these tests @ Devstack:
from django.contrib.auth.models import User from django.contrib.auth.models import User
from api_manager import models as api_models from progress.models import CourseModuleCompletion
from api_manager.management.commands import migrate_stage_prefix from api_manager.management.commands import migrate_stage_prefix
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
...@@ -32,19 +32,19 @@ class MigrateCourseIdsTests(ModuleStoreTestCase): ...@@ -32,19 +32,19 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
""" """
# Set up the data to be migrated # Set up the data to be migrated
user = User.objects.create(email='testuser@edx.org', username='testuser', password='testpassword', is_active=True) user = User.objects.create(email='testuser@edx.org', username='testuser', password='testpassword', is_active=True)
course_module_completion = api_models.CourseModuleCompletion.objects.create(user=user, course_id=self.good_style_course_id, content_id=self.good_style_content_id, stage=self.bad_style_stage) course_module_completion = CourseModuleCompletion.objects.create(user=user, course_id=self.good_style_course_id, content_id=self.good_style_content_id, stage=self.bad_style_stage)
user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True) user2 = User.objects.create(email='testuser2@edx.org', username='testuser2', password='testpassword2', is_active=True)
course_module_completion2 = api_models.CourseModuleCompletion.objects.create(user=user2, course_id=self.good_style_course_id2, content_id=self.good_style_content_id2, stage=self.bad_style_stage2) course_module_completion2 = CourseModuleCompletion.objects.create(user=user2, course_id=self.good_style_course_id2, content_id=self.good_style_content_id2, stage=self.bad_style_stage2)
# Run the data migration # Run the data migration
migrate_stage_prefix.Command().handle() migrate_stage_prefix.Command().handle()
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion.id)
self.assertEqual(updated_course_module_completion.stage, self.good_style_stage) self.assertEqual(updated_course_module_completion.stage, self.good_style_stage)
updated_course_module_completion = api_models.CourseModuleCompletion.objects.get(id=course_module_completion2.id) updated_course_module_completion = CourseModuleCompletion.objects.get(id=course_module_completion2.id)
self.assertEqual(updated_course_module_completion.stage, self.good_style_stage2) self.assertEqual(updated_course_module_completion.stage, self.good_style_stage2)
print "Course Module Completion Data Migration Passed" print "Course Module Completion Data Migration Passed"
...@@ -133,28 +133,6 @@ class CourseContentGroupRelationship(TimeStampedModel): ...@@ -133,28 +133,6 @@ class CourseContentGroupRelationship(TimeStampedModel):
unique_together = ("course_id", "content_id", "group_profile") unique_together = ("course_id", "content_id", "group_profile")
class CourseModuleCompletion(TimeStampedModel):
"""
The CourseModuleCompletion model contains user, course, module information
to monitor a user's progression throughout the duration of a course,
we need to observe and record completions of the individual course modules.
"""
user = models.ForeignKey(User, db_index=True, related_name="course_completions")
course_id = models.CharField(max_length=255, db_index=True)
content_id = models.CharField(max_length=255, db_index=True)
stage = models.CharField(max_length=255, null=True, blank=True)
@classmethod
def get_actual_completions(cls):
"""
This would skip those modules with ignorable categories
"""
detached_categories = getattr(settings, 'PROGRESS_DETACHED_CATEGORIES', [])
cat_list = [Q(content_id__contains=item.strip()) for item in detached_categories]
cat_list = reduce(lambda a, b: a | b, cat_list)
return cls.objects.all().exclude(cat_list)
class APIUserQuerySet(models.query.QuerySet): # pylint: disable=R0924 class APIUserQuerySet(models.query.QuerySet): # pylint: disable=R0924
""" Custom QuerySet to modify id based lookup """ """ Custom QuerySet to modify id based lookup """
def filter(self, *args, **kwargs): def filter(self, *args, **kwargs):
......
...@@ -46,7 +46,7 @@ from util.password_policy_validators import ( ...@@ -46,7 +46,7 @@ from util.password_policy_validators import (
from xmodule.modulestore import InvalidLocationError from xmodule.modulestore import InvalidLocationError
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from api_manager.courses.serializers import CourseModuleCompletionSerializer from progress.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
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Module rendering Module rendering
""" """
from __future__ import absolute_import
import hashlib import hashlib
import json import json
import logging import logging
...@@ -70,7 +71,7 @@ from xmodule.lti_module import LTIModule ...@@ -70,7 +71,7 @@ from xmodule.lti_module import LTIModule
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
from xmodule.mixin import wrap_with_license from xmodule.mixin import wrap_with_license
from api_manager.models import CourseModuleCompletion from progress.models import CourseModuleCompletion
from util.json_request import JsonResponse from util.json_request import JsonResponse
from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip
......
...@@ -8,8 +8,7 @@ from django.core.management.base import BaseCommand ...@@ -8,8 +8,7 @@ from django.core.management.base import BaseCommand
from django.conf import settings from django.conf import settings
from django.db.models import Q from django.db.models import Q
from progress.models import StudentProgress from progress.models import StudentProgress, CourseModuleCompletion
from api_manager.models import CourseModuleCompletion
from student.models import CourseEnrollment from student.models import CourseEnrollment
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
......
...@@ -10,7 +10,7 @@ from django.db.models.signals import post_save ...@@ -10,7 +10,7 @@ from django.db.models.signals import post_save
from capa.tests.response_xml_factory import StringResponseXMLFactory from capa.tests.response_xml_factory import StringResponseXMLFactory
from progress.management.commands import generate_progress_entries from progress.management.commands import generate_progress_entries
from progress.models import StudentProgress, StudentProgressHistory from progress.models import StudentProgress, StudentProgressHistory, CourseModuleCompletion
from progress.signals import handle_cmc_post_save_signal from progress.signals import handle_cmc_post_save_signal
from student.tests.factories import UserFactory, CourseEnrollmentFactory from student.tests.factories import UserFactory, CourseEnrollmentFactory
from api_manager.models import CourseModuleCompletion from api_manager.models import CourseModuleCompletion
......
# -*- 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 'CourseModuleCompletion'
db.create_table('progress_coursemodulecompletion', (
('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)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='course_completions', to=orm['auth.User'])),
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('content_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('stage', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
))
db.send_create_signal('progress', ['CourseModuleCompletion'])
def backwards(self, orm):
# Deleting model 'CourseModuleCompletion'
db.delete_table('progress_coursemodulecompletion')
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'})
},
'progress.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']"})
},
'progress.studentprogress': {
'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'StudentProgress'},
'completions': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': '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'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'progress.studentprogresshistory': {
'Meta': {'object_name': 'StudentProgressHistory'},
'completions': ('django.db.models.fields.IntegerField', [], {}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': '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'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['progress']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
import logging
from south.db import db
from south.v2 import DataMigration
from django.db import models
from django.db import connection
log = logging.getLogger(__name__)
class Migration(DataMigration):
def print_message(self, msg):
print msg
log.info(msg)
def forwards(self, orm):
"Write your forwards methods here."
existing_entries = orm.CourseModuleCompletion.objects.all().count()
if existing_entries == 0:
try:
cursor = connection.cursor()
cursor.execute('INSERT INTO progress_coursemodulecompletion SELECT * from api_manager_coursemodulecompletion')
db.delete_table('api_manager_coursemodulecompletion')
log_msg = 'coursemodulecompletion entries moved from api_manager to progress app'
self.print_message(log_msg)
except Exception as e:
log_msg = e.message
self.print_message(log_msg)
else:
log_msg = 'progress_coursemodulecompletion is not empty. You might have already filled it.'
self.print_message(log_msg)
def backwards(self, orm):
"Write your backwards methods here."
pass
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'})
},
'progress.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']"})
},
'progress.studentprogress': {
'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'StudentProgress'},
'completions': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': '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'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'progress.studentprogresshistory': {
'Meta': {'object_name': 'StudentProgressHistory'},
'completions': ('django.db.models.fields.IntegerField', [], {}),
'course_id': ('xmodule_django.models.CourseKeyField', [], {'db_index': 'True', 'max_length': '255', 'blank': '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'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
complete_apps = ['progress']
symmetrical = True
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Django database models supporting the progress app Django database models supporting the progress app
""" """
from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.db.models import Sum, Q from django.db.models import Sum, Q
...@@ -114,3 +115,25 @@ class StudentProgressHistory(TimeStampedModel): ...@@ -114,3 +115,25 @@ class StudentProgressHistory(TimeStampedModel):
user = models.ForeignKey(User, db_index=True) user = models.ForeignKey(User, db_index=True)
course_id = CourseKeyField(db_index=True, max_length=255, blank=True) course_id = CourseKeyField(db_index=True, max_length=255, blank=True)
completions = models.IntegerField() completions = models.IntegerField()
class CourseModuleCompletion(TimeStampedModel):
"""
The CourseModuleCompletion model contains user, course, module information
to monitor a user's progression throughout the duration of a course,
we need to observe and record completions of the individual course modules.
"""
user = models.ForeignKey(User, db_index=True, related_name="course_completions")
course_id = models.CharField(max_length=255, db_index=True)
content_id = models.CharField(max_length=255, db_index=True)
stage = models.CharField(max_length=255, null=True, blank=True)
@classmethod
def get_actual_completions(cls):
"""
This would skip those modules with ignorable categories
"""
detached_categories = getattr(settings, 'PROGRESS_DETACHED_CATEGORIES', [])
cat_list = [Q(content_id__contains=item.strip()) for item in detached_categories]
cat_list = reduce(lambda a, b: a | b, cat_list)
return cls.objects.all().exclude(cat_list)
""" Django REST Framework Serializers """
from progress.models import CourseModuleCompletion
from rest_framework import serializers
class CourseModuleCompletionSerializer(serializers.ModelSerializer):
""" Serializer for CourseModuleCompletion model interactions """
user_id = serializers.Field(source='user_id')
class Meta:
""" Serializer/field specification """
model = CourseModuleCompletion
fields = ('id', 'user_id', 'course_id', 'content_id', 'stage', 'created', 'modified')
read_only = ('id', 'created')
...@@ -8,8 +8,7 @@ from django.db.models.signals import post_save ...@@ -8,8 +8,7 @@ from django.db.models.signals import post_save
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings from django.conf import settings
from progress.models import StudentProgress, StudentProgressHistory from progress.models import StudentProgress, StudentProgressHistory, CourseModuleCompletion
from api_manager.models import CourseModuleCompletion
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
......
...@@ -715,7 +715,11 @@ if FEATURES.get('STUDENT_GRADEBOOK') and "'gradebook'" not in INSTALLED_APPS: ...@@ -715,7 +715,11 @@ 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 ################# ############# Projects App #################
if FEATURES.get('PROJECTS_APP') and "projects" not in INSTALLED_APPS:
INSTALLED_APPS += ('projects',)
############# Organizations App #################
if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS: if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS:
INSTALLED_APPS += ('organizations',) INSTALLED_APPS += ('organizations',)
......
...@@ -433,8 +433,11 @@ FEATURES = { ...@@ -433,8 +433,11 @@ FEATURES = {
# 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 projects,
'PROJECTS_APP': False,
# Enable the Organizations, # Enable the Organizations,
'ORGANIZATIONS_APP': False, 'ORGANIZATIONS_APP': False
} }
# Ignore static asset files on import which match this pattern # Ignore static asset files on import which match this pattern
...@@ -1946,7 +1949,6 @@ INSTALLED_APPS = ( ...@@ -1946,7 +1949,6 @@ INSTALLED_APPS = (
# EDX API application # EDX API application
'api_manager', 'api_manager',
'projects',
) )
######################### CSRF ######################################### ######################### CSRF #########################################
......
...@@ -529,7 +529,12 @@ FEATURES['STUDENT_PROGRESS'] = True ...@@ -529,7 +529,12 @@ 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 ################# ############# Projects App #################
FEATURES['PROJECTS_APP'] = True
if FEATURES.get('PROJECTS_APP') and "projects" not in INSTALLED_APPS:
INSTALLED_APPS += ('projects',)
############# Organizations App #################
FEATURES['ORGANIZATIONS_APP'] = True FEATURES['ORGANIZATIONS_APP'] = True
if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS: if FEATURES.get('ORGANIZATIONS_APP') and "organizations" not in INSTALLED_APPS:
INSTALLED_APPS += ('organizations',) 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