Commit 38b859fd by zubair-arbi

use 'name' field in 'CreditRequirement' model to save the location of xblocks

parent 099be9e7
...@@ -6,7 +6,7 @@ from openedx.core.djangoapps.credit.exceptions import InvalidCreditCourse ...@@ -6,7 +6,7 @@ from openedx.core.djangoapps.credit.exceptions import InvalidCreditCourse
def set_credit_requirements(course_key, requirements): def set_credit_requirements(course_key, requirements):
""" Add requirements to given course """Add requirements to given course.
Args: Args:
course_key(CourseKey): The identifier for course course_key(CourseKey): The identifier for course
...@@ -17,23 +17,21 @@ def set_credit_requirements(course_key, requirements): ...@@ -17,23 +17,21 @@ def set_credit_requirements(course_key, requirements):
"course-v1-edX-DemoX-1T2015", "course-v1-edX-DemoX-1T2015",
[ [
{ {
"namespace": "verification",
"name": "verification",
"criteria": {},
},
{
"namespace": "reverification", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "Assessment 1",
"criteria": {}, "criteria": {},
}, },
{ {
"namespace": "proctored_exam", "namespace": "proctored_exam",
"name": "final", "name": "i4x://edX/DemoX/proctoring-block/final_uuid",
"display_name": "Final Exam",
"criteria": {}, "criteria": {},
}, },
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": {"min_grade": 0.8}, "criteria": {"min_grade": 0.8},
}, },
]) ])
...@@ -65,7 +63,7 @@ def set_credit_requirements(course_key, requirements): ...@@ -65,7 +63,7 @@ def set_credit_requirements(course_key, requirements):
def get_credit_requirements(course_key, namespace=None): def get_credit_requirements(course_key, namespace=None):
""" Returns the requirements of a given course and namespace """Get credit eligibility requirements of a given course and namespace.
Args: Args:
course_key(CourseKey): The identifier for course course_key(CourseKey): The identifier for course
...@@ -77,23 +75,21 @@ def get_credit_requirements(course_key, namespace=None): ...@@ -77,23 +75,21 @@ def get_credit_requirements(course_key, namespace=None):
requirements = requirements =
[ [
{ {
"namespace": "verification",
"name": "verification",
"criteria": {},
},
{
"namespace": "reverification", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "Assessment 1",
"criteria": {}, "criteria": {},
}, },
{ {
"namespace": "proctored_exam", "namespace": "proctored_exam",
"name": "final", "name": "i4x://edX/DemoX/proctoring-block/final_uuid",
"display_name": "Final Exam",
"criteria": {}, "criteria": {},
}, },
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": {"min_grade": 0.8}, "criteria": {"min_grade": 0.8},
}, },
] ]
...@@ -108,6 +104,7 @@ def get_credit_requirements(course_key, namespace=None): ...@@ -108,6 +104,7 @@ def get_credit_requirements(course_key, namespace=None):
{ {
"namespace": requirement.namespace, "namespace": requirement.namespace,
"name": requirement.name, "name": requirement.name,
"display_name": requirement.display_name,
"criteria": requirement.criteria "criteria": requirement.criteria
} }
for requirement in requirements for requirement in requirements
...@@ -115,7 +112,8 @@ def get_credit_requirements(course_key, namespace=None): ...@@ -115,7 +112,8 @@ def get_credit_requirements(course_key, namespace=None):
def _get_requirements_to_disable(old_requirements, new_requirements): def _get_requirements_to_disable(old_requirements, new_requirements):
""" Returns the ids of CreditRequirement to be disabled that are deleted from the courseware """Get the ids of 'CreditRequirement' entries to be disabled that are
deleted from the courseware.
Args: Args:
old_requirements(QuerySet): QuerySet of CreditRequirement old_requirements(QuerySet): QuerySet of CreditRequirement
...@@ -128,6 +126,7 @@ def _get_requirements_to_disable(old_requirements, new_requirements): ...@@ -128,6 +126,7 @@ def _get_requirements_to_disable(old_requirements, new_requirements):
for old_req in old_requirements: for old_req in old_requirements:
found_flag = False found_flag = False
for req in new_requirements: for req in new_requirements:
# check if an already added requirement is modified
if req["namespace"] == old_req.namespace and req["name"] == old_req.name: if req["namespace"] == old_req.namespace and req["name"] == old_req.name:
found_flag = True found_flag = True
break break
...@@ -137,7 +136,7 @@ def _get_requirements_to_disable(old_requirements, new_requirements): ...@@ -137,7 +136,7 @@ def _get_requirements_to_disable(old_requirements, new_requirements):
def _validate_requirements(requirements): def _validate_requirements(requirements):
""" Validate the requirements """Validate the requirements.
Args: Args:
requirements(list): List of requirements requirements(list): List of requirements
...@@ -152,6 +151,8 @@ def _validate_requirements(requirements): ...@@ -152,6 +151,8 @@ def _validate_requirements(requirements):
invalid_params.append("namespace") invalid_params.append("namespace")
if not requirement.get("name"): if not requirement.get("name"):
invalid_params.append("name") invalid_params.append("name")
if not requirement.get("display_name"):
invalid_params.append("display_name")
if "criteria" not in requirement: if "criteria" not in requirement:
invalid_params.append("criteria") invalid_params.append("criteria")
......
""" This module contains the exceptions raised in credit course requirements """ """
This module contains the exceptions raised in credit course requirements.
"""
class InvalidCreditRequirements(Exception): class InvalidCreditRequirements(Exception):
""" The exception occurs when the requirement dictionary has invalid format. """ """
The exception occurs when the requirement dictionary has invalid format.
"""
pass pass
class InvalidCreditCourse(Exception): class InvalidCreditCourse(Exception):
""" The exception occurs when the the course is not marked as a Credit Course. """ """
The exception occurs when the the course is not marked as a Credit Course.
"""
pass pass
# -*- 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 field 'CreditRequirement.display_name'
db.add_column('credit_creditrequirement', 'display_name',
self.gf('django.db.models.fields.CharField')(default='', max_length=255),
keep_default=False)
def backwards(self, orm):
# Deleting field 'CreditRequirement.display_name'
db.delete_column('credit_creditrequirement', 'display_name')
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'}),
'course': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'credit_requirements'", 'to': "orm['credit.CreditCourse']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'criteria': ('jsonfield.fields.JSONField', [], {}),
'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'}),
'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'}),
'reason': ('jsonfield.fields.JSONField', [], {'default': '{}'}),
'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']
\ No newline at end of file
...@@ -19,14 +19,16 @@ log = logging.getLogger(__name__) ...@@ -19,14 +19,16 @@ log = logging.getLogger(__name__)
class CreditCourse(models.Model): class CreditCourse(models.Model):
"""Model for tracking a credit course.""" """
Model for tracking a credit course.
"""
course_key = CourseKeyField(max_length=255, db_index=True, unique=True) course_key = CourseKeyField(max_length=255, db_index=True, unique=True)
enabled = models.BooleanField(default=False) enabled = models.BooleanField(default=False)
@classmethod @classmethod
def is_credit_course(cls, course_key): def is_credit_course(cls, course_key):
""" Check that given course is credit or not """Check that given course is credit or not.
Args: Args:
course_key(CourseKey): The course identifier course_key(CourseKey): The course identifier
...@@ -38,7 +40,7 @@ class CreditCourse(models.Model): ...@@ -38,7 +40,7 @@ class CreditCourse(models.Model):
@classmethod @classmethod
def get_credit_course(cls, course_key): def get_credit_course(cls, course_key):
""" Get the credit course if exists for the given course_key """Get the credit course if exists for the given 'course_key'.
Args: Args:
course_key(CourseKey): The course identifier course_key(CourseKey): The course identifier
...@@ -65,28 +67,36 @@ class CreditProvider(TimeStampedModel): ...@@ -65,28 +67,36 @@ class CreditProvider(TimeStampedModel):
class CreditRequirement(TimeStampedModel): class CreditRequirement(TimeStampedModel):
"""This model represents a credit requirement. """This model represents a credit requirement.
Each requirement is uniquely identified by a `namespace` and a `name`. CreditRequirements Each requirement is uniquely identified by its 'namespace' and
also include a `criteria` dictionary, the format of which varies by the type of requirement. 'name' fields.
The criteria dictionary provides additional information clients may need to determine The 'name' field stores the unique name or location (in case of XBlock)
whether a user has satisfied the requirement. for a requirement, which serves as the unique identifier for that
requirement.
The 'display_name' field stores the display name of the requirement.
The 'criteria' field dictionary provides additional information, clients
may need to determine whether a user has satisfied the requirement.
""" """
course = models.ForeignKey(CreditCourse, related_name="credit_requirements") course = models.ForeignKey(CreditCourse, related_name="credit_requirements")
namespace = models.CharField(max_length=255) namespace = models.CharField(max_length=255)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
display_name = models.CharField(max_length=255)
criteria = JSONField() criteria = JSONField()
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
class Meta(object): class Meta(object):
"""Model metadata""" """
Model metadata.
"""
unique_together = ('namespace', 'name', 'course') unique_together = ('namespace', 'name', 'course')
@classmethod @classmethod
def add_or_update_course_requirement(cls, credit_course, requirement): def add_or_update_course_requirement(cls, credit_course, requirement):
""" Add requirement to a given course """Add requirement to a given course.
Args: Args:
credit_course(CreditCourse): The identifier for credit course course credit_course(CreditCourse): The identifier for credit course
requirement(dict): requirement dict to be added requirement(dict): Requirement dict to be added
Returns: Returns:
(CreditRequirement, created) tuple (CreditRequirement, created) tuple
...@@ -96,6 +106,7 @@ class CreditRequirement(TimeStampedModel): ...@@ -96,6 +106,7 @@ class CreditRequirement(TimeStampedModel):
course=credit_course, course=credit_course,
namespace=requirement["namespace"], namespace=requirement["namespace"],
name=requirement["name"], name=requirement["name"],
display_name=requirement["display_name"],
defaults={"criteria": requirement["criteria"], "active": True} defaults={"criteria": requirement["criteria"], "active": True}
) )
if not created: if not created:
...@@ -107,11 +118,11 @@ class CreditRequirement(TimeStampedModel): ...@@ -107,11 +118,11 @@ class CreditRequirement(TimeStampedModel):
@classmethod @classmethod
def get_course_requirements(cls, course_key, namespace=None): def get_course_requirements(cls, course_key, namespace=None):
""" Get credit requirements of a given course """Get credit requirements of a given course.
Args: Args:
course_key(CourseKey): The identifier for a course course_key(CourseKey): The identifier for a course
namespace(str): namespace of credit course requirements namespace(str): Namespace of credit course requirements
Returns: Returns:
QuerySet of CreditRequirement model QuerySet of CreditRequirement model
...@@ -123,7 +134,7 @@ class CreditRequirement(TimeStampedModel): ...@@ -123,7 +134,7 @@ class CreditRequirement(TimeStampedModel):
@classmethod @classmethod
def disable_credit_requirements(cls, requirement_ids): def disable_credit_requirements(cls, requirement_ids):
""" Mark the given requirements inactive """Mark the given requirements inactive.
Args: Args:
requirement_ids(list): List of ids requirement_ids(list): List of ids
...@@ -176,5 +187,7 @@ class CreditEligibility(TimeStampedModel): ...@@ -176,5 +187,7 @@ class CreditEligibility(TimeStampedModel):
provider = models.ForeignKey(CreditProvider, related_name="eligibilities") provider = models.ForeignKey(CreditProvider, related_name="eligibilities")
class Meta(object): class Meta(object):
"""Model metadata""" """
Model metadata.
"""
unique_together = ('username', 'course') unique_together = ('username', 'course')
"""This file contains receivers of course publication signals.""" """
This file contains receivers of course publication signals.
"""
from django.dispatch import receiver from django.dispatch import receiver
...@@ -7,11 +9,12 @@ from xmodule.modulestore.django import SignalHandler ...@@ -7,11 +9,12 @@ from xmodule.modulestore.django import SignalHandler
@receiver(SignalHandler.course_published) @receiver(SignalHandler.course_published)
def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument
""" """Receive 'course_published' signal and kick off a celery task to update
Receives signal and kicks off celery task to update the course requirements. the credit course requirements.
""" """
# import here, because signal is registered at startup, but items in tasks are not yet able to be loaded # Import here, because signal is registered at startup, but items in tasks
# are not yet able to be loaded
from .tasks import update_course_requirements from .tasks import update_course_requirements
update_course_requirements.delay(unicode(course_key)) update_course_requirements.delay(unicode(course_key))
""" This file contains celery tasks for credit course views """ """
This file contains celery tasks for credit course views.
"""
from django.conf import settings from django.conf import settings
...@@ -55,20 +57,20 @@ def _get_course_credit_requirements(course): ...@@ -55,20 +57,20 @@ def _get_course_credit_requirements(course):
List of minimum_grade_credit and ICRV requirements List of minimum_grade_credit and ICRV requirements
""" """
icrv_requirements = _get_credit_course_requirement_xblocks(course) credit_xblock_requirements = _get_credit_course_requirement_xblocks(course)
min_grade_requirement = _get_min_grade_requirement(course) min_grade_requirement = _get_min_grade_requirement(course)
credit_requirements = icrv_requirements + min_grade_requirement credit_requirements = credit_xblock_requirements + min_grade_requirement
return credit_requirements return credit_requirements
def _get_min_grade_requirement(course): def _get_min_grade_requirement(course):
"""Returns the list of minimum_grade_credit requirements for the given course. """Get list of 'minimum_grade_credit' requirement for the given course.
Args: Args:
course(Course): The course object course(Course): The course object
Raises: Raises:
AttributeError if the course has not minimum_grade_credit attribute AttributeError if the course has not 'minimum_grade_credit' attribute
Returns: Returns:
The list of minimum_grade_credit requirements The list of minimum_grade_credit requirements
...@@ -80,6 +82,7 @@ def _get_min_grade_requirement(course): ...@@ -80,6 +82,7 @@ def _get_min_grade_requirement(course):
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": getattr(course, "minimum_grade_credit") "min_grade": getattr(course, "minimum_grade_credit")
} }
...@@ -91,7 +94,7 @@ def _get_min_grade_requirement(course): ...@@ -91,7 +94,7 @@ def _get_min_grade_requirement(course):
def _get_credit_course_requirement_xblocks(course): # pylint: disable=invalid-name def _get_credit_course_requirement_xblocks(course): # pylint: disable=invalid-name
"""Generates a course structure dictionary for the specified course. """Generate a course structure dictionary for the specified course.
Args: Args:
course(Course): The course object course(Course): The course object
...@@ -109,23 +112,25 @@ def _get_credit_course_requirement_xblocks(course): # pylint: disable=invalid-n ...@@ -109,23 +112,25 @@ def _get_credit_course_requirement_xblocks(course): # pylint: disable=invalid-n
block = { block = {
"namespace": curr_block.get_credit_requirement_namespace(), "namespace": curr_block.get_credit_requirement_namespace(),
"name": curr_block.get_credit_requirement_name(), "name": curr_block.get_credit_requirement_name(),
"display_name": curr_block.get_credit_requirement_display_name(),
"criteria": "" "criteria": ""
} }
requirements_blocks.append(block) requirements_blocks.append(block)
# Add this blocks children to the stack so that we can traverse them as well. # Add the children of current block to the stack so that we can
# traverse them as well.
blocks_stack.extend(children) blocks_stack.extend(children)
return requirements_blocks return requirements_blocks
def _is_credit_requirement(xblock): def _is_credit_requirement(xblock):
"""Check if the given xblock is a credit requirement. """Check if the given XBlock is a credit requirement.
Args: Args:
xblock(XBlock): The given xblock object xblock(XBlock): The given XBlock object
Returns: Returns:
True if xblock is a credit requirement else False True if XBlock is a credit requirement else False
""" """
is_credit_requirement = False is_credit_requirement = False
......
""" Tests for credit course api """ """
Tests for credit course api.
"""
import ddt import ddt
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.credit.api import ( from openedx.core.djangoapps.credit.api import (
get_credit_requirements, set_credit_requirements, _get_requirements_to_disable get_credit_requirements, set_credit_requirements, _get_requirements_to_disable
) )
...@@ -12,7 +16,9 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -12,7 +16,9 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@ddt.ddt @ddt.ddt
class ApiTestCases(ModuleStoreTestCase): class ApiTestCases(ModuleStoreTestCase):
""" Tests for credit course api """ """
Tests for credit course api.
"""
def setUp(self, **kwargs): def setUp(self, **kwargs):
super(ApiTestCases, self).setUp() super(ApiTestCases, self).setUp()
...@@ -39,6 +45,7 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -39,6 +45,7 @@ class ApiTestCases(ModuleStoreTestCase):
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade"
} }
] ]
) )
...@@ -48,25 +55,34 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -48,25 +55,34 @@ class ApiTestCases(ModuleStoreTestCase):
set_credit_requirements(self.course_key, requirements) set_credit_requirements(self.course_key, requirements)
def test_set_credit_requirements_invalid_course(self): def test_set_credit_requirements_invalid_course(self):
"""Test that 'InvalidCreditCourse' exception is raise if we try to
set credit requirements for a non credit course.
"""
requirements = [ requirements = [
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": {} "criteria": {}
} }
] ]
with self.assertRaises(InvalidCreditCourse): with self.assertRaises(InvalidCreditCourse):
set_credit_requirements(self.course_key, requirements) set_credit_requirements(self.course_key, requirements)
self.add_credit_course(enabled=False) self.add_credit_course(enabled=False)
with self.assertRaises(InvalidCreditCourse): with self.assertRaises(InvalidCreditCourse):
set_credit_requirements(self.course_key, requirements) set_credit_requirements(self.course_key, requirements)
def test_set_get_credit_requirements(self): def test_set_get_credit_requirements(self):
"""Test that if same requirement is added multiple times
then it is added only one time and update for next all iterations.
"""
self.add_credit_course() self.add_credit_course()
requirements = [ requirements = [
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
...@@ -74,27 +90,26 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -74,27 +90,26 @@ class ApiTestCases(ModuleStoreTestCase):
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.9
} }
} }
] ]
set_credit_requirements(self.course_key, requirements) set_credit_requirements(self.course_key, requirements)
self.assertEqual(len(get_credit_requirements(self.course_key)), 1) self.assertEqual(len(get_credit_requirements(self.course_key)), 1)
# now verify that the saved requirement has values of last requirement
# from all same requirements
self.assertEqual(get_credit_requirements(self.course_key)[0], requirements[1])
def test_disable_credit_requirements(self): def test_disable_credit_requirements(self):
self.add_credit_course() self.add_credit_course()
requirements = [ requirements = [
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"criteria": { "display_name": "Grade",
"min_grade": 0.8
}
},
{
"namespace": "grade",
"name": "grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
...@@ -106,12 +121,14 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -106,12 +121,14 @@ class ApiTestCases(ModuleStoreTestCase):
requirements = [ requirements = [
{ {
"namespace": "reverification", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "Assessment 1",
"criteria": {} "criteria": {}
} }
] ]
set_credit_requirements(self.course_key, requirements) set_credit_requirements(self.course_key, requirements)
self.assertEqual(len(get_credit_requirements(self.course_key)), 1) self.assertEqual(len(get_credit_requirements(self.course_key)), 1)
grade_req = CreditRequirement.objects.filter(namespace="grade", name="grade") grade_req = CreditRequirement.objects.filter(namespace="grade", name="grade")
self.assertEqual(len(grade_req), 1) self.assertEqual(len(grade_req), 1)
self.assertEqual(grade_req[0].active, False) self.assertEqual(grade_req[0].active, False)
...@@ -122,13 +139,7 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -122,13 +139,7 @@ class ApiTestCases(ModuleStoreTestCase):
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"criteria": { "display_name": "Grade",
"min_grade": 0.8
}
},
{
"namespace": "grade",
"name": "grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
...@@ -142,7 +153,8 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -142,7 +153,8 @@ class ApiTestCases(ModuleStoreTestCase):
requirements = [ requirements = [
{ {
"namespace": "reverification", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "Assessment 1",
"criteria": {} "criteria": {}
} }
] ]
...@@ -154,13 +166,15 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -154,13 +166,15 @@ class ApiTestCases(ModuleStoreTestCase):
{ {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
}, },
{ {
"namespace": "reverification", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "Assessment 1",
"criteria": {} "criteria": {}
} }
] ]
...@@ -168,8 +182,9 @@ class ApiTestCases(ModuleStoreTestCase): ...@@ -168,8 +182,9 @@ class ApiTestCases(ModuleStoreTestCase):
self.assertEqual(len(requirements_to_disabled), 0) self.assertEqual(len(requirements_to_disabled), 0)
def add_credit_course(self, enabled=True): def add_credit_course(self, enabled=True):
""" Mark the course as a credit """ """
Mark the course as a credit.
"""
credit_course = CreditCourse(course_key=self.course_key, enabled=enabled) credit_course = CreditCourse(course_key=self.course_key, enabled=enabled)
credit_course.save() credit_course.save()
return credit_course return credit_course
""" Tests for credit course models """ """
Tests for credit course models.
"""
import ddt import ddt
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.credit.models import CreditCourse, CreditRequirement from openedx.core.djangoapps.credit.models import CreditCourse, CreditRequirement
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@ddt.ddt @ddt.ddt
class ModelTestCases(ModuleStoreTestCase): class ModelTestCases(ModuleStoreTestCase):
""" Tests for credit course models """ """
Tests for credit course models.
"""
def setUp(self, **kwargs): def setUp(self, **kwargs):
super(ModelTestCases, self).setUp() super(ModelTestCases, self).setUp()
...@@ -28,6 +33,7 @@ class ModelTestCases(ModuleStoreTestCase): ...@@ -28,6 +33,7 @@ class ModelTestCases(ModuleStoreTestCase):
requirement = { requirement = {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
...@@ -43,6 +49,7 @@ class ModelTestCases(ModuleStoreTestCase): ...@@ -43,6 +49,7 @@ class ModelTestCases(ModuleStoreTestCase):
requirement = { requirement = {
"namespace": "grade", "namespace": "grade",
"name": "grade", "name": "grade",
"display_name": "Grade",
"criteria": { "criteria": {
"min_grade": 0.8 "min_grade": 0.8
} }
...@@ -52,9 +59,10 @@ class ModelTestCases(ModuleStoreTestCase): ...@@ -52,9 +59,10 @@ class ModelTestCases(ModuleStoreTestCase):
self.assertEqual(created, True) self.assertEqual(created, True)
requirement = { requirement = {
"namespace": "icrv", "namespace": "reverification",
"name": "midterm", "name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"criteria": "" "display_name": "Assessment 1",
"criteria": {}
} }
credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement) credit_req, created = CreditRequirement.add_or_update_course_requirement(credit_course, requirement)
self.assertEqual(credit_course, credit_req.course) self.assertEqual(credit_course, credit_req.course)
...@@ -62,6 +70,7 @@ class ModelTestCases(ModuleStoreTestCase): ...@@ -62,6 +70,7 @@ class ModelTestCases(ModuleStoreTestCase):
requirements = CreditRequirement.get_course_requirements(self.course_key) requirements = CreditRequirement.get_course_requirements(self.course_key)
self.assertEqual(len(requirements), 2) self.assertEqual(len(requirements), 2)
requirements = CreditRequirement.get_course_requirements(self.course_key, namespace="grade") requirements = CreditRequirement.get_course_requirements(self.course_key, namespace="grade")
self.assertEqual(len(requirements), 1) self.assertEqual(len(requirements), 1)
......
""" Tests for credit course tasks """ """
Tests for credit course tasks.
"""
import mock import mock
from datetime import datetime from datetime import datetime
...@@ -13,16 +15,17 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory ...@@ -13,16 +15,17 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
class TestTaskExecution(ModuleStoreTestCase): class TestTaskExecution(ModuleStoreTestCase):
""" """Set of tests to ensure that the task code will do the right thing when
Set of tests to ensure that the task code will do the right thing when executed directly.
executed directly. The test course gets created without the listeners
being present, which allows us to ensure that when the listener is The test course gets created without the listeners being present, which
executed, it is done as expected. allows us to ensure that when the listener is executed, it is done as
expected.
""" """
def mocked_set_credit_requirements(course_key, requirements): # pylint: disable=no-self-argument, unused-argument def mocked_set_credit_requirements(course_key, requirements): # pylint: disable=no-self-argument, unused-argument
""" """Used as a side effect when mocking method credit api method
Used as a side effect when mocking `verify_student.ssencrypt.has_valid_signature`. 'set_credit_requirements'.
""" """
raise InvalidCreditRequirements raise InvalidCreditRequirements
...@@ -46,8 +49,7 @@ class TestTaskExecution(ModuleStoreTestCase): ...@@ -46,8 +49,7 @@ class TestTaskExecution(ModuleStoreTestCase):
def test_task_adding_requirements_invalid_course(self): def test_task_adding_requirements_invalid_course(self):
""" """
Make sure that the receiver correctly fires off the task when Test that credit requirements cannot be added for non credit course.
invoked by signal
""" """
requirements = get_credit_requirements(self.course.id) requirements = get_credit_requirements(self.course.id)
self.assertEqual(len(requirements), 0) self.assertEqual(len(requirements), 0)
...@@ -57,9 +59,10 @@ class TestTaskExecution(ModuleStoreTestCase): ...@@ -57,9 +59,10 @@ class TestTaskExecution(ModuleStoreTestCase):
self.assertEqual(len(requirements), 0) self.assertEqual(len(requirements), 0)
def test_task_adding_requirements(self): def test_task_adding_requirements(self):
""" """Test that credit requirements are added properly for credit course.
Make sure that the receiver correctly fires off the task when Make sure that the receiver correctly fires off the task when
invoked by signal invoked by signal.
""" """
self.add_credit_course(self.course.id) self.add_credit_course(self.course.id)
requirements = get_credit_requirements(self.course.id) requirements = get_credit_requirements(self.course.id)
...@@ -70,9 +73,8 @@ class TestTaskExecution(ModuleStoreTestCase): ...@@ -70,9 +73,8 @@ class TestTaskExecution(ModuleStoreTestCase):
self.assertEqual(len(requirements), 1) self.assertEqual(len(requirements), 1)
def test_task_adding_icrv_requirements(self): def test_task_adding_icrv_requirements(self):
""" """Make sure that the receiver correctly fires off the task when
Make sure that the receiver correctly fires off the task when invoked by signal.
invoked by signal
""" """
self.add_credit_course(self.course.id) self.add_credit_course(self.course.id)
self.add_icrv_xblock() self.add_icrv_xblock()
...@@ -90,7 +92,9 @@ class TestTaskExecution(ModuleStoreTestCase): ...@@ -90,7 +92,9 @@ class TestTaskExecution(ModuleStoreTestCase):
) )
) )
def test_retry(self): def test_retry(self):
""" """Test that adding credit requirements is retried when
'InvalidCreditRequirements' exception is raised.
Make sure that the receiver correctly fires off the task when Make sure that the receiver correctly fires off the task when
invoked by signal invoked by signal
""" """
...@@ -103,10 +107,10 @@ class TestTaskExecution(ModuleStoreTestCase): ...@@ -103,10 +107,10 @@ class TestTaskExecution(ModuleStoreTestCase):
self.assertEqual(len(requirements), 0) self.assertEqual(len(requirements), 0)
def add_credit_course(self, course_key): def add_credit_course(self, course_key):
""" Add the course as a credit """Add the course as a credit.
Args: Args:
course_key(CourseKey): identifier for the course course_key(CourseKey): Identifier for the course
Returns: Returns:
CreditCourse object added CreditCourse object added
......
...@@ -50,7 +50,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c ...@@ -50,7 +50,7 @@ git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c
git+https://github.com/edx/edx-lint.git@ed8c8d2a0267d4d42f43642d193e25f8bd575d9b#egg=edx_lint==0.2.3 git+https://github.com/edx/edx-lint.git@ed8c8d2a0267d4d42f43642d193e25f8bd575d9b#egg=edx_lint==0.2.3
-e git+https://github.com/edx/xblock-utils.git@db22bc40fd2a75458a3c66d057f88aff5a7383e6#egg=xblock-utils -e git+https://github.com/edx/xblock-utils.git@db22bc40fd2a75458a3c66d057f88aff5a7383e6#egg=xblock-utils
-e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive -e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive
-e git+https://github.com/edx/edx-reverification-block.git@03da85753d5f563a22c1282c0e89fcb2e828b8c1#egg=edx-reverification-block -e git+https://github.com/edx/edx-reverification-block.git@6e2834c5f7e998ad9b81170e7ceb4d8a64900eb0#egg=edx-reverification-block
git+https://github.com/edx/ecommerce-api-client.git@1.0.0#egg=ecommerce-api-client==1.0.0 git+https://github.com/edx/ecommerce-api-client.git@1.0.0#egg=ecommerce-api-client==1.0.0
# Third Party XBlocks # Third Party XBlocks
......
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