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 """
from api_manager.models import CourseModuleCompletion
from api_manager.utils import generate_base_uri
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):
""" Serializer for model interactions """
grade = serializers.Field()
......
......@@ -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, \
course_exists, get_modulestore, get_course_descriptor, get_aggregate_exclusion_user_ids
from api_manager.models import CourseGroupRelationship, CourseContentGroupRelationship, GroupProfile, \
CourseModuleCompletion
from api_manager.models import CourseGroupRelationship, CourseContentGroupRelationship, GroupProfile
from progress.models import CourseModuleCompletion
from api_manager.permissions import SecureAPIView, SecureListAPIView
from api_manager.users.serializers import UserSerializer, UserCountByCitySerializer
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 progress.serializers import CourseModuleCompletionSerializer
......
......@@ -8,6 +8,7 @@ from django.core.management.base import BaseCommand
from django.test import RequestFactory
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 opaque_keys import InvalidKeyError
......@@ -46,7 +47,7 @@ class Command(BaseCommand):
pass # If the key conversion fails it was either a new-style key or junk data
ccg.save()
course_module_completions = api_models.CourseModuleCompletion.objects.all()
course_module_completions = CourseModuleCompletion.objects.all()
for cmc in course_module_completions:
course_id = cmc.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
import logging
from django.core.management.base import BaseCommand
from progress.models import CourseModuleCompletion
from api_manager import models as api_models
log = logging.getLogger(__name__)
......@@ -53,7 +53,7 @@ class Command(BaseCommand):
log.warning('Complete!')
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:
cmc.course_id = _migrate_course_id(cmc.course_id)
cmc.content_id = _migrate_content_id(cmc.content_id)
......
......@@ -5,7 +5,7 @@ import logging
from django.core.management.base import BaseCommand
from api_manager import models as api_models
from progress.models import CourseModuleCompletion
log = logging.getLogger(__name__)
......@@ -17,7 +17,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
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:
if cmc.stage is not None:
cmc.stage = cmc.stage.replace("i4x://", "")
......
......@@ -8,6 +8,7 @@ import uuid
from django.contrib.auth.models import Group, User
from api_manager import models as api_models
from progress.models import CourseModuleCompletion
from api_manager.management.commands import migrate_courseids
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
......@@ -66,14 +67,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
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_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)
group2 = Group.objects.create(name='Test 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_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
......@@ -95,10 +96,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
self.assertEqual(updated_course_content_group.content_id, self.new_style_content_id2)
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.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.content_id, self.new_style_content_id2)
print "Course Module Completion Data Migration Passed"
......@@ -8,6 +8,7 @@ import uuid
from django.contrib.auth.models import Group, User
from api_manager import models as api_models
from progress.models import CourseModuleCompletion
from api_manager.management.commands import migrate_courseids_v2
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
......@@ -41,14 +42,14 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
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_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)
group2 = Group.objects.create(name='Test 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_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
......@@ -70,10 +71,10 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
self.assertEqual(updated_course_content_group.content_id, self.good_style_content_id2)
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.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.content_id, 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:
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 xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
......@@ -32,19 +32,19 @@ class MigrateCourseIdsTests(ModuleStoreTestCase):
"""
# Set up the data to be migrated
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)
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
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)
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)
print "Course Module Completion Data Migration Passed"
......@@ -133,28 +133,6 @@ class CourseContentGroupRelationship(TimeStampedModel):
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
""" Custom QuerySet to modify id based lookup """
def filter(self, *args, **kwargs):
......
......@@ -46,7 +46,7 @@ from util.password_policy_validators import (
from xmodule.modulestore import InvalidLocationError
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.permissions import SecureAPIView, SecureListAPIView, IdsInFilterBackend, HasOrgsFilterBackend
from api_manager.models import GroupProfile, APIUser as User
......
......@@ -2,6 +2,7 @@
Module rendering
"""
from __future__ import absolute_import
import hashlib
import json
import logging
......@@ -70,7 +71,7 @@ from xmodule.lti_module import LTIModule
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.x_module import XModuleDescriptor
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.sandboxing import can_execute_unsafe_code, get_python_lib_zip
......
......@@ -8,8 +8,7 @@ from django.core.management.base import BaseCommand
from django.conf import settings
from django.db.models import Q
from progress.models import StudentProgress
from api_manager.models import CourseModuleCompletion
from progress.models import StudentProgress, CourseModuleCompletion
from student.models import CourseEnrollment
from xmodule.modulestore.django import modulestore
......
......@@ -10,7 +10,7 @@ from django.db.models.signals import post_save
from capa.tests.response_xml_factory import StringResponseXMLFactory
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 student.tests.factories import UserFactory, CourseEnrollmentFactory
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 @@
Django database models supporting the progress app
"""
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Sum, Q
......@@ -114,3 +115,25 @@ class StudentProgressHistory(TimeStampedModel):
user = models.ForeignKey(User, db_index=True)
course_id = CourseKeyField(db_index=True, max_length=255, blank=True)
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
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from progress.models import StudentProgress, StudentProgressHistory
from api_manager.models import CourseModuleCompletion
from progress.models import StudentProgress, StudentProgressHistory, CourseModuleCompletion
log = logging.getLogger(__name__)
......
......@@ -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:
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:
INSTALLED_APPS += ('organizations',)
......
......@@ -433,8 +433,11 @@ FEATURES = {
# addition to setting the flag to True here. A reference is available in aws.py
'STUDENT_PROGRESS': False,
# Enable the projects,
'PROJECTS_APP': False,
# Enable the Organizations,
'ORGANIZATIONS_APP': False,
'ORGANIZATIONS_APP': False
}
# Ignore static asset files on import which match this pattern
......@@ -1946,7 +1949,6 @@ INSTALLED_APPS = (
# EDX API application
'api_manager',
'projects',
)
######################### CSRF #########################################
......
......@@ -529,7 +529,12 @@ FEATURES['STUDENT_PROGRESS'] = True
if FEATURES.get('STUDENT_PROGRESS', False) and "'progress'" not in INSTALLED_APPS:
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
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