Commit cc04bb99 by Zia Fazal Committed by Matt Drayer

Add new gating logic to restrict users from accessing courseware when an entrance exam is required

- added entrance exam check on course info
- staff can by pass gating and added tests
- refined gating logic
- changes after rebasing with Asad's branch
- check ENTRANCE_EXAMS feature is enabled
- updated test to reflect new logic
- catering anonymous user in entrance exam permission
- fixed broken tests
- change after feedback on 16/3
- fix for a broken test
- created new entrance_exams module
- fixed quality error and improved test coverage
- put get_required_content back in milestones helper
- Refactored entrance exams logic
- Refactored tabs logic
- Fixed broken unit test
- changes after feedback from dan-f on 3/27
- removed unnecessary user.is_anonymous check
- Addressed PR feedback
- Addressed commit-specific feedback
- Rework guard clauses
- Add coverage for course info case
parent 092c86e0
......@@ -6,7 +6,6 @@ Utility library for working with the edx-milestones app
from django.conf import settings
from django.utils.translation import ugettext as _
from courseware.models import StudentModule
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from xmodule.modulestore.django import modulestore
......@@ -165,12 +164,10 @@ 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 = milestones_api.get_course_milestones_fulfillment_paths(
milestone_paths = get_course_milestones_fulfillment_paths(
unicode(course.id),
serialize_user(user)
)
......@@ -183,49 +180,9 @@ def get_required_content(course, user):
if milestone_path.get('content') and len(milestone_path['content']):
for content in milestone_path['content']:
required_content.append(content)
#local imports to avoid circular reference
from student.models import EntranceExamConfiguration
can_skip_entrance_exam = EntranceExamConfiguration.user_can_skip_entrance_exam(user, course.id)
# check if required_content has any entrance exam and user is allowed to skip it
# then remove it from required content
if required_content and getattr(course, 'entrance_exam_enabled', False) and can_skip_entrance_exam:
descriptors = [modulestore().get_item(UsageKey.from_string(content)) for content in required_content]
entrance_exam_contents = [unicode(descriptor.location)
for descriptor in descriptors if descriptor.is_entrance_exam]
required_content = list(set(required_content) - set(entrance_exam_contents))
return required_content
def calculate_entrance_exam_score(user, course_descriptor, exam_modules):
"""
Calculates the score (percent) of the entrance exam using the provided modules
"""
exam_module_ids = [exam_module.location for exam_module in exam_modules]
student_modules = StudentModule.objects.filter(
student=user,
course_id=course_descriptor.id,
module_state_key__in=exam_module_ids,
)
exam_pct = 0
if student_modules:
module_pcts = []
ignore_categories = ['course', 'chapter', 'sequential', 'vertical']
for module in exam_modules:
if module.graded and module.category not in ignore_categories:
module_pct = 0
try:
student_module = student_modules.get(module_state_key=module.location)
if student_module.max_grade:
module_pct = student_module.grade / student_module.max_grade
module_pcts.append(module_pct)
except StudentModule.DoesNotExist:
pass
if module_pcts:
exam_pct = sum(module_pcts) / float(len(module_pcts))
return exam_pct
def milestones_achieved_by_user(user, namespace):
"""
It would fetch list of milestones completed by user
......
......@@ -1037,7 +1037,7 @@ class EntranceExamTest(UniqueCourseTest):
self.course_info['run'], self.course_info['display_name']
).install()
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.settings_page = SettingsPage(
self.browser,
self.course_info['org'],
......@@ -1050,19 +1050,19 @@ class EntranceExamTest(UniqueCourseTest):
def test_entrance_exam_section(self):
"""
Scenario: Any course that is enabled for an entrance exam, should have entrance exam section at course info
Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at courseware
page.
Given that I am on the course info page
When I view the course info that has an entrance exam
Then there should be an "Entrance Exam" section.'
Given that I am on the courseware page
When I view the courseware that has an entrance exam
Then there should be an "Entrance Exam" chapter.'
"""
# visit course info page and make sure there is not entrance exam section.
self.course_info_page.visit()
self.course_info_page.wait_for_page()
entrance_exam_link_selector = 'div#accordion nav div h3 a'
# visit courseware page and make sure there is not entrance exam chapter.
self.courseware_page.visit()
self.courseware_page.wait_for_page()
self.assertFalse(element_has_text(
page=self.course_info_page,
css_selector='div ol li a',
page=self.courseware_page,
css_selector=entrance_exam_link_selector,
text='Entrance Exam'
))
......@@ -1082,10 +1082,10 @@ class EntranceExamTest(UniqueCourseTest):
AutoAuthPage(self.browser, course_id=self.course_id, staff=False).visit()
# visit course info page and make sure there is an "Entrance Exam" section.
self.course_info_page.visit()
self.course_info_page.wait_for_page()
self.courseware_page.visit()
self.courseware_page.wait_for_page()
self.assertTrue(element_has_text(
page=self.course_info_page,
css_selector='div ol li a',
page=self.courseware_page,
css_selector=entrance_exam_link_selector,
text='Entrance Exam'
))
......@@ -12,6 +12,7 @@ from django.contrib.auth.models import AnonymousUser
from django.utils.timezone import UTC
from opaque_keys.edx.keys import CourseKey, UsageKey
from xblock.core import XBlock
from xmodule.course_module import (
......@@ -25,12 +26,13 @@ from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPa
from external_auth.models import ExternalAuthMap
from courseware.masquerade import get_masquerade_role, is_masquerading_as_student
from student import auth
from student.models import CourseEnrollment, CourseEnrollmentAllowed
from student.roles import (
GlobalStaff, CourseStaffRole, CourseInstructorRole,
OrgStaffRole, OrgInstructorRole, CourseBetaTesterRole
)
from student.models import CourseEnrollment, CourseEnrollmentAllowed
from util.milestones_helpers import get_pre_requisite_courses_not_completed
import dogstats_wrapper as dog_stats_api
DEBUG_ACCESS = False
......
......@@ -23,10 +23,8 @@ from courseware.model_data import FieldDataCache
from courseware.module_render import get_module
from student.models import CourseEnrollment
import branding
from util.milestones_helpers import get_required_content, calculate_entrance_exam_score
from util.module_utils import yield_dynamic_descriptor_descendents
from opaque_keys.edx.keys import UsageKey
from .module_render import get_module_for_descriptor
log = logging.getLogger(__name__)
......@@ -449,47 +447,3 @@ def get_problems_in_section(section):
problem_descriptors[unicode(component.location)] = component
return problem_descriptors
def get_entrance_exam_score(request, course):
"""
Get entrance exam score
"""
exam_key = UsageKey.from_string(course.entrance_exam_id)
exam_descriptor = modulestore().get_item(exam_key)
def inner_get_module(descriptor):
"""
Delegate to get_module_for_descriptor.
"""
field_data_cache = FieldDataCache([descriptor], course.id, request.user)
return get_module_for_descriptor(request.user, request, descriptor, field_data_cache, course.id)
exam_module_generators = yield_dynamic_descriptor_descendents(
exam_descriptor,
inner_get_module
)
exam_modules = [module for module in exam_module_generators]
return calculate_entrance_exam_score(request.user, course, exam_modules)
def get_entrance_exam_content_info(request, course):
"""
Get the entrance exam content information e.g. chapter, exam passing state.
return exam chapter and its passing state.
"""
required_content = get_required_content(course, request.user)
exam_chapter = None
is_exam_passed = True
# Iterating the list of required content of this course.
for content in required_content:
# database lookup to required content pointer
usage_key = course.id.make_usage_key_from_deprecated_string(content)
module_item = modulestore().get_item(usage_key)
if not module_item.hide_from_toc and module_item.is_entrance_exam:
# Here we are looking for entrance exam module/chapter in required_content.
# If module_item is an entrance exam chapter then set and return its info e.g. exam chapter, exam state.
exam_chapter = module_item
is_exam_passed = False
break
return exam_chapter, is_exam_passed
"""
This file contains all entrance exam related utils/logic.
"""
from django.conf import settings
from courseware.access import has_access
from courseware.model_data import FieldDataCache
from courseware.models import StudentModule
from opaque_keys.edx.keys import UsageKey
from student.models import EntranceExamConfiguration
from util.milestones_helpers import get_required_content
from util.module_utils import yield_dynamic_descriptor_descendents
from xmodule.modulestore.django import modulestore
def feature_is_enabled():
"""
Checks to see if the Entrance Exams feature is enabled
Use this operation instead of checking the feature flag all over the place
"""
return settings.FEATURES.get('ENTRANCE_EXAMS', False)
def course_has_entrance_exam(course):
"""
Checks to see if a course is properly configured for an entrance exam
"""
if not feature_is_enabled():
return False
if not course.entrance_exam_enabled:
return False
if not course.entrance_exam_id:
return False
return True
def user_can_skip_entrance_exam(request, user, course):
"""
Checks all of the various override conditions for a user to skip an entrance exam
Begin by short-circuiting if the course does not have an entrance exam
"""
if not course_has_entrance_exam(course):
return True
if not user.is_authenticated():
return False
if has_access(user, 'staff', course):
return True
if EntranceExamConfiguration.user_can_skip_entrance_exam(user, course.id):
return True
if not get_entrance_exam_content(request, course):
return True
return False
def user_has_passed_entrance_exam(request, course):
"""
Checks to see if the user has attained a sufficient score to pass the exam
Begin by short-circuiting if the course does not have an entrance exam
"""
if not course_has_entrance_exam(course):
return True
if not request.user.is_authenticated():
return False
entrance_exam_score = get_entrance_exam_score(request, course)
if entrance_exam_score >= course.entrance_exam_minimum_score_pct:
return True
return False
# pylint: disable=invalid-name
def user_must_complete_entrance_exam(request, user, course):
"""
Some courses can be gated on an Entrance Exam, which is a specially-configured chapter module which
presents users with a problem set which they must complete. This particular workflow determines
whether or not the user is allowed to clear the Entrance Exam gate and access the rest of the course.
"""
# First, let's see if the user is allowed to skip
if user_can_skip_entrance_exam(request, user, course):
return False
# If they can't actually skip the exam, we'll need to see if they've already passed it
if user_has_passed_entrance_exam(request, course):
return False
# Can't skip, haven't passed, must take the exam
return True
def _calculate_entrance_exam_score(user, course_descriptor, exam_modules):
"""
Calculates the score (percent) of the entrance exam using the provided modules
"""
# All of the exam module ids
exam_module_ids = [exam_module.location for exam_module in exam_modules]
# All of the corresponding student module records
student_modules = StudentModule.objects.filter(
student=user,
course_id=course_descriptor.id,
module_state_key__in=exam_module_ids,
)
student_module_dict = {}
for student_module in student_modules:
student_module_dict[unicode(student_module.module_state_key)] = {
'grade': student_module.grade,
'max_grade': student_module.max_grade
}
exam_percentage = 0
module_percentages = []
ignore_categories = ['course', 'chapter', 'sequential', 'vertical']
for module in exam_modules:
if module.graded and module.category not in ignore_categories:
module_percentage = 0
module_location = unicode(module.location)
if module_location in student_module_dict and student_module_dict[module_location]['max_grade']:
student_module = student_module_dict[module_location]
module_percentage = student_module['grade'] / student_module['max_grade']
module_percentages.append(module_percentage)
if module_percentages:
exam_percentage = sum(module_percentages) / float(len(module_percentages))
return exam_percentage
def get_entrance_exam_score(request, course):
"""
Gather the set of modules which comprise the entrance exam
Note that 'request' may not actually be a genuine request, due to the
circular nature of module_render calling entrance_exams and get_module_for_descriptor
being used here. In some use cases, the caller is actually mocking a request, although
in these scenarios the 'user' child object can be trusted and used as expected.
It's a much larger refactoring job to break this legacy mess apart, unfortunately.
"""
exam_key = UsageKey.from_string(course.entrance_exam_id)
exam_descriptor = modulestore().get_item(exam_key)
def inner_get_module(descriptor):
"""
Delegate to get_module_for_descriptor (imported here to avoid circular reference)
"""
from courseware.module_render import get_module_for_descriptor
field_data_cache = FieldDataCache([descriptor], course.id, request.user)
return get_module_for_descriptor(
request.user,
request,
descriptor,
field_data_cache,
course.id
)
exam_module_generators = yield_dynamic_descriptor_descendents(
exam_descriptor,
inner_get_module
)
exam_modules = [module for module in exam_module_generators]
return _calculate_entrance_exam_score(request.user, course, exam_modules)
def get_entrance_exam_content(request, course):
"""
Get the entrance exam content information (ie, chapter module)
"""
required_content = get_required_content(course, request.user)
exam_module = None
for content in required_content:
usage_key = course.id.make_usage_key_from_deprecated_string(content)
module_item = modulestore().get_item(usage_key)
if not module_item.hide_from_toc and module_item.is_entrance_exam:
exam_module = module_item
break
return exam_module
......@@ -23,12 +23,17 @@ from django.core.context_processors import csrf
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponse
from django.test.client import RequestFactory
from django.views.decorators.csrf import csrf_exempt
from capa.xqueue_interface import XQueueInterface
from courseware.access import has_access, get_user_role
from courseware.masquerade import setup_masquerade
from courseware.model_data import FieldDataCache, DjangoKeyValueStore
from courseware.entrance_exams import (
get_entrance_exam_score,
user_must_complete_entrance_exam
)
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, unquote_slashes, quote_slashes
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
......@@ -132,11 +137,17 @@ def toc_for_course(request, course, active_chapter, active_section, field_data_c
if course_module is None:
return None
# Check to see if the course is gated on milestone-required content (such as an Entrance Exam)
toc_chapters = list()
chapters = course_module.get_display_items()
# See if the course is gated by one or more content milestones
required_content = milestones_helpers.get_required_content(course, request.user)
chapters = list()
for chapter in course_module.get_display_items():
# The user may not actually have to complete the entrance exam, if one is required
if not user_must_complete_entrance_exam(request, request.user, course):
required_content = [content for content in required_content if not content == course.entrance_exam_id]
for chapter in chapters:
# Only show required content, if there is required content
# chapter.hide_from_toc is read-only (boo)
local_hide_from_toc = False
......@@ -162,11 +173,13 @@ def toc_for_course(request, course, active_chapter, active_section, field_data_c
'active': active,
'graded': section.graded,
})
chapters.append({'display_name': chapter.display_name_with_default,
'url_name': chapter.url_name,
'sections': sections,
'active': chapter.url_name == active_chapter})
return chapters
toc_chapters.append({
'display_name': chapter.display_name_with_default,
'url_name': chapter.url_name,
'sections': sections,
'active': chapter.url_name == active_chapter
})
return toc_chapters
def get_module(user, request, usage_key, field_data_cache,
......@@ -361,20 +374,6 @@ def get_module_system_for_user(user, field_data_cache,
request_token=request_token,
)
def _calculate_entrance_exam_score(user, course_descriptor):
"""
Internal helper to calculate a user's score for a course's entrance exam
"""
exam_key = UsageKey.from_string(course_descriptor.entrance_exam_id)
exam_descriptor = modulestore().get_item(exam_key)
exam_module_generators = yield_dynamic_descriptor_descendents(
exam_descriptor,
inner_get_module
)
exam_modules = [module for module in exam_module_generators]
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):
"""
Internal helper to handle milestone fulfillments for the specified content module
......@@ -388,7 +387,10 @@ def get_module_system_for_user(user, field_data_cache,
entrance_exam_enabled = getattr(course, 'entrance_exam_enabled', False)
in_entrance_exam = getattr(content, 'in_entrance_exam', False)
if entrance_exam_enabled and in_entrance_exam:
exam_pct = _calculate_entrance_exam_score(user, course)
# We don't have access to the true request object in this context, but we can use a mock
request = RequestFactory().request()
request.user = user
exam_pct = get_entrance_exam_score(request, course)
if exam_pct >= course.entrance_exam_minimum_score_pct:
exam_key = UsageKey.from_string(course.entrance_exam_id)
relationship_types = milestones_helpers.get_milestone_relationship_types()
......@@ -398,7 +400,7 @@ def get_module_system_for_user(user, field_data_cache,
relationship=relationship_types['FULFILLS']
)
# Add each milestone to the user's set...
user = {'id': user.id}
user = {'id': request.user.id}
for milestone in content_milestones:
milestones_helpers.add_user_milestone(user, milestone)
......@@ -437,15 +439,13 @@ def get_module_system_for_user(user, field_data_cache,
dog_stats_api.increment("lms.courseware.question_answered", tags=tags)
# If we're using the awesome edx-milestones app, we need to cycle
# through the fulfillment scenarios to see if any are now applicable
# Cycle through the milestone fulfillment scenarios to see if any are now applicable
# thanks to the updated grading information that was just submitted
if settings.FEATURES.get('MILESTONES_APP', False):
_fulfill_content_milestones(
user,
course_id,
descriptor.location,
)
_fulfill_content_milestones(
user,
course_id,
descriptor.location,
)
def publish(block, event_type, event):
"""A function that allows XModules to publish events."""
......
......@@ -3,9 +3,11 @@ This module is essentially a broker to xmodule/tabs.py -- it was originally intr
perform some LMS-specific tab display gymnastics for the Entrance Exams feature
"""
from django.conf import settings
from django.test.client import RequestFactory
from django.utils.translation import ugettext as _
from courseware.access import has_access
from courseware.entrance_exams import user_must_complete_entrance_exam
from student.models import CourseEnrollment, EntranceExamConfiguration
from xmodule.tabs import CourseTabList
......@@ -25,29 +27,15 @@ def get_course_tab_list(course, user):
user_is_enrolled
)
# Entrance Exams Feature
# If the course has an entrance exam, we'll need to see if the user has not passed it
# If so, we'll need to hide away all of the tabs except for Courseware and Instructor
entrance_exam_mode = False
if settings.FEATURES.get('ENTRANCE_EXAMS', False):
if getattr(course, 'entrance_exam_enabled', False):
course_milestones_paths = milestones_helpers.get_course_milestones_fulfillment_paths(
unicode(course.id),
milestones_helpers.serialize_user(user)
)
for __, value in course_milestones_paths.iteritems():
if len(value.get('content', [])):
for content in value['content']:
if content == course.entrance_exam_id \
and not EntranceExamConfiguration.user_can_skip_entrance_exam(user, course.id):
entrance_exam_mode = True
break
# Now that we've loaded the tabs for this course, perform the Entrance Exam mode work
# Majority case is no entrance exam defined
# Now that we've loaded the tabs for this course, perform the Entrance Exam work
# If the user has to take an entrance exam, we'll need to hide away all of the tabs
# except for the Courseware and Instructor tabs (latter is only viewed if applicable)
# We don't have access to the true request object in this context, but we can use a mock
request = RequestFactory().request()
request.user = user
course_tab_list = []
for tab in xmodule_tab_list:
if entrance_exam_mode:
if user_must_complete_entrance_exam(request, user, course):
# Hide all of the tabs except for 'Courseware' and 'Instructor'
# Rename 'Courseware' tab to 'Entrance Exam'
if tab.type not in ['courseware', 'instructor']:
......
......@@ -10,7 +10,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from courseware.courses import get_course_by_id
from courseware.tests.helpers import get_request_for_user, LoginEnrollmentTestCase
from courseware.tests.factories import InstructorFactory
from courseware.tests.factories import InstructorFactory, StaffFactory
from xmodule import tabs
from xmodule.modulestore.tests.django_utils import (
TEST_DATA_MIXED_TOY_MODULESTORE, TEST_DATA_MIXED_CLOSED_MODULESTORE
......@@ -146,15 +146,18 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
Unit Test: test_get_course_tabs_list_entrance_exam_enabled
"""
entrance_exam = ItemFactory.create(
category="chapter", parent_location=self.course.location,
data="Exam Data", display_name="Entrance Exam"
category="chapter",
parent_location=self.course.location,
data="Exam Data",
display_name="Entrance Exam",
is_entrance_exam=True
)
entrance_exam.is_entrance_exam = True
milestone = {
'name': 'Test Milestone',
'namespace': '{}.entrance_exams'.format(unicode(self.course.id)),
'description': 'Testing Courseware Tabs'
}
self.user.is_staff = False
self.course.entrance_exam_enabled = True
self.course.entrance_exam_id = unicode(entrance_exam.location)
milestone = milestones_helpers.add_milestone(milestone)
......@@ -170,10 +173,9 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
milestone
)
course_tab_list = get_course_tab_list(self.course, self.user)
self.assertEqual(len(course_tab_list), 2)
self.assertEqual(len(course_tab_list), 1)
self.assertEqual(course_tab_list[0]['tab_id'], 'courseware')
self.assertEqual(course_tab_list[0]['name'], 'Entrance Exam')
self.assertEqual(course_tab_list[1]['tab_id'], 'instructor')
def test_get_course_tabs_list_skipped_entrance_exam(self):
"""
......@@ -198,6 +200,19 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
course_tab_list = get_course_tab_list(self.course, self.user)
self.assertEqual(len(course_tab_list), 5)
def test_course_tabs_list_for_staff_members(self):
"""
Tests tab list is not limited if user is member of staff
and has not passed entrance exam.
"""
# Login as member of staff
self.client.logout()
staff_user = StaffFactory(course_key=self.course.id)
self.client.login(username=staff_user.username, password='test')
course_tab_list = get_course_tab_list(self.course, staff_user)
self.assertEqual(len(course_tab_list), 5)
class TextBookTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
"""
......
......@@ -32,12 +32,22 @@ from markupsafe import escape
from courseware import grades
from courseware.access import has_access, _adjust_start_date_for_beta_testers
from courseware.courses import get_courses, get_course, get_studio_url, get_course_with_access, sort_by_announcement,\
get_entrance_exam_content_info
from courseware.courses import sort_by_start_date, get_entrance_exam_score
from courseware.courses import (
get_courses, get_course,
get_studio_url, get_course_with_access,
sort_by_announcement,
sort_by_start_date,
)
from courseware.masquerade import setup_masquerade
from courseware.model_data import FieldDataCache
from .module_render import toc_for_course, get_module_for_descriptor, get_module
from .entrance_exams import (
course_has_entrance_exam,
get_entrance_exam_content,
get_entrance_exam_score,
user_must_complete_entrance_exam,
user_has_passed_entrance_exam
)
from courseware.models import StudentModule, StudentModuleHistory
from course_modes.models import CourseMode
......@@ -366,6 +376,15 @@ def _index_bulk_op(request, course_key, chapter, section, position):
user.id, unicode(course.id))
return redirect(reverse('dashboard'))
# Entrance Exam Check
# If the course has an entrance exam and the requested chapter is NOT the entrance exam, and
# the user hasn't yet met the criteria to bypass the entrance exam, redirect them to the exam.
if chapter and course_has_entrance_exam(course):
chapter_descriptor = course.get_child_by(lambda m: m.location.name == chapter)
if chapter_descriptor and not getattr(chapter_descriptor, 'is_entrance_exam', False) \
and user_must_complete_entrance_exam(request, user, course):
log.info(u'User %d tried to view course %s without passing entrance exam', user.id, unicode(course.id))
return redirect(reverse('courseware', args=[unicode(course.id)]))
# check to see if there is a required survey that must be taken before
# the user can access the course.
if survey.utils.must_answer_survey(course, user):
......@@ -412,9 +431,9 @@ def _index_bulk_op(request, course_key, chapter, section, position):
return render_to_response('courseware/courseware.html', context)
elif chapter is None:
# Check first to see if we should instead redirect the user to an Entrance Exam
if settings.FEATURES.get('ENTRANCE_EXAMS', False) and course.entrance_exam_enabled:
exam_chapter, __ = get_entrance_exam_content_info(request, course)
if exam_chapter is not None:
if course_has_entrance_exam(course):
exam_chapter = get_entrance_exam_content(request, course)
if exam_chapter:
exam_section = None
if exam_chapter.get_children():
exam_section = exam_chapter.get_children()[0]
......@@ -454,13 +473,12 @@ def _index_bulk_op(request, course_key, chapter, section, position):
return redirect(reverse('courseware', args=[course.id.to_deprecated_string()]))
raise Http404
if settings.FEATURES.get('ENTRANCE_EXAMS', False) and course.entrance_exam_enabled:
if course_has_entrance_exam(course):
# Message should not appear outside the context of entrance exam subsection.
# if section is none then we don't need to show message on welcome back screen also.
if getattr(chapter_module, 'is_entrance_exam', False) and section is not None:
__, is_exam_passed = get_entrance_exam_content_info(request, course)
context['entrance_exam_current_score'] = get_entrance_exam_score(request, course)
context['entrance_exam_passed'] = is_exam_passed
context['entrance_exam_passed'] = user_has_passed_entrance_exam(request, course)
if section is not None:
section_descriptor = chapter_descriptor.get_child_by(lambda m: m.location.name == section)
......@@ -670,6 +688,11 @@ def course_info(request, course_id):
with modulestore().bulk_operations(course_key):
course = get_course_with_access(request.user, 'load', course_key)
# If the user needs to take an entrance exam to access this course, then we'll need
# to send them to that specific course module before allowing them into other areas
if user_must_complete_entrance_exam(request, request.user, course):
return redirect(reverse('courseware', args=[unicode(course.id)]))
# check to see if there is a required survey that must be taken before
# the user can access the course.
if request.user.is_authenticated() and survey.utils.must_answer_survey(course, request.user):
......
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