Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
9e734442
Commit
9e734442
authored
Mar 28, 2017
by
Diana Huang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial version of new transformer.
parent
c58b3a14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
209 additions
and
33 deletions
+209
-33
common/djangoapps/util/milestones_helpers.py
+2
-2
common/test/acceptance/pages/lms/courseware.py
+1
-0
common/test/acceptance/tests/lms/test_lms.py
+42
-7
common/test/acceptance/tests/lms/test_lms_entrance_exams.py
+29
-1
common/test/acceptance/tests/studio/test_import_export.py
+17
-6
lms/djangoapps/course_api/blocks/api.py
+5
-1
lms/djangoapps/course_api/blocks/tests/test_api.py
+2
-2
lms/djangoapps/course_api/blocks/transformers/__init__.py
+3
-0
lms/djangoapps/course_api/blocks/transformers/milestones.py
+75
-9
lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py
+29
-2
lms/djangoapps/courseware/entrance_exams.py
+1
-1
lms/djangoapps/courseware/module_render.py
+1
-1
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
+1
-0
openedx/features/course_experience/views/course_outline.py
+1
-1
No files found.
common/djangoapps/util/milestones_helpers.py
View file @
9e734442
...
...
@@ -207,7 +207,7 @@ def remove_course_milestones(course_key, user, relationship):
milestones_api
.
remove_user_milestone
({
'id'
:
user
.
id
},
milestone
)
def
get_required_content
(
course
,
user
):
def
get_required_content
(
course
_key
,
user
):
"""
Queries milestones subsystem to see if the specified course is gated on one or more milestones,
and if those milestones can be fulfilled via completion of a particular course content module
...
...
@@ -217,7 +217,7 @@ def get_required_content(course, user):
# Get all of the outstanding milestones for this course, for this user
try
:
milestone_paths
=
get_course_milestones_fulfillment_paths
(
unicode
(
course
.
id
),
unicode
(
course
_key
),
serialize_user
(
user
)
)
except
InvalidMilestoneRelationshipTypeException
:
...
...
common/test/acceptance/pages/lms/courseware.py
View file @
9e734442
...
...
@@ -30,6 +30,7 @@ class CoursewarePage(CoursePage):
def
is_browser_on_page
(
self
):
return
self
.
q
(
css
=
'.course-content'
)
.
present
# TODO: TNL-6546: Remove and find callers
@property
def
chapter_count_in_navigation
(
self
):
"""
...
...
common/test/acceptance/tests/lms/test_lms.py
View file @
9e734442
...
...
@@ -1233,7 +1233,7 @@ class EntranceExamTest(UniqueCourseTest):
self
.
course_info
[
'run'
],
self
.
course_info
[
'display_name'
]
)
.
install
()
self
.
course
ware_page
=
Coursewar
ePage
(
self
.
browser
,
self
.
course_id
)
self
.
course
_home_page
=
CourseHom
ePage
(
self
.
browser
,
self
.
course_id
)
self
.
settings_page
=
SettingsPage
(
self
.
browser
,
self
.
course_info
[
'org'
],
...
...
@@ -1246,18 +1246,53 @@ class EntranceExamTest(UniqueCourseTest):
def
test_entrance_exam_section
(
self
):
"""
Scenario: Any course that is enabled for an entrance exam, should have
entrance exam section in the course outline.
Given that I visit the course outline
And entrance exams are not yet enabled
Then I should not see an "Entrance Exam" section
When I log in as staff
And enable entrance exams
And I visit the course outline again as student
Then there should be an "Entrance Exam" chapter.'
"""
# visit the course outline and make sure there is no "Entrance Exam" section.
self
.
course_home_page
.
visit
()
self
.
assertFalse
(
'Entrance Exam'
in
self
.
course_home_page
.
outline
.
sections
.
keys
())
# Logout and login as a staff.
LogoutPage
(
self
.
browser
)
.
visit
()
AutoAuthPage
(
self
.
browser
,
course_id
=
self
.
course_id
,
staff
=
True
)
.
visit
()
# visit course settings page and set/enabled entrance exam for that course.
self
.
settings_page
.
visit
()
self
.
settings_page
.
entrance_exam_field
.
click
()
self
.
settings_page
.
save_changes
()
# Logout and login as a student.
LogoutPage
(
self
.
browser
)
.
visit
()
AutoAuthPage
(
self
.
browser
,
course_id
=
self
.
course_id
,
staff
=
False
)
.
visit
()
# visit the course outline and make sure there is an "Entrance Exam" section.
self
.
course_home_page
.
visit
()
self
.
assertTrue
(
'Entrance Exam'
in
self
.
course_home_page
.
outline
.
sections
.
keys
())
# TODO: TNL-6546: Remove test
def
test_entrance_exam_section_2
(
self
):
"""
Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at course
page.
Given that I am on the course page
When I view the course that has an entrance exam
Then there should be an "Entrance Exam" chapter.'
"""
courseware_page
=
CoursewarePage
(
self
.
browser
,
self
.
course_id
)
entrance_exam_link_selector
=
'.accordion .course-navigation .chapter .group-heading'
# visit course page and make sure there is not entrance exam chapter.
self
.
courseware_page
.
visit
()
self
.
courseware_page
.
wait_for_page
()
courseware_page
.
visit
()
courseware_page
.
wait_for_page
()
self
.
assertFalse
(
element_has_text
(
page
=
self
.
courseware_page
,
page
=
courseware_page
,
css_selector
=
entrance_exam_link_selector
,
text
=
'Entrance Exam'
))
...
...
@@ -1276,10 +1311,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
.
courseware_page
.
visit
()
self
.
courseware_page
.
wait_for_page
()
courseware_page
.
visit
()
courseware_page
.
wait_for_page
()
self
.
assertTrue
(
element_has_text
(
page
=
self
.
courseware_page
,
page
=
courseware_page
,
css_selector
=
entrance_exam_link_selector
,
text
=
'Entrance Exam'
))
...
...
common/test/acceptance/tests/lms/test_lms_entrance_exams.py
View file @
9e734442
...
...
@@ -6,6 +6,7 @@ from textwrap import dedent
from
common.test.acceptance.tests.helpers
import
UniqueCourseTest
from
common.test.acceptance.pages.studio.auto_auth
import
AutoAuthPage
from
common.test.acceptance.pages.lms.course_home
import
CourseHomePage
from
common.test.acceptance.pages.lms.courseware
import
CoursewarePage
from
common.test.acceptance.pages.lms.problem
import
ProblemPage
from
common.test.acceptance.fixtures.course
import
CourseFixture
,
XBlockFixtureDesc
...
...
@@ -92,6 +93,8 @@ class EntranceExamPassTest(EntranceExamTest):
When I pass entrance exam
Then I can see complete TOC of course
And I can see message indicating my pass status
When I switch to course home page
Then I see 2 sections
"""
self
.
courseware_page
.
visit
()
problem_page
=
ProblemPage
(
self
.
browser
)
...
...
@@ -102,4 +105,29 @@ class EntranceExamPassTest(EntranceExamTest):
problem_page
.
click_submit
()
self
.
courseware_page
.
wait_for_page
()
self
.
assertTrue
(
self
.
courseware_page
.
has_passed_message
())
self
.
assertEqual
(
self
.
courseware_page
.
chapter_count_in_navigation
,
2
)
course_home_page
=
CourseHomePage
(
self
.
browser
,
self
.
course_id
)
course_home_page
.
visit
()
self
.
assertEqual
(
course_home_page
.
outline
.
num_sections
,
2
)
# TODO: TNL-6546: Delete test using outline on courseware
def
test_course_is_unblocked_as_soon_as_student_passes_entrance_exam_2
(
self
):
"""
Scenario: Ensure that entrance exam status message is updated and courseware is unblocked as soon as
student passes entrance exam.
Given I have a course with entrance exam as pre-requisite
When I pass entrance exam
Then I can see complete TOC of course
And I can see message indicating my pass status
"""
self
.
courseware_page
.
visit
()
problem_page
=
ProblemPage
(
self
.
browser
)
self
.
assertEqual
(
problem_page
.
wait_for_page
()
.
problem_name
,
'HEIGHT OF EIFFEL TOWER'
)
self
.
assertTrue
(
self
.
courseware_page
.
has_entrance_exam_message
())
self
.
assertFalse
(
self
.
courseware_page
.
has_passed_message
())
problem_page
.
click_choice
(
'choice_1'
)
problem_page
.
click_submit
()
self
.
courseware_page
.
wait_for_page
()
self
.
assertTrue
(
self
.
courseware_page
.
has_passed_message
())
self
.
assertEqual
(
self
.
courseware_page
.
num_sections
,
2
)
common/test/acceptance/tests/studio/test_import_export.py
View file @
9e734442
...
...
@@ -14,6 +14,7 @@ from common.test.acceptance.pages.studio.import_export import (
ImportCoursePage
)
from
common.test.acceptance.pages.studio.library
import
LibraryEditPage
from
common.test.acceptance.pages.studio.overview
import
CourseOutlinePage
from
common.test.acceptance.pages.lms.course_home
import
CourseHomePage
from
common.test.acceptance.pages.lms.courseware
import
CoursewarePage
from
common.test.acceptance.pages.lms.staff_view
import
StaffCoursewarePage
...
...
@@ -282,9 +283,13 @@ class TestEntranceExamCourseImport(ImportTestMixin, StudioCourseTest):
When I visit the import page
And I upload a course that has an entrance exam section named 'Entrance Exam'
And I visit the course outline page again
The section named 'Entrance Exam' should now be available.
And when I switch the view mode to student view and Visit CourseWare
Then I see one section in the sidebar that is 'Entrance Exam'
The section named 'Entrance Exam' should now be available
When I visit the LMS Course Home page
Then I should see a section named 'Section' or 'Entrance Exam'
When I switch the view mode to student view
Then I should only see a section named 'Entrance Exam'
When I visit the courseware page
Then a message regarding the 'Entrance Exam'
"""
self
.
landing_page
.
visit
()
# Should not exist yet.
...
...
@@ -300,10 +305,16 @@ class TestEntranceExamCourseImport(ImportTestMixin, StudioCourseTest):
self
.
landing_page
.
section
(
"Section"
)
self
.
landing_page
.
view_live
()
course_home
=
CourseHomePage
(
self
.
browser
,
self
.
course_id
)
course_home
.
visit
()
self
.
assertEqual
(
course_home
.
outline
.
num_sections
,
2
)
course_home
.
preview
.
set_staff_view_mode
(
'Student'
)
self
.
assertEqual
(
course_home
.
outline
.
num_sections
,
1
)
courseware
=
CoursewarePage
(
self
.
browser
,
self
.
course_id
)
courseware
.
wait_for_page
()
StaffCoursewarePage
(
self
.
browser
,
self
.
course_id
)
.
set_staff_view_mode
(
'Learner'
)
self
.
assertEqual
(
courseware
.
num_sections
,
1
)
courseware
.
visit
()
StaffCoursewarePage
(
self
.
browser
,
self
.
course_id
)
.
set_staff_view_mode
(
'Student'
)
self
.
assertIn
(
"To access course materials, you must score"
,
courseware
.
entrance_exam_message_selector
.
text
[
0
]
)
...
...
lms/djangoapps/course_api/blocks/api.py
View file @
9e734442
...
...
@@ -51,8 +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
if
user
is
not
None
:
transformers
+=
COURSE_BLOCK_ACCESS_TRANSFORMERS
+
[
MilestonesTransformer
(),
HiddenContentTransformer
()]
transformers
+=
COURSE_BLOCK_ACCESS_TRANSFORMERS
transformers
+=
[
MilestonesTransformer
(
can_view_special_exam
),
HiddenContentTransformer
()]
transformers
+=
[
BlocksAPITransformer
(
block_counts
,
...
...
lms/djangoapps/course_api/blocks/tests/test_api.py
View file @
9e734442
...
...
@@ -146,7 +146,7 @@ class TestGetBlocksQueryCounts(SharedModuleStoreTestCase):
self
.
_get_blocks
(
course
,
expected_mongo_queries
=
0
,
expected_sql_queries
=
5
if
with_storage_backing
else
4
,
expected_sql_queries
=
6
if
with_storage_backing
else
5
,
)
@ddt.data
(
...
...
@@ -164,5 +164,5 @@ class TestGetBlocksQueryCounts(SharedModuleStoreTestCase):
self
.
_get_blocks
(
course
,
expected_mongo_queries
,
expected_sql_queries
=
1
3
if
with_storage_backing
else
5
,
expected_sql_queries
=
1
4
if
with_storage_backing
else
6
,
)
lms/djangoapps/course_api/blocks/transformers/__init__.py
View file @
9e734442
...
...
@@ -6,6 +6,7 @@ from lms.djangoapps.course_blocks.transformers.visibility import VisibilityTrans
from
.student_view
import
StudentViewTransformer
from
.block_counts
import
BlockCountsTransformer
from
.navigation
import
BlockNavigationTransformer
from
.milestones
import
MilestonesTransformer
class
SupportedFieldType
(
object
):
...
...
@@ -44,6 +45,8 @@ SUPPORTED_FIELDS = [
# 'student_view_multi_device'
SupportedFieldType
(
StudentViewTransformer
.
STUDENT_VIEW_MULTI_DEVICE
,
StudentViewTransformer
),
SupportedFieldType
(
'special_exam'
,
MilestonesTransformer
),
# set the block_field_name to None so the entire data for the transformer is serialized
SupportedFieldType
(
None
,
BlockCountsTransformer
,
BlockCountsTransformer
.
BLOCK_COUNTS
),
...
...
lms/djangoapps/course_api/blocks/transformers/milestones.py
View file @
9e734442
...
...
@@ -2,16 +2,22 @@
Milestones Transformer
"""
import
logging
from
django.conf
import
settings
from
openedx.core.djangoapps.content.block_structure.transformer
import
(
BlockStructureTransformer
,
FilteringTransformerMixin
,
)
from
edx_proctoring.exceptions
import
ProctoredExamNotFoundException
from
edx_proctoring.api
import
get_attempt_status_summary
from
student.models
import
EntranceExamConfiguration
from
util
import
milestones_helpers
log
=
logging
.
getLogger
(
__name__
)
class
MilestonesTransformer
(
FilteringTransformerMixin
,
BlockStructureTransformer
):
class
MilestonesTransformer
(
BlockStructureTransformer
):
"""
Excludes all special exams (timed, proctored, practice proctored) from the student view.
Excludes all blocks with unfulfilled milestones from the student view.
...
...
@@ -23,6 +29,9 @@ class MilestonesTransformer(FilteringTransformerMixin, BlockStructureTransformer
def
name
(
cls
):
return
"milestones"
def
__init__
(
self
,
can_view_special_exams
=
True
):
self
.
can_view_special_exams
=
can_view_special_exams
@classmethod
def
collect
(
cls
,
block_structure
):
"""
...
...
@@ -35,22 +44,79 @@ class MilestonesTransformer(FilteringTransformerMixin, BlockStructureTransformer
block_structure
.
request_xblock_fields
(
'is_proctored_enabled'
)
block_structure
.
request_xblock_fields
(
'is_practice_exam'
)
block_structure
.
request_xblock_fields
(
'is_timed_exam'
)
block_structure
.
request_xblock_fields
(
'entrance_exam_id'
)
def
transform
(
self
,
usage_info
,
block_structure
):
"""
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
,
)
def
transform_block_filters
(
self
,
usage_info
,
block_structure
):
if
usage_info
.
has_staff_access
:
return
[
block_structure
.
create_universal_filter
()]
root_key
=
block_structure
.
root_block_usage_key
course_key
=
root_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
):
"""
Checks whether the user is gated from accessing this block, first via special exams,
then via a general milestones check.
"""
return
(
settings
.
FEATURES
.
get
(
'ENABLE_SPECIAL_EXAMS'
,
False
)
and
self
.
is_special_exam
(
block_key
,
block_structure
)
)
or
self
.
has_pending_milestones_for_user
(
block_key
,
usage_info
)
if
usage_info
.
has_staff_access
:
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
(
settings
.
FEATURES
.
get
(
'ENABLE_SPECIAL_EXAMS'
,
False
)
and
(
self
.
is_special_exam
(
block_key
,
block_structure
)
and
not
self
.
can_view_special_exams
)):
return
True
return
False
return
[
block_structure
.
create_removal_filter
(
user_gated_from_block
)]
for
block_key
in
block_structure
.
topological_traversal
():
if
user_gated_from_block
(
block_key
):
block_structure
.
remove_block
(
block_key
,
False
)
else
:
add_special_exam_info
(
block_key
)
@staticmethod
def
is_special_exam
(
block_key
,
block_structure
):
...
...
lms/djangoapps/course_api/blocks/transformers/tests/test_milestones.py
View file @
9e734442
...
...
@@ -9,6 +9,7 @@ from gating import api as lms_gating_api
from
lms.djangoapps.course_blocks.transformers.tests.helpers
import
CourseStructureTestCase
from
milestones.tests.utils
import
MilestonesTestCaseMixin
from
openedx.core.lib.gating
import
api
as
gating_api
from
openedx.core.djangoapps.content.block_structure.transformers
import
BlockStructureTransformers
from
student.tests.factories
import
CourseEnrollmentFactory
from
..milestones
import
MilestonesTransformer
...
...
@@ -38,6 +39,8 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
# Enroll user in course.
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
is_active
=
True
)
self
.
transformers
=
BlockStructureTransformers
([
self
.
TRANSFORMER_CLASS_TO_TEST
(
False
)])
def
setup_gated_section
(
self
,
gated_block
,
gating_block
):
"""
Test helper to create a gating requirement.
...
...
@@ -157,7 +160,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
self
.
course
.
enable_subsection_gating
=
True
self
.
setup_gated_section
(
self
.
blocks
[
gated_block_ref
],
self
.
blocks
[
gating_block_ref
])
with
self
.
assertNumQueries
(
6
):
with
self
.
assertNumQueries
(
8
):
self
.
get_blocks_and_check_against_expected
(
self
.
user
,
expected_blocks_before_completion
)
# clear the request cache to simulate a new request
...
...
@@ -171,7 +174,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
self
.
user
,
)
with
self
.
assertNumQueries
(
6
):
with
self
.
assertNumQueries
(
8
):
self
.
get_blocks_and_check_against_expected
(
self
.
user
,
self
.
ALL_BLOCKS_EXCEPT_SPECIAL
)
def
test_staff_access
(
self
):
...
...
@@ -183,6 +186,30 @@ 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
):
"""
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.
"""
self
.
transformers
=
BlockStructureTransformers
([
self
.
TRANSFORMER_CLASS_TO_TEST
(
True
)])
self
.
course
.
enable_subsection_gating
=
True
self
.
setup_gated_section
(
self
.
blocks
[
'H'
],
self
.
blocks
[
'A'
])
expected_blocks
=
(
'course'
,
'A'
,
'B'
,
'C'
,
'ProctoredExam'
,
'D'
,
'E'
,
'PracticeExam'
,
'F'
,
'G'
,
'TimedExam'
,
'J'
,
'K'
)
self
.
get_blocks_and_check_against_expected
(
self
.
user
,
expected_blocks
)
# clear the request cache to simulate a new request
self
.
clear_caches
()
# this call triggers reevaluation of prerequisites fulfilled by the gating block.
with
patch
(
'gating.api._get_subsection_percentage'
,
Mock
(
return_value
=
100
)):
lms_gating_api
.
evaluate_prerequisite
(
self
.
course
,
Mock
(
location
=
self
.
blocks
[
'A'
]
.
location
),
self
.
user
,
)
self
.
get_blocks_and_check_against_expected
(
self
.
user
,
self
.
ALL_BLOCKS
)
def
get_blocks_and_check_against_expected
(
self
,
user
,
expected_blocks
):
"""
Calls the course API as the specified user and checks the
...
...
lms/djangoapps/courseware/entrance_exams.py
View file @
9e734442
...
...
@@ -55,7 +55,7 @@ def get_entrance_exam_content(user, course):
"""
Get the entrance exam content information (ie, chapter module)
"""
required_content
=
get_required_content
(
course
,
user
)
required_content
=
get_required_content
(
course
.
id
,
user
)
exam_module
=
None
for
content
in
required_content
:
...
...
lms/djangoapps/courseware/module_render.py
View file @
9e734442
...
...
@@ -162,7 +162,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_
# Check for content which needs to be completed
# before the rest of the content is made available
required_content
=
milestones_helpers
.
get_required_content
(
course
,
user
)
required_content
=
milestones_helpers
.
get_required_content
(
course
.
id
,
user
)
# The user may not actually have to complete the entrance exam, if one is required
if
user_can_skip_entrance_exam
(
user
,
course
):
...
...
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
View file @
9e734442
...
...
@@ -27,6 +27,7 @@ from django.utils.translation import ugettext as _
</div>
<ol
class=
"outline-item focusable"
role=
"group"
tabindex=
"0"
>
% for subsection in section.get('children') or []:
${ subsection.get('special_exam', '') }
<li
class=
"subsection ${ 'current' if subsection['current'] else '' }"
role=
"treeitem"
...
...
openedx/features/course_experience/views/course_outline.py
View file @
9e734442
...
...
@@ -47,7 +47,7 @@ class CourseOutlineFragmentView(EdxFragmentView):
course_usage_key
,
user
=
request
.
user
,
nav_depth
=
3
,
requested_fields
=
[
'children'
,
'display_name'
,
'type'
],
requested_fields
=
[
'children'
,
'display_name'
,
'type'
,
'due'
,
'graded'
,
'special_exam'
],
block_types_filter
=
[
'course'
,
'chapter'
,
'sequential'
]
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment