Cleaned up milestones API references

This commit is contained in:
Matt Drayer
2015-03-11 23:15:46 -04:00
parent cce00e6933
commit cd05391335
9 changed files with 414 additions and 181 deletions

View File

@@ -5,30 +5,24 @@ Utility library for working with the edx-milestones app
from django.conf import settings
from django.utils.translation import ugettext as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from courseware.models import StudentModule
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from xmodule.modulestore.django import modulestore
from milestones.api import (
get_course_milestones,
add_milestone,
add_course_milestone,
remove_course_milestone,
get_course_milestones_fulfillment_paths,
add_user_milestone,
get_user_milestones,
)
from milestones.models import MilestoneRelationshipType
from milestones.exceptions import InvalidMilestoneRelationshipTypeException
from opaque_keys.edx.keys import UsageKey
NAMESPACE_CHOICES = {
'ENTRANCE_EXAM': 'entrance_exams'
}
def get_namespace_choices():
"""
Return the enum to the caller
"""
return NAMESPACE_CHOICES
def add_prerequisite_course(course_key, prerequisite_course_key):
"""
It would create a milestone, then it would set newly created
@@ -36,18 +30,23 @@ def add_prerequisite_course(course_key, prerequisite_course_key):
and it would set newly created milestone as fulfilment
milestone for course referred by `prerequisite_course_key`.
"""
if settings.FEATURES.get('MILESTONES_APP', False):
# create a milestone
milestone = add_milestone({
'name': _('Course {} requires {}'.format(unicode(course_key), unicode(prerequisite_course_key))),
'namespace': unicode(prerequisite_course_key),
'description': _('System defined milestone'),
})
# add requirement course milestone
add_course_milestone(course_key, 'requires', milestone)
if not settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
return None
from milestones import api as milestones_api
milestone_name = _('Course {course_id} requires {prerequisite_course_id}').format(
course_id=unicode(course_key),
prerequisite_course_id=unicode(prerequisite_course_key)
)
milestone = milestones_api.add_milestone({
'name': milestone_name,
'namespace': unicode(prerequisite_course_key),
'description': _('System defined milestone'),
})
# add requirement course milestone
milestones_api.add_course_milestone(course_key, 'requires', milestone)
# add fulfillment course milestone
add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
# add fulfillment course milestone
milestones_api.add_course_milestone(prerequisite_course_key, 'fulfills', milestone)
def remove_prerequisite_course(course_key, milestone):
@@ -55,11 +54,13 @@ def remove_prerequisite_course(course_key, milestone):
It would remove pre-requisite course milestone for course
referred by `course_key`.
"""
if settings.FEATURES.get('MILESTONES_APP', False):
remove_course_milestone(
course_key,
milestone,
)
if not settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
return None
from milestones import api as milestones_api
milestones_api.remove_course_milestone(
course_key,
milestone,
)
def set_prerequisite_courses(course_key, prerequisite_course_keys):
@@ -69,18 +70,20 @@ def set_prerequisite_courses(course_key, prerequisite_course_keys):
To only remove course milestones pass `course_key` and empty list or
None as `prerequisite_course_keys` .
"""
if settings.FEATURES.get('MILESTONES_APP', False):
#remove any existing requirement milestones with this pre-requisite course as requirement
course_milestones = get_course_milestones(course_key=course_key, relationship="requires")
if course_milestones:
for milestone in course_milestones:
remove_prerequisite_course(course_key, milestone)
if not settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
return None
from milestones import api as milestones_api
#remove any existing requirement milestones with this pre-requisite course as requirement
course_milestones = milestones_api.get_course_milestones(course_key=course_key, relationship="requires")
if course_milestones:
for milestone in course_milestones:
remove_prerequisite_course(course_key, milestone)
# add milestones if pre-requisite course is selected
if prerequisite_course_keys:
for prerequisite_course_key_string in prerequisite_course_keys:
prerequisite_course_key = CourseKey.from_string(prerequisite_course_key_string)
add_prerequisite_course(course_key, prerequisite_course_key)
# add milestones if pre-requisite course is selected
if prerequisite_course_keys:
for prerequisite_course_key_string in prerequisite_course_keys:
prerequisite_course_key = CourseKey.from_string(prerequisite_course_key_string)
add_prerequisite_course(course_key, prerequisite_course_key)
def get_pre_requisite_courses_not_completed(user, enrolled_courses):
@@ -91,10 +94,11 @@ def get_pre_requisite_courses_not_completed(user, enrolled_courses):
prerequisite courses yet to be completed.
"""
pre_requisite_courses = {}
if settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES'):
if settings.FEATURES.get('ENABLE_PREREQUISITE_COURSES', False):
from milestones import api as milestones_api
for course_key in enrolled_courses:
required_courses = []
fulfilment_paths = get_course_milestones_fulfillment_paths(course_key, {'id': user.id})
fulfilment_paths = milestones_api.get_course_milestones_fulfillment_paths(course_key, {'id': user.id})
for milestone_key, milestone_value in fulfilment_paths.items(): # pylint: disable=unused-variable
for key, value in milestone_value.items():
if key == 'courses' and value:
@@ -146,10 +150,12 @@ def fulfill_course_milestone(course_key, user):
Marks the course specified by the given course_key as complete for the given user.
If any other courses require this course as a prerequisite, their milestones will be appropriately updated.
"""
if settings.FEATURES.get('MILESTONES_APP', False):
course_milestones = get_course_milestones(course_key=course_key, relationship="fulfills")
for milestone in course_milestones:
add_user_milestone({'id': user.id}, milestone)
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
course_milestones = milestones_api.get_course_milestones(course_key=course_key, relationship="fulfills")
for milestone in course_milestones:
milestones_api.add_user_milestone({'id': user.id}, milestone)
def get_required_content(course, user):
@@ -159,9 +165,12 @@ def get_required_content(course, user):
"""
required_content = []
if settings.FEATURES.get('MILESTONES_APP', False):
from milestones import api as milestones_api
from milestones.exceptions import InvalidMilestoneRelationshipTypeException
# Get all of the outstanding milestones for this course, for this user
try:
milestone_paths = get_course_milestones_fulfillment_paths(
milestone_paths = milestones_api.get_course_milestones_fulfillment_paths(
unicode(course.id),
serialize_user(user)
)
@@ -221,8 +230,10 @@ def milestones_achieved_by_user(user, namespace):
"""
It would fetch list of milestones completed by user
"""
if settings.FEATURES.get('MILESTONES_APP', False):
return get_user_milestones({'id': user.id}, namespace)
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.get_user_milestones({'id': user.id}, namespace)
def is_valid_course_key(key):
@@ -240,9 +251,11 @@ def seed_milestone_relationship_types():
"""
Helper method to pre-populate MRTs so the tests can run
"""
if settings.FEATURES.get('MILESTONES_APP', False):
MilestoneRelationshipType.objects.create(name='requires')
MilestoneRelationshipType.objects.create(name='fulfills')
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones.models import MilestoneRelationshipType
MilestoneRelationshipType.objects.create(name='requires')
MilestoneRelationshipType.objects.create(name='fulfills')
def generate_milestone_namespace(namespace, course_key=None):
@@ -261,3 +274,106 @@ def serialize_user(user):
return {
'id': user.id,
}
def add_milestone(milestone_data):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.add_milestone(milestone_data)
def get_milestones(namespace):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return []
from milestones import api as milestones_api
return milestones_api.get_milestones(namespace)
def get_milestone_relationship_types():
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return {}
from milestones import api as milestones_api
return milestones_api.get_milestone_relationship_types()
def add_course_milestone(course_id, relationship, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.add_course_milestone(course_id, relationship, milestone)
def get_course_milestones(course_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return []
from milestones import api as milestones_api
return milestones_api.get_course_milestones(course_id)
def add_course_content_milestone(course_id, content_id, relationship, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.add_course_content_milestone(course_id, content_id, relationship, milestone)
def get_course_content_milestones(course_id, content_id, relationship):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return []
from milestones import api as milestones_api
return milestones_api.get_course_content_milestones(course_id, content_id, relationship)
def remove_content_references(content_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.remove_content_references(content_id)
def get_course_milestones_fulfillment_paths(course_id, user_id):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.get_course_milestones_fulfillment_paths(
course_id,
user_id
)
def add_user_milestone(user, milestone):
"""
Client API operation adapter/wrapper
"""
if not settings.FEATURES.get('MILESTONES_APP', False):
return None
from milestones import api as milestones_api
return milestones_api.add_user_milestone(user, milestone)

View File

@@ -0,0 +1,87 @@
"""
Tests for the milestones helpers library, which is the integration point for the edx_milestones API
"""
from mock import patch
from util import milestones_helpers
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@patch.dict('django.conf.settings.FEATURES', {'MILESTONES_APP': False})
class MilestonesHelpersTestCase(ModuleStoreTestCase):
"""
Main test suite for Milestones API client library
"""
def setUp(self):
"""
Test case scaffolding
"""
super(MilestonesHelpersTestCase, self).setUp(create_user=False)
self.course = CourseFactory.create(
metadata={
'entrance_exam_enabled': True,
}
)
self.user = {'id': '123'}
self.milestone = {
'name': 'Test Milestone',
'namespace': 'doesnt.matter',
'description': 'Testing Milestones Helpers Library',
}
def test_add_milestone_returns_none_when_app_disabled(self):
response = milestones_helpers.add_milestone(milestone_data=self.milestone)
self.assertIsNone(response)
def test_get_milestones_returns_none_when_app_disabled(self):
response = milestones_helpers.get_milestones(namespace="whatever")
self.assertEqual(len(response), 0)
def test_get_milestone_relationship_types_returns_none_when_app_disabled(self):
response = milestones_helpers.get_milestone_relationship_types()
self.assertEqual(len(response), 0)
def test_add_course_milestone_returns_none_when_app_disabled(self):
response = milestones_helpers.add_course_milestone(unicode(self.course.id), 'requires', self.milestone)
self.assertIsNone(response)
def test_get_course_milestones_returns_none_when_app_disabled(self):
response = milestones_helpers.get_course_milestones(unicode(self.course.id))
self.assertEqual(len(response), 0)
def test_add_course_content_milestone_returns_none_when_app_disabled(self):
response = milestones_helpers.add_course_content_milestone(
unicode(self.course.id),
'i4x://any/content/id',
'requires',
self.milestone
)
self.assertIsNone(response)
def test_get_course_content_milestones_returns_none_when_app_disabled(self):
response = milestones_helpers.get_course_content_milestones(
unicode(self.course.id),
'i4x://doesnt/matter/for/this/test',
'requires'
)
self.assertEqual(len(response), 0)
def test_remove_content_references_returns_none_when_app_disabled(self):
response = milestones_helpers.remove_content_references("i4x://any/content/id/will/do")
self.assertIsNone(response)
def test_get_namespace_choices_returns_values_when_app_disabled(self):
response = milestones_helpers.get_namespace_choices()
self.assertIn('ENTRANCE_EXAM', response)
def test_get_course_milestones_fulfillment_paths_returns_none_when_app_disabled(self):
response = milestones_helpers.get_course_milestones_fulfillment_paths(unicode(self.course.id), self.user)
self.assertIsNone(response)
def test_add_user_milestone_returns_none_when_app_disabled(self):
response = milestones_helpers.add_user_milestone(self.user, self.milestone)
self.assertIsNone(response)