Commit aafd6a03 by Diana Huang

Add 'format' as a requested field.

parent 31aa776e
......@@ -134,16 +134,6 @@ class CourseHomeA11yTest(CourseHomeBaseTest):
self.logout_page = LogoutPage(self.browser)
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
self.course_info['run']
)
# adds graded assignments to course home for testing course outline a11y
self._set_policy_for_subsection("Homework", 0)
def test_course_home_a11y(self):
"""
Test the accessibility of the course home page with course outline.
......@@ -153,18 +143,6 @@ class CourseHomeA11yTest(CourseHomeBaseTest):
course_home_page.visit()
course_home_page.a11y_audit.check_for_accessibility_errors()
def _set_policy_for_subsection(self, policy, section=0):
"""
Set the grading policy for the first subsection in the specified section.
If a section index is not provided, 0 is assumed.
"""
with self._logged_in_session(staff=True):
self.studio_course_outline.visit()
modal = self.studio_course_outline.section_at(section).subsection_at(
0).edit()
modal.policy = policy
modal.save()
@contextmanager
def _logged_in_session(self, staff=False):
"""
......
......@@ -51,12 +51,12 @@ def get_blocks(
"""
# create ordered list of transformers, adding BlocksAPITransformer at end.
transformers = BlockStructureTransformers()
can_view_special_exam = False
if requested_fields is not None and 'special_exam' in requested_fields:
can_view_special_exam = True
include_special_exams = False
if requested_fields is not None and 'special_exam_info' in requested_fields:
include_special_exams = True
if user is not None:
transformers += COURSE_BLOCK_ACCESS_TRANSFORMERS
transformers += [MilestonesTransformer(can_view_special_exam), HiddenContentTransformer()]
transformers += [MilestonesTransformer(include_special_exams), HiddenContentTransformer()]
transformers += [
BlocksAPITransformer(
block_counts,
......
......@@ -45,7 +45,7 @@ SUPPORTED_FIELDS = [
# 'student_view_multi_device'
SupportedFieldType(StudentViewTransformer.STUDENT_VIEW_MULTI_DEVICE, StudentViewTransformer),
SupportedFieldType('special_exam', MilestonesTransformer),
SupportedFieldType('special_exam_info', MilestonesTransformer),
# set the block_field_name to None so the entire data for the transformer is serialized
SupportedFieldType(None, BlockCountsTransformer, BlockCountsTransformer.BLOCK_COUNTS),
......
......@@ -19,7 +19,8 @@ log = logging.getLogger(__name__)
class MilestonesTransformer(BlockStructureTransformer):
"""
Excludes all special exams (timed, proctored, practice proctored) from the student view.
Adds special exams (timed, proctored, practice proctored) to the student view.
May exclude special exams.
Excludes all blocks with unfulfilled milestones from the student view.
"""
WRITE_VERSION = 1
......@@ -29,8 +30,8 @@ class MilestonesTransformer(BlockStructureTransformer):
def name(cls):
return "milestones"
def __init__(self, can_view_special_exams=True):
self.can_view_special_exams = can_view_special_exams
def __init__(self, include_special_exams=True):
self.include_special_exams = include_special_exams
@classmethod
def collect(cls, block_structure):
......@@ -51,48 +52,9 @@ class MilestonesTransformer(BlockStructureTransformer):
Modify block structure according to the behavior of milestones and special exams.
"""
def add_special_exam_info(block_key):
"""
Adds special exam information to course blocks.
"""
if self.is_special_exam(block_key, block_structure):
#
# call into edx_proctoring subsystem
# to get relevant proctoring information regarding this
# level of the courseware
#
# This will return None, if (user, course_id, content_id)
# is not applicable
#
timed_exam_attempt_context = None
try:
timed_exam_attempt_context = get_attempt_status_summary(
usage_info.user.id,
unicode(block_key.course_key),
unicode(block_key)
)
except ProctoredExamNotFoundException as ex:
log.exception(ex)
if timed_exam_attempt_context:
# yes, user has proctoring context about
# this level of the courseware
# so add to the accordion data context
block_structure.set_transformer_block_field(
block_key,
self,
'special_exam',
timed_exam_attempt_context,
)
root_key = block_structure.root_block_usage_key
course_key = root_key.course_key
course_key = block_structure.root_block_usage_key.course_key
user_can_skip = EntranceExamConfiguration.user_can_skip_entrance_exam(usage_info.user, course_key)
exam_id = block_structure.get_xblock_field(root_key, 'entrance_exam_id')
required_content = milestones_helpers.get_required_content(course_key, usage_info.user)
if user_can_skip:
required_content = [content for content in required_content if not content == exam_id]
def user_gated_from_block(block_key):
"""
......@@ -103,12 +65,11 @@ class MilestonesTransformer(BlockStructureTransformer):
return False
elif self.has_pending_milestones_for_user(block_key, usage_info):
return True
elif required_content:
if block_key.block_type == 'chapter' and unicode(block_key) not in required_content:
return True
elif self.gated_by_required_content(block_key, block_structure, user_can_skip, required_content):
return True
elif (settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and
(self.is_special_exam(block_key, block_structure) and
not self.can_view_special_exams)):
not self.include_special_exams)):
return True
return False
......@@ -116,7 +77,7 @@ class MilestonesTransformer(BlockStructureTransformer):
if user_gated_from_block(block_key):
block_structure.remove_block(block_key, False)
else:
add_special_exam_info(block_key)
self.add_special_exam_info(block_key, block_structure, usage_info)
@staticmethod
def is_special_exam(block_key, block_structure):
......@@ -142,3 +103,50 @@ class MilestonesTransformer(BlockStructureTransformer):
'requires',
usage_info.user.id
))
def add_special_exam_info(self, block_key, block_structure, usage_info):
"""
Adds special exam information to course blocks.
"""
if self.is_special_exam(block_key, block_structure):
# call into edx_proctoring subsystem to get relevant special exam information
#
# This will return None, if (user, course_id, content_id) is not applicable
special_exam_attempt_context = None
try:
special_exam_attempt_context = get_attempt_status_summary(
usage_info.user.id,
unicode(block_key.course_key),
unicode(block_key)
)
except ProctoredExamNotFoundException as ex:
log.exception(ex)
if special_exam_attempt_context:
# yes, user has proctoring context about
# this level of the courseware
# so add to the accordion data context
block_structure.set_transformer_block_field(
block_key,
self,
'special_exam_info',
special_exam_attempt_context,
)
@staticmethod
def gated_by_required_content(block_key, block_structure, user_can_skip, required_content):
"""
Returns True if the current block associated with the block_key should be gated by the given required_content.
Returns False otherwise.
"""
if not required_content:
return False
exam_id = block_structure.get_xblock_field(block_structure.root_block_usage_key, 'entrance_exam_id')
if user_can_skip:
required_content = [content for content in required_content if not content == exam_id]
if block_key.block_type == 'chapter' and unicode(block_key) not in required_content:
return True
return False
......@@ -186,7 +186,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
self.setup_gated_section(self.blocks['H'], self.blocks['A'])
self.get_blocks_and_check_against_expected(self.staff, expected_blocks)
def test_can_view_special(self):
def test_special_exams(self):
"""
When the block structure transformers are set to allow users to view special exams,
ensure that we can see the special exams and not any of the otherwise gated blocks.
......
......@@ -51,26 +51,26 @@ from django.utils.translation import ugettext as _
if subsection.get('due') is None:
data_string = subsection.get('format')
else:
if 'special_exam' in subsection:
if 'special_exam_info' in subsection:
data_string = _('due {date}')
else:
data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection.get('format'))
%>
% if subsection.get('format') or 'special_exam' in subsection:
% if subsection.get('format') or 'special_exam_info' in subsection:
<span class="subtitle">
% if 'special_exam' in subsection:
## Display the proctored exam status icon and status message
<span
class="menu-icon icon fa ${subsection['special_exam'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam'].get('status', 'eligible')}"
class="menu-icon icon fa ${subsection['special_exam_info'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam_info'].get('status', 'eligible')}"
aria-hidden="true"
></span>
<span class="subtitle-name">
${subsection['special_exam'].get('short_description', '')}
${subsection['special_exam_info'].get('short_description', '')}
</span>
## completed proctored exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not subsection['special_exam'].get('in_completed_state', False):
% if not subsection['special_exam_info'].get('in_completed_state', False):
<span
class="localized-datetime subtitle-name"
data-datetime="${subsection.get('due')}"
......
......@@ -47,6 +47,22 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
ItemFactory.create(category='vertical', parent_location=section2.location)
course.last_accessed = None
cls.courses.append(course)
course = CourseFactory.create()
with cls.store.bulk_operations(course.id):
chapter = ItemFactory.create(category='chapter', parent_location=course.location)
section = ItemFactory.create(
category='sequential',
parent_location=chapter.location,
due=datetime.datetime.now(),
graded=True,
format='Homework',
)
ItemFactory.create(category='vertical', parent_location=section.location)
course.last_accessed = section.url_name
cls.courses.append(course)
@classmethod
def setUpTestData(cls):
"""Set up and enroll our fake user in the course."""
......@@ -70,14 +86,14 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
self.assertEqual(response.status_code, 200)
response_content = response.content.decode("utf-8")
if course.last_accessed is not None:
self.assertIn('Resume Course', response_content)
else:
self.assertNotIn('Resume Course', response_content)
self.assertIn('Resume Course', response_content)
for chapter in course.children:
self.assertIn(chapter.display_name, response_content)
for section in chapter.children:
self.assertIn(section.display_name, response_content)
if section.graded:
self.assertIn(section.due, response_content)
self.assertIn(section.format, response_content)
for vertical in section.children:
self.assertNotIn(vertical.display_name, response_content)
......
......@@ -48,7 +48,7 @@ class CourseOutlineFragmentView(EdxFragmentView):
course_usage_key,
user=request.user,
nav_depth=3,
requested_fields=['children', 'display_name', 'type', 'due', 'graded', 'special_exam'],
requested_fields=['children', 'display_name', 'type', 'due', 'graded', 'special_exam_info', 'format'],
block_types_filter=['course', 'chapter', 'sequential']
)
......
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