From 2599445190bef091b38ce3ed129528675c5ca6c5 Mon Sep 17 00:00:00 2001 From: Awais Date: Thu, 14 May 2015 17:10:41 +0500 Subject: [PATCH] ECOM-1590 adding models for credit eligibility. --- cms/envs/common.py | 3 + lms/envs/common.py | 3 + openedx/core/djangoapps/credit/__init__.py | 0 openedx/core/djangoapps/credit/admin.py | 8 + .../credit/migrations/0001_initial.py | 140 ++++++++++++++++++ .../djangoapps/credit/migrations/__init__.py | 0 openedx/core/djangoapps/credit/models.py | 79 ++++++++++ requirements/edx/base.txt | 6 +- 8 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 openedx/core/djangoapps/credit/__init__.py create mode 100644 openedx/core/djangoapps/credit/admin.py create mode 100644 openedx/core/djangoapps/credit/migrations/0001_initial.py create mode 100644 openedx/core/djangoapps/credit/migrations/__init__.py create mode 100644 openedx/core/djangoapps/credit/models.py diff --git a/cms/envs/common.py b/cms/envs/common.py index 9fa6ed5790..63497dc6fd 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -729,6 +729,9 @@ INSTALLED_APPS = ( 'edx_jsme', # Molecular Structure 'openedx.core.djangoapps.content.course_structures', + + # Credit courses + 'openedx.core.djangoapps.credit', ) diff --git a/lms/envs/common.py b/lms/envs/common.py index 1586bd5519..7f4ea4d1a8 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1762,6 +1762,9 @@ INSTALLED_APPS = ( 'cors_csrf', 'commerce', + + # Credit courses + 'openedx.core.djangoapps.credit', ) ######################### CSRF ######################################### diff --git a/openedx/core/djangoapps/credit/__init__.py b/openedx/core/djangoapps/credit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/credit/admin.py b/openedx/core/djangoapps/credit/admin.py new file mode 100644 index 0000000000..0fe66771f5 --- /dev/null +++ b/openedx/core/djangoapps/credit/admin.py @@ -0,0 +1,8 @@ +""" +Django admin page for credit eligibility +""" +from ratelimitbackend import admin +from .models import CreditCourse + + +admin.site.register(CreditCourse) diff --git a/openedx/core/djangoapps/credit/migrations/0001_initial.py b/openedx/core/djangoapps/credit/migrations/0001_initial.py new file mode 100644 index 0000000000..ce708422a8 --- /dev/null +++ b/openedx/core/djangoapps/credit/migrations/0001_initial.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as 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 'CreditCourse' + db.create_table('credit_creditcourse', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('course_key', self.gf('xmodule_django.models.CourseKeyField')(unique=True, max_length=255, db_index=True)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal('credit', ['CreditCourse']) + + # Adding model 'CreditProvider' + db.create_table('credit_creditprovider', ( + ('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)), + ('provider_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)), + ('display_name', self.gf('django.db.models.fields.CharField')(max_length=255)), + )) + db.send_create_signal('credit', ['CreditProvider']) + + # Adding model 'CreditRequirement' + db.create_table('credit_creditrequirement', ( + ('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)), + ('course', self.gf('django.db.models.fields.related.ForeignKey')(related_name='credit_requirements', to=orm['credit.CreditCourse'])), + ('namespace', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('configuration', self.gf('jsonfield.fields.JSONField')()), + ('active', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('credit', ['CreditRequirement']) + + # Adding unique constraint on 'CreditRequirement', fields ['namespace', 'name', 'course'] + db.create_unique('credit_creditrequirement', ['namespace', 'name', 'course_id']) + + # Adding model 'CreditRequirementStatus' + db.create_table('credit_creditrequirementstatus', ( + ('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)), + ('username', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)), + ('requirement', self.gf('django.db.models.fields.related.ForeignKey')(related_name='statuses', to=orm['credit.CreditRequirement'])), + ('status', self.gf('django.db.models.fields.CharField')(max_length=32)), + )) + db.send_create_signal('credit', ['CreditRequirementStatus']) + + # Adding model 'CreditEligibility' + db.create_table('credit_crediteligibility', ( + ('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)), + ('username', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)), + ('course', self.gf('django.db.models.fields.related.ForeignKey')(related_name='eligibilities', to=orm['credit.CreditCourse'])), + ('provider', self.gf('django.db.models.fields.related.ForeignKey')(related_name='eligibilities', to=orm['credit.CreditProvider'])), + )) + db.send_create_signal('credit', ['CreditEligibility']) + + # Adding unique constraint on 'CreditEligibility', fields ['username', 'course'] + db.create_unique('credit_crediteligibility', ['username', 'course_id']) + + + def backwards(self, orm): + # Removing unique constraint on 'CreditEligibility', fields ['username', 'course'] + db.delete_unique('credit_crediteligibility', ['username', 'course_id']) + + # Removing unique constraint on 'CreditRequirement', fields ['namespace', 'name', 'course'] + db.delete_unique('credit_creditrequirement', ['namespace', 'name', 'course_id']) + + # Deleting model 'CreditCourse' + db.delete_table('credit_creditcourse') + + # Deleting model 'CreditProvider' + db.delete_table('credit_creditprovider') + + # Deleting model 'CreditRequirement' + db.delete_table('credit_creditrequirement') + + # Deleting model 'CreditRequirementStatus' + db.delete_table('credit_creditrequirementstatus') + + # Deleting model 'CreditEligibility' + db.delete_table('credit_crediteligibility') + + + models = { + 'credit.creditcourse': { + 'Meta': {'object_name': 'CreditCourse'}, + 'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'credit.crediteligibility': { + 'Meta': {'unique_together': "(('username', 'course'),)", 'object_name': 'CreditEligibility'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'eligibilities'", 'to': "orm['credit.CreditCourse']"}), + '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'}), + 'provider': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'eligibilities'", 'to': "orm['credit.CreditProvider']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + 'credit.creditprovider': { + 'Meta': {'object_name': 'CreditProvider'}, + 'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}), + 'provider_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}) + }, + 'credit.creditrequirement': { + 'Meta': {'unique_together': "(('namespace', 'name', 'course'),)", 'object_name': 'CreditRequirement'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'configuration': ('jsonfield.fields.JSONField', [], {}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credit_requirements'", 'to': "orm['credit.CreditCourse']"}), + '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'credit.creditrequirementstatus': { + 'Meta': {'object_name': 'CreditRequirementStatus'}, + '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'}), + 'requirement': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'statuses'", 'to': "orm['credit.CreditRequirement']"}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + } + } + + complete_apps = ['credit'] diff --git a/openedx/core/djangoapps/credit/migrations/__init__.py b/openedx/core/djangoapps/credit/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/credit/models.py b/openedx/core/djangoapps/credit/models.py new file mode 100644 index 0000000000..8da5ec03f8 --- /dev/null +++ b/openedx/core/djangoapps/credit/models.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" +Models for Credit Eligibility for courses. + +Credit courses allow students to receive university credit for +successful completion of a course on EdX +""" + +import logging + +from django.db import models +from model_utils.models import TimeStampedModel +from xmodule_django.models import CourseKeyField +from jsonfield.fields import JSONField + +log = logging.getLogger(__name__) + + +class CreditCourse(models.Model): + """Model for tracking a credit course.""" + + course_key = CourseKeyField(max_length=255, db_index=True, unique=True) + enabled = models.BooleanField(default=False) + + +class CreditProvider(TimeStampedModel): + """This model represents an institution that can grant credit for a course. + + Each provider is identified by unique ID (e.g., 'ASU'). + """ + + provider_id = models.CharField(max_length=255, db_index=True, unique=True) + display_name = models.CharField(max_length=255) + + +class CreditRequirement(TimeStampedModel): + """This model represents a credit requirement. + + Each requirement is uniquely identified by a `namespace` and a `name`. CreditRequirements + also include a `configuration` dictionary, the format of which varies by the type of requirement. + The configuration dictionary provides additional information clients may need to determine + whether a user has satisfied the requirement. + """ + + course = models.ForeignKey(CreditCourse, related_name="credit_requirements") + namespace = models.CharField(max_length=255) + name = models.CharField(max_length=255) + configuration = JSONField() + active = models.BooleanField(default=True) + + class Meta(object): + """Model metadata""" + unique_together = ('namespace', 'name', 'course') + + +class CreditRequirementStatus(TimeStampedModel): + """This model represents the status of each requirement.""" + + REQUIREMENT_STATUS_CHOICES = ( + ("satisfied", "satisfied"), + ) + + username = models.CharField(max_length=255, db_index=True) + requirement = models.ForeignKey(CreditRequirement, related_name="statuses") + status = models.CharField(choices=REQUIREMENT_STATUS_CHOICES, max_length=32) + + +class CreditEligibility(TimeStampedModel): + """A record of a user's eligibility for credit from a specific credit + provider for a specific course. + """ + + username = models.CharField(max_length=255, db_index=True) + course = models.ForeignKey(CreditCourse, related_name="eligibilities") + provider = models.ForeignKey(CreditProvider, related_name="eligibilities") + + class Meta(object): + """Model metadata""" + unique_together = ('username', 'course') diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 74c94ab2fc..56fb22b505 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -1,9 +1,8 @@ # DON'T JUST ADD NEW DEPENDENCIES!!! # # If you open a pull request that adds a new dependency, you should notify: -# * @mollydb to check licensing -# * One of @e0d, @feanil, @fredsmith, @maxrothman, or @jibsheet -# to check system requirements +# * @mollydb - to check licensing +# * support@edx.org - to check system requirements beautifulsoup4==4.1.3 beautifulsoup==3.2.1 @@ -154,3 +153,4 @@ analytics-python==0.4.4 # Needed for mailchimp(mailing djangoapp) mailsnake==1.6.2 +jsonfield==1.0.3