Cleaned up milestones API references
This commit is contained in:
@@ -6,6 +6,7 @@ import json
|
||||
import copy
|
||||
import mock
|
||||
from mock import patch
|
||||
import unittest
|
||||
|
||||
from django.utils.timezone import UTC
|
||||
from django.test.utils import override_settings
|
||||
@@ -139,19 +140,9 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
self.assertNotContains(response, "Course Introduction Video")
|
||||
self.assertNotContains(response, "Requirements")
|
||||
|
||||
def _seed_milestone_relationship_types(self):
|
||||
"""
|
||||
Helper method to prepopulate MRTs so the tests can run
|
||||
Note the settings check -- exams feature must be enabled for the tests to run correctly
|
||||
"""
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
from milestones.models import MilestoneRelationshipType
|
||||
MilestoneRelationshipType.objects.create(name='requires')
|
||||
MilestoneRelationshipType.objects.create(name='fulfills')
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ENTRANCE_EXAMS': True})
|
||||
@unittest.skipUnless(settings.FEATURES.get('ENTRANCE_EXAMS', False), True)
|
||||
def test_entrance_exam_created_updated_and_deleted_successfully(self):
|
||||
self._seed_milestone_relationship_types()
|
||||
seed_milestone_relationship_types()
|
||||
settings_details_url = get_url(self.course.id)
|
||||
data = {
|
||||
'entrance_exam_enabled': 'true',
|
||||
@@ -196,13 +187,13 @@ class CourseDetailsTestCase(CourseTestCase):
|
||||
self.assertFalse(course.entrance_exam_enabled)
|
||||
self.assertEquals(course.entrance_exam_minimum_score_pct, None)
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ENTRANCE_EXAMS': True})
|
||||
@unittest.skipUnless(settings.FEATURES.get('ENTRANCE_EXAMS', False), True)
|
||||
def test_entrance_exam_store_default_min_score(self):
|
||||
"""
|
||||
test that creating an entrance exam should store the default value, if key missing in json request
|
||||
or entrance_exam_minimum_score_pct is an empty string
|
||||
"""
|
||||
self._seed_milestone_relationship_types()
|
||||
seed_milestone_relationship_types()
|
||||
settings_details_url = get_url(self.course.id)
|
||||
test_data_1 = {
|
||||
'entrance_exam_enabled': 'true',
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
Entrance Exams view module -- handles all requests related to entrance exam management via Studio
|
||||
Intended to be utilized as an AJAX callback handler, versus a proper view/screen
|
||||
"""
|
||||
from functools import wraps
|
||||
import json
|
||||
import logging
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
from django.test import RequestFactory
|
||||
|
||||
from contentstore.views.helpers import create_xblock
|
||||
from contentstore.views.item import delete_item
|
||||
from milestones import api as milestones_api
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys import InvalidKeyError
|
||||
from student.auth import has_course_author_access
|
||||
from util.milestones_helpers import generate_milestone_namespace, NAMESPACE_CHOICES
|
||||
from util import milestones_helpers
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from django.conf import settings
|
||||
@@ -40,8 +40,24 @@ def _get_default_entrance_exam_minimum_pct():
|
||||
return entrance_exam_minimum_score_pct
|
||||
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
def check_feature_enabled(feature_name):
|
||||
"""
|
||||
Ensure the specified feature is turned on. Return an HTTP 400 code if not.
|
||||
"""
|
||||
def _check_feature_enabled(view_func):
|
||||
def _decorator(request, *args, **kwargs):
|
||||
# Deny access if the entrance exam feature is disabled
|
||||
if not settings.FEATURES.get(feature_name, False):
|
||||
return HttpResponseBadRequest()
|
||||
return view_func(request, *args, **kwargs)
|
||||
return wraps(view_func)(_decorator)
|
||||
return _check_feature_enabled
|
||||
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
|
||||
def entrance_exam(request, course_key_string):
|
||||
"""
|
||||
The restful handler for entrance exams.
|
||||
@@ -88,6 +104,7 @@ def entrance_exam(request, course_key_string):
|
||||
return HttpResponse(status=405)
|
||||
|
||||
|
||||
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
|
||||
def create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct):
|
||||
"""
|
||||
api method to create an entrance exam.
|
||||
@@ -150,27 +167,28 @@ def _create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct=N
|
||||
)
|
||||
|
||||
# Add an entrance exam milestone if one does not already exist
|
||||
milestone_namespace = generate_milestone_namespace(
|
||||
NAMESPACE_CHOICES['ENTRANCE_EXAM'],
|
||||
namespace_choices = milestones_helpers.get_namespace_choices()
|
||||
milestone_namespace = milestones_helpers.generate_milestone_namespace(
|
||||
namespace_choices.get('ENTRANCE_EXAM'),
|
||||
course_key
|
||||
)
|
||||
milestones = milestones_api.get_milestones(milestone_namespace)
|
||||
milestones = milestones_helpers.get_milestones(milestone_namespace)
|
||||
if len(milestones):
|
||||
milestone = milestones[0]
|
||||
else:
|
||||
description = 'Autogenerated during {} entrance exam creation.'.format(unicode(course.id))
|
||||
milestone = milestones_api.add_milestone({
|
||||
milestone = milestones_helpers.add_milestone({
|
||||
'name': 'Completed Course Entrance Exam',
|
||||
'namespace': milestone_namespace,
|
||||
'description': description
|
||||
})
|
||||
relationship_types = milestones_api.get_milestone_relationship_types()
|
||||
milestones_api.add_course_milestone(
|
||||
relationship_types = milestones_helpers.get_milestone_relationship_types()
|
||||
milestones_helpers.add_course_milestone(
|
||||
unicode(course.id),
|
||||
relationship_types['REQUIRES'],
|
||||
milestone
|
||||
)
|
||||
milestones_api.add_course_content_milestone(
|
||||
milestones_helpers.add_course_content_milestone(
|
||||
unicode(course.id),
|
||||
unicode(created_block.location),
|
||||
relationship_types['FULFILLS'],
|
||||
@@ -202,6 +220,7 @@ def _get_entrance_exam(request, course_key): # pylint: disable=W0613
|
||||
return HttpResponse(status=404)
|
||||
|
||||
|
||||
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
|
||||
def update_entrance_exam(request, course_key, exam_data):
|
||||
"""
|
||||
Operation to update course fields pertaining to entrance exams
|
||||
@@ -215,6 +234,7 @@ def update_entrance_exam(request, course_key, exam_data):
|
||||
CourseMetadata.update_from_dict(metadata, course, request.user)
|
||||
|
||||
|
||||
@check_feature_enabled(feature_name='ENTRANCE_EXAMS')
|
||||
def delete_entrance_exam(request, course_key):
|
||||
"""
|
||||
api method to delete an entrance exam
|
||||
@@ -238,7 +258,7 @@ def _delete_entrance_exam(request, course_key):
|
||||
for course_child in course_children:
|
||||
if course_child.is_entrance_exam:
|
||||
delete_item(request, course_child.scope_ids.usage_id)
|
||||
milestones_api.remove_content_references(unicode(course_child.scope_ids.usage_id))
|
||||
milestones_helpers.remove_content_references(unicode(course_child.scope_ids.usage_id))
|
||||
|
||||
# Reset the entrance exam flags on the course
|
||||
# Reload the course so we have the latest state
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Test module for Entrance Exams AJAX callback handler workflows
|
||||
"""
|
||||
import json
|
||||
from mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
@@ -9,18 +10,14 @@ from django.test.client import RequestFactory
|
||||
|
||||
from contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase
|
||||
from contentstore.utils import reverse_url
|
||||
from contentstore.views.entrance_exam import create_entrance_exam
|
||||
from contentstore.views.entrance_exam import create_entrance_exam, update_entrance_exam, delete_entrance_exam
|
||||
from models.settings.course_grading import CourseGradingModel
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from student.tests.factories import UserFactory
|
||||
from util import milestones_helpers
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
if settings.FEATURES.get('MILESTONES_APP', False):
|
||||
from milestones import api as milestones_api
|
||||
from milestones.models import MilestoneRelationshipType
|
||||
from util.milestones_helpers import serialize_user
|
||||
|
||||
|
||||
class EntranceExamHandlerTests(CourseTestCase):
|
||||
"""
|
||||
@@ -36,9 +33,8 @@ class EntranceExamHandlerTests(CourseTestCase):
|
||||
self.usage_key = self.course.location
|
||||
self.course_url = '/course/{}'.format(unicode(self.course.id))
|
||||
self.exam_url = '/course/{}/entrance_exam/'.format(unicode(self.course.id))
|
||||
MilestoneRelationshipType.objects.create(name='requires', active=True)
|
||||
MilestoneRelationshipType.objects.create(name='fulfills', active=True)
|
||||
self.milestone_relationship_types = milestones_api.get_milestone_relationship_types()
|
||||
milestones_helpers.seed_milestone_relationship_types()
|
||||
self.milestone_relationship_types = milestones_helpers.get_milestone_relationship_types()
|
||||
|
||||
def test_contentstore_views_entrance_exam_post(self):
|
||||
"""
|
||||
@@ -55,8 +51,8 @@ class EntranceExamHandlerTests(CourseTestCase):
|
||||
self.assertTrue(metadata['entrance_exam_enabled'])
|
||||
self.assertIsNotNone(metadata['entrance_exam_minimum_score_pct'])
|
||||
self.assertIsNotNone(metadata['entrance_exam_id']['value'])
|
||||
self.assertTrue(len(milestones_api.get_course_milestones(unicode(self.course.id))))
|
||||
content_milestones = milestones_api.get_course_content_milestones(
|
||||
self.assertTrue(len(milestones_helpers.get_course_milestones(unicode(self.course.id))))
|
||||
content_milestones = milestones_helpers.get_course_content_milestones(
|
||||
unicode(self.course.id),
|
||||
metadata['entrance_exam_id']['value'],
|
||||
self.milestone_relationship_types['FULFILLS']
|
||||
@@ -123,12 +119,12 @@ class EntranceExamHandlerTests(CourseTestCase):
|
||||
)
|
||||
user.set_password('test')
|
||||
user.save()
|
||||
milestones = milestones_api.get_course_milestones(unicode(self.course_key))
|
||||
milestones = milestones_helpers.get_course_milestones(unicode(self.course_key))
|
||||
self.assertEqual(len(milestones), 1)
|
||||
milestone_key = '{}.{}'.format(milestones[0]['namespace'], milestones[0]['name'])
|
||||
paths = milestones_api.get_course_milestones_fulfillment_paths(
|
||||
paths = milestones_helpers.get_course_milestones_fulfillment_paths(
|
||||
unicode(self.course_key),
|
||||
serialize_user(user)
|
||||
milestones_helpers.serialize_user(user)
|
||||
)
|
||||
|
||||
# What we have now is a course milestone requirement and no valid fulfillment
|
||||
@@ -250,3 +246,22 @@ class EntranceExamHandlerTests(CourseTestCase):
|
||||
|
||||
resp = create_entrance_exam(request, self.course.id, None)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': False})
|
||||
def test_entrance_exam_feature_flag_gating(self):
|
||||
user = UserFactory()
|
||||
user.is_staff = True
|
||||
request = RequestFactory()
|
||||
request.user = user
|
||||
|
||||
resp = self.client.get(self.exam_url)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
resp = create_entrance_exam(request, self.course.id, None)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
resp = delete_entrance_exam(request, self.course.id)
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
# No return, so we'll just ensure no exception is thrown
|
||||
update_entrance_exam(request, self.course.id, {})
|
||||
|
||||
@@ -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)
|
||||
|
||||
87
common/djangoapps/util/tests/test_milestones_helpers.py
Normal file
87
common/djangoapps/util/tests/test_milestones_helpers.py
Normal 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)
|
||||
@@ -63,10 +63,8 @@ from xmodule.x_module import XModuleDescriptor
|
||||
from xblock_django.user_service import DjangoXBlockUserService
|
||||
from util.json_request import JsonResponse
|
||||
from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip
|
||||
if settings.FEATURES.get('MILESTONES_APP', False):
|
||||
from milestones import api as milestones_api
|
||||
from util.milestones_helpers import calculate_entrance_exam_score, get_required_content
|
||||
from util.module_utils import yield_dynamic_descriptor_descendents
|
||||
from util import milestones_helpers
|
||||
from util.module_utils import yield_dynamic_descriptor_descendents
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -136,14 +134,14 @@ def toc_for_course(request, course, active_chapter, active_section, field_data_c
|
||||
return None
|
||||
|
||||
# Check to see if the course is gated on milestone-required content (such as an Entrance Exam)
|
||||
required_content = get_required_content(course, request.user)
|
||||
required_content = milestones_helpers.get_required_content(course, request.user)
|
||||
|
||||
chapters = list()
|
||||
for chapter in course_module.get_display_items():
|
||||
# Only show required content, if there is required content
|
||||
# chapter.hide_from_toc is read-only (boo)
|
||||
local_hide_from_toc = False
|
||||
if len(required_content):
|
||||
if required_content:
|
||||
if unicode(chapter.location) not in required_content:
|
||||
local_hide_from_toc = True
|
||||
|
||||
@@ -375,7 +373,7 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
inner_get_module
|
||||
)
|
||||
exam_modules = [module for module in exam_module_generators]
|
||||
exam_score = calculate_entrance_exam_score(user, course_descriptor, exam_modules)
|
||||
exam_score = milestones_helpers.calculate_entrance_exam_score(user, course_descriptor, exam_modules)
|
||||
return exam_score
|
||||
|
||||
def _fulfill_content_milestones(user, course_key, content_key):
|
||||
@@ -394,8 +392,8 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
exam_pct = _calculate_entrance_exam_score(user, course)
|
||||
if exam_pct >= course.entrance_exam_minimum_score_pct:
|
||||
exam_key = UsageKey.from_string(course.entrance_exam_id)
|
||||
relationship_types = milestones_api.get_milestone_relationship_types()
|
||||
content_milestones = milestones_api.get_course_content_milestones(
|
||||
relationship_types = milestones_helpers.get_milestone_relationship_types()
|
||||
content_milestones = milestones_helpers.get_course_content_milestones(
|
||||
course_key,
|
||||
exam_key,
|
||||
relationship=relationship_types['FULFILLS']
|
||||
@@ -403,7 +401,7 @@ def get_module_system_for_user(user, field_data_cache,
|
||||
# Add each milestone to the user's set...
|
||||
user = {'id': user.id}
|
||||
for milestone in content_milestones:
|
||||
milestones_api.add_user_milestone(user, milestone)
|
||||
milestones_helpers.add_user_milestone(user, milestone)
|
||||
|
||||
def handle_grade_event(block, event_type, event): # pylint: disable=unused-argument
|
||||
"""
|
||||
|
||||
@@ -9,9 +9,7 @@ from courseware.access import has_access
|
||||
from student.models import CourseEnrollment, EntranceExamConfiguration
|
||||
from xmodule.tabs import CourseTabList
|
||||
|
||||
if settings.FEATURES.get('MILESTONES_APP', False):
|
||||
from milestones.api import get_course_milestones_fulfillment_paths
|
||||
from util.milestones_helpers import serialize_user
|
||||
from util import milestones_helpers
|
||||
|
||||
|
||||
def get_course_tab_list(course, user):
|
||||
@@ -33,9 +31,9 @@ def get_course_tab_list(course, user):
|
||||
entrance_exam_mode = False
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
if getattr(course, 'entrance_exam_enabled', False):
|
||||
course_milestones_paths = get_course_milestones_fulfillment_paths(
|
||||
course_milestones_paths = milestones_helpers.get_course_milestones_fulfillment_paths(
|
||||
unicode(course.id),
|
||||
serialize_user(user)
|
||||
milestones_helpers.serialize_user(user)
|
||||
)
|
||||
for __, value in course_milestones_paths.iteritems():
|
||||
if len(value.get('content', [])):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Tests use cases related to LMS Entrance Exam behavior, such as gated content access (TOC)
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -9,12 +10,11 @@ from courseware.model_data import FieldDataCache
|
||||
from courseware.module_render import get_module, toc_for_course
|
||||
from courseware.tests.factories import UserFactory, InstructorFactory
|
||||
from courseware.courses import get_entrance_exam_content_info, get_entrance_exam_score
|
||||
from milestones import api as milestones_api
|
||||
from milestones.models import MilestoneRelationshipType
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DATA_MOCK_MODULESTORE
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from util.milestones_helpers import generate_milestone_namespace, NAMESPACE_CHOICES
|
||||
from util import milestones_helpers
|
||||
from student.models import CourseEnrollment
|
||||
from mock import patch
|
||||
import mock
|
||||
@@ -23,8 +23,10 @@ import mock
|
||||
class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
"""
|
||||
Check that content is properly gated. Create a test course from scratch to mess with.
|
||||
We typically assume that the Entrance Exam feature flag is set to True in test.py
|
||||
However, the tests below are designed to execute workflows regardless of the setting
|
||||
If set to False, we are essentially confirming that the workflows do not cause exceptions
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Test case scaffolding
|
||||
@@ -109,30 +111,31 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
category="problem",
|
||||
display_name="Exam Problem - Problem 3"
|
||||
)
|
||||
milestone_namespace = generate_milestone_namespace(
|
||||
NAMESPACE_CHOICES['ENTRANCE_EXAM'],
|
||||
self.course.id
|
||||
)
|
||||
self.milestone = {
|
||||
'name': 'Test Milestone',
|
||||
'namespace': milestone_namespace,
|
||||
'description': 'Testing Courseware Entrance Exam Chapter',
|
||||
}
|
||||
MilestoneRelationshipType.objects.create(name='requires', active=True)
|
||||
MilestoneRelationshipType.objects.create(name='fulfills', active=True)
|
||||
self.milestone_relationship_types = milestones_api.get_milestone_relationship_types()
|
||||
self.milestone = milestones_api.add_milestone(self.milestone)
|
||||
milestones_api.add_course_milestone(
|
||||
unicode(self.course.id),
|
||||
self.milestone_relationship_types['REQUIRES'],
|
||||
self.milestone
|
||||
)
|
||||
milestones_api.add_course_content_milestone(
|
||||
unicode(self.course.id),
|
||||
unicode(self.entrance_exam.location),
|
||||
self.milestone_relationship_types['FULFILLS'],
|
||||
self.milestone
|
||||
)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
namespace_choices = milestones_helpers.get_namespace_choices()
|
||||
milestone_namespace = milestones_helpers.generate_milestone_namespace(
|
||||
namespace_choices.get('ENTRANCE_EXAM'),
|
||||
self.course.id
|
||||
)
|
||||
self.milestone = {
|
||||
'name': 'Test Milestone',
|
||||
'namespace': milestone_namespace,
|
||||
'description': 'Testing Courseware Entrance Exam Chapter',
|
||||
}
|
||||
milestones_helpers.seed_milestone_relationship_types()
|
||||
self.milestone_relationship_types = milestones_helpers.get_milestone_relationship_types()
|
||||
self.milestone = milestones_helpers.add_milestone(self.milestone)
|
||||
milestones_helpers.add_course_milestone(
|
||||
unicode(self.course.id),
|
||||
self.milestone_relationship_types['REQUIRES'],
|
||||
self.milestone
|
||||
)
|
||||
milestones_helpers.add_course_content_milestone(
|
||||
unicode(self.course.id),
|
||||
unicode(self.entrance_exam.location),
|
||||
self.milestone_relationship_types['FULFILLS'],
|
||||
self.milestone
|
||||
)
|
||||
user = UserFactory()
|
||||
self.request = RequestFactory()
|
||||
self.request.user = user
|
||||
@@ -241,7 +244,8 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
'section': self.exam_1.location.name
|
||||
})
|
||||
resp = self.client.get(url)
|
||||
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': False})
|
||||
def test_entrance_exam_content_absence(self):
|
||||
@@ -261,7 +265,6 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
self.assertNotIn('Exam Problem - Problem 1', resp.content)
|
||||
self.assertNotIn('Exam Problem - Problem 2', resp.content)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
|
||||
def test_entrance_exam_content_presence(self):
|
||||
"""
|
||||
Unit Test: If entrance exam is enabled then its content e.g. problems should be loaded and redirection will
|
||||
@@ -275,41 +278,44 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
'section': self.exam_1.location.name
|
||||
})
|
||||
resp = self.client.get(url)
|
||||
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
|
||||
resp = self.client.get(expected_url)
|
||||
self.assertIn('Exam Problem - Problem 1', resp.content)
|
||||
self.assertIn('Exam Problem - Problem 2', resp.content)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
|
||||
resp = self.client.get(expected_url)
|
||||
self.assertIn('Exam Problem - Problem 1', resp.content)
|
||||
self.assertIn('Exam Problem - Problem 2', resp.content)
|
||||
|
||||
def test_entrance_exam_content_info(self):
|
||||
"""
|
||||
test entrance exam content info method
|
||||
"""
|
||||
exam_chapter, is_exam_passed = get_entrance_exam_content_info(self.request, self.course)
|
||||
self.assertEqual(exam_chapter.url_name, self.entrance_exam.url_name)
|
||||
self.assertEqual(is_exam_passed, False)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertEqual(exam_chapter.url_name, self.entrance_exam.url_name)
|
||||
self.assertEqual(is_exam_passed, False)
|
||||
|
||||
# Pass the entrance exam
|
||||
# pylint: disable=maybe-no-member,no-member
|
||||
grade_dict = {'value': 1, 'max_value': 1, 'user_id': self.request.user.id}
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.course.id,
|
||||
self.request.user,
|
||||
self.course,
|
||||
depth=2
|
||||
)
|
||||
# pylint: disable=protected-access
|
||||
module = get_module(
|
||||
self.request.user,
|
||||
self.request,
|
||||
self.problem_1.scope_ids.usage_id,
|
||||
field_data_cache,
|
||||
)._xmodule
|
||||
module.system.publish(self.problem_1, 'grade', grade_dict)
|
||||
# Pass the entrance exam
|
||||
# pylint: disable=maybe-no-member,no-member
|
||||
grade_dict = {'value': 1, 'max_value': 1, 'user_id': self.request.user.id}
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.course.id,
|
||||
self.request.user,
|
||||
self.course,
|
||||
depth=2
|
||||
)
|
||||
# pylint: disable=protected-access
|
||||
module = get_module(
|
||||
self.request.user,
|
||||
self.request,
|
||||
self.problem_1.scope_ids.usage_id,
|
||||
field_data_cache,
|
||||
)._xmodule
|
||||
module.system.publish(self.problem_1, 'grade', grade_dict)
|
||||
|
||||
exam_chapter, is_exam_passed = get_entrance_exam_content_info(self.request, self.course)
|
||||
self.assertEqual(exam_chapter, None)
|
||||
self.assertEqual(is_exam_passed, True)
|
||||
exam_chapter, is_exam_passed = get_entrance_exam_content_info(self.request, self.course)
|
||||
self.assertEqual(exam_chapter, None)
|
||||
self.assertEqual(is_exam_passed, True)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
|
||||
def test_entrance_exam_score(self):
|
||||
"""
|
||||
test entrance exam score. we will hit the method get_entrance_exam_score to verify exam score.
|
||||
@@ -352,8 +358,9 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
}
|
||||
)
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertIn('To access course materials, you must score', resp.content)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertIn('To access course materials, you must score', resp.content)
|
||||
|
||||
def test_entrance_exam_requirement_message_hidden(self):
|
||||
"""
|
||||
@@ -369,8 +376,9 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
)
|
||||
resp = self.client.get(url)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertNotIn('To access course materials, you must score', resp.content)
|
||||
self.assertNotIn('You have passed the entrance exam.', resp.content)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertNotIn('To access course materials, you must score', resp.content)
|
||||
self.assertNotIn('You have passed the entrance exam.', resp.content)
|
||||
|
||||
def test_entrance_exam_passed_message_and_course_content(self):
|
||||
"""
|
||||
@@ -404,10 +412,12 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
module.system.publish(self.problem_1, 'grade', grade_dict)
|
||||
|
||||
resp = self.client.get(url)
|
||||
self.assertNotIn('To access course materials, you must score', resp.content)
|
||||
self.assertIn('You have passed the entrance exam.', resp.content)
|
||||
self.assertIn('Lesson 1', resp.content)
|
||||
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
|
||||
self.assertNotIn('To access course materials, you must score', resp.content)
|
||||
self.assertIn('You have passed the entrance exam.', resp.content)
|
||||
self.assertIn('Lesson 1', resp.content)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
|
||||
def test_entrance_exam_gating(self):
|
||||
"""
|
||||
Unit Test: test_entrance_exam_gating
|
||||
@@ -477,6 +487,7 @@ class EntranceExamTestCases(ModuleStoreTestCase):
|
||||
for toc_section in self.expected_unlocked_toc:
|
||||
self.assertIn(toc_section, unlocked_toc)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'ENTRANCE_EXAMS': True})
|
||||
def test_skip_entrance_exame_gating(self):
|
||||
"""
|
||||
Tests gating is disabled if skip entrance exam is set for a user.
|
||||
|
||||
@@ -15,16 +15,14 @@ from xmodule import tabs
|
||||
from xmodule.modulestore.tests.django_utils import (
|
||||
TEST_DATA_MIXED_TOY_MODULESTORE, TEST_DATA_MIXED_CLOSED_MODULESTORE
|
||||
)
|
||||
|
||||
from courseware.tabs import get_course_tab_list
|
||||
from courseware.views import get_static_tab_contents, static_tab
|
||||
from student.tests.factories import UserFactory
|
||||
from util import milestones_helpers
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
if settings.FEATURES.get('MILESTONES_APP', False):
|
||||
from courseware.tabs import get_course_tab_list
|
||||
from milestones import api as milestones_api
|
||||
from milestones.models import MilestoneRelationshipType
|
||||
|
||||
|
||||
class StaticTabDateTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
"""Test cases for Static Tab Dates."""
|
||||
@@ -140,9 +138,8 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
self.setup_user()
|
||||
self.enroll(self.course)
|
||||
self.user.is_staff = True
|
||||
self.relationship_types = milestones_api.get_milestone_relationship_types()
|
||||
MilestoneRelationshipType.objects.create(name='requires')
|
||||
MilestoneRelationshipType.objects.create(name='fulfills')
|
||||
self.relationship_types = milestones_helpers.get_milestone_relationship_types()
|
||||
milestones_helpers.seed_milestone_relationship_types()
|
||||
|
||||
def test_get_course_tabs_list_entrance_exam_enabled(self):
|
||||
"""
|
||||
@@ -160,13 +157,13 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
}
|
||||
self.course.entrance_exam_enabled = True
|
||||
self.course.entrance_exam_id = unicode(entrance_exam.location)
|
||||
milestone = milestones_api.add_milestone(milestone)
|
||||
milestones_api.add_course_milestone(
|
||||
milestone = milestones_helpers.add_milestone(milestone)
|
||||
milestones_helpers.add_course_milestone(
|
||||
unicode(self.course.id),
|
||||
self.relationship_types['REQUIRES'],
|
||||
milestone
|
||||
)
|
||||
milestones_api.add_course_content_milestone(
|
||||
milestones_helpers.add_course_content_milestone(
|
||||
unicode(self.course.id),
|
||||
unicode(entrance_exam.location),
|
||||
self.relationship_types['FULFILLS'],
|
||||
|
||||
Reference in New Issue
Block a user