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
3b31270e
Commit
3b31270e
authored
Apr 25, 2017
by
Robert Raposa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix Start Course vs Resume Course using Course Blocks.
parent
4a4f135f
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
277 additions
and
126 deletions
+277
-126
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
+0
-1
lms/djangoapps/course_blocks/transformers/library_content.py
+3
-28
lms/djangoapps/course_blocks/utils.py
+32
-0
lms/djangoapps/courseware/courses.py
+0
-25
lms/djangoapps/courseware/views/views.py
+24
-3
openedx/features/course_experience/templates/course_experience/course-home-fragment.html
+11
-9
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
+3
-3
openedx/features/course_experience/tests/views/test_course_outline.py
+73
-15
openedx/features/course_experience/utils.py
+78
-0
openedx/features/course_experience/views/course_home.py
+46
-7
openedx/features/course_experience/views/course_outline.py
+7
-35
No files found.
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py
View file @
3b31270e
...
...
@@ -23,7 +23,6 @@ from xmodule.modulestore.django import modulestore, clear_existing_modulestores,
from
xmodule.modulestore.tests.mongo_connection
import
MONGO_PORT_NUM
,
MONGO_HOST
from
xmodule.modulestore.tests.factories
import
XMODULE_FACTORY_LOCK
from
openedx.core.djangoapps.bookmarks.signals
import
trigger_update_xblocks_cache_task
from
openedx.core.djangolib.testing.utils
import
CacheIsolationMixin
,
CacheIsolationTestCase
...
...
lms/djangoapps/course_blocks/transformers/library_content.py
View file @
3b31270e
...
...
@@ -12,6 +12,8 @@ from xmodule.modulestore.django import modulestore
from
eventtracking
import
tracker
from
track
import
contexts
from
..utils
import
get_student_module_as_dict
class
ContentLibraryTransformer
(
FilteringTransformerMixin
,
BlockStructureTransformer
):
"""
...
...
@@ -78,12 +80,7 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
max_count
=
block_structure
.
get_xblock_field
(
block_key
,
'max_count'
)
# Retrieve "selected" json from LMS MySQL database.
module
=
self
.
_get_student_module
(
usage_info
.
user
,
usage_info
.
course_key
,
block_key
)
if
module
:
state_dict
=
json
.
loads
(
module
.
state
)
else
:
state_dict
=
{}
state_dict
=
get_student_module_as_dict
(
usage_info
.
user
,
usage_info
.
course_key
,
block_key
)
for
selected_block
in
state_dict
.
get
(
'selected'
,
[]):
# Add all selected entries for this user for this
# library module to the selected list.
...
...
@@ -135,28 +132,6 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
return
[
block_structure
.
create_removal_filter
(
check_child_removal
)]
@classmethod
def
_get_student_module
(
cls
,
user
,
course_key
,
block_key
):
"""
Get the student module for the given user for the given block.
Arguments:
user (User)
course_key (CourseLocator)
block_key (BlockUsageLocator)
Returns:
StudentModule if exists, or None.
"""
try
:
return
StudentModule
.
objects
.
get
(
student
=
user
,
course_id
=
course_key
,
module_state_key
=
block_key
,
)
except
StudentModule
.
DoesNotExist
:
return
None
def
_publish_events
(
self
,
block_structure
,
location
,
previous_count
,
max_count
,
block_keys
,
user_id
):
"""
Helper method to publish events for analytics purposes
...
...
lms/djangoapps/course_blocks/utils.py
0 → 100644
View file @
3b31270e
"""
Common utilities for use along with the course blocks.
"""
import
json
from
courseware.models
import
StudentModule
def
get_student_module_as_dict
(
user
,
course_key
,
block_key
):
"""
Get the student module as a dict for the given user for the given block.
Arguments:
user (User)
course_key (CourseLocator)
block_key (BlockUsageLocator)
Returns:
StudentModule as a (possibly empty) dict.
"""
try
:
student_module
=
StudentModule
.
objects
.
get
(
student
=
user
,
course_id
=
course_key
,
module_state_key
=
block_key
,
)
except
StudentModule
.
DoesNotExist
:
student_module
=
None
if
student_module
:
return
json
.
loads
(
student_module
.
state
)
else
:
return
{}
lms/djangoapps/courseware/courses.py
View file @
3b31270e
...
...
@@ -518,28 +518,3 @@ def get_current_child(xmodule, min_depth=None, requested_child=None):
child
=
_get_default_child_module
(
children
)
return
child
def
get_last_accessed_courseware
(
course
,
request
,
user
):
"""
Returns a tuple containing the courseware module (URL, id) that the user last accessed,
or (None, None) if it cannot be found.
"""
# TODO: convert this method to use the Course Blocks API
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
course
.
id
,
request
.
user
,
course
,
depth
=
2
)
course_module
=
get_module_for_descriptor
(
user
,
request
,
course
,
field_data_cache
,
course
.
id
,
course
=
course
)
chapter_module
=
get_current_child
(
course_module
)
if
chapter_module
is
not
None
:
section_module
=
get_current_child
(
chapter_module
)
if
section_module
is
not
None
:
url
=
reverse
(
'courseware_section'
,
kwargs
=
{
'course_id'
:
unicode
(
course
.
id
),
'chapter'
:
chapter_module
.
url_name
,
'section'
:
section_module
.
url_name
})
return
(
url
,
section_module
.
url_name
)
return
(
None
,
None
)
lms/djangoapps/courseware/views/views.py
View file @
3b31270e
...
...
@@ -66,7 +66,7 @@ from courseware.courses import (
get_course_by_id
,
get_course_overview_with_access
,
get_course_with_access
,
get_
last_accessed_courseware
,
get_
current_child
,
get_permission_for_course_about
,
get_studio_url
,
sort_by_announcement
,
...
...
@@ -97,7 +97,6 @@ from openedx.features.course_experience import (
UNIFIED_COURSE_VIEW_FLAG
,
)
from
openedx.features.enterprise_support.api
import
data_sharing_consent_required
from
shoppingcart.models
import
CourseRegistrationCode
from
shoppingcart.utils
import
is_shopping_cart_enabled
from
student.models
import
UserTestGroup
,
CourseEnrollment
from
survey.utils
import
must_answer_survey
...
...
@@ -249,6 +248,28 @@ def course_info(request, course_id):
Assumes the course_id is in a valid format.
"""
def
get_last_accessed_courseware
(
course
,
request
,
user
):
"""
Returns the courseware module URL that the user last accessed, or None if it cannot be found.
"""
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
course
.
id
,
request
.
user
,
course
,
depth
=
2
)
course_module
=
get_module_for_descriptor
(
user
,
request
,
course
,
field_data_cache
,
course
.
id
,
course
=
course
)
chapter_module
=
get_current_child
(
course_module
)
if
chapter_module
is
not
None
:
section_module
=
get_current_child
(
chapter_module
)
if
section_module
is
not
None
:
url
=
reverse
(
'courseware_section'
,
kwargs
=
{
'course_id'
:
unicode
(
course
.
id
),
'chapter'
:
chapter_module
.
url_name
,
'section'
:
section_module
.
url_name
})
return
url
return
None
# If the unified course experience is enabled, redirect to the "Course" tab
if
waffle
.
flag_is_active
(
request
,
UNIFIED_COURSE_EXPERIENCE_FLAG
):
return
redirect
(
reverse
(
course_home_url_name
(
request
),
args
=
[
course_id
]))
...
...
@@ -343,7 +364,7 @@ def course_info(request, course_id):
# Get the URL of the user's last position in order to display the 'where you were last' message
context
[
'last_accessed_courseware_url'
]
=
None
if
SelfPacedConfiguration
.
current
()
.
enable_course_home_improvements
:
context
[
'last_accessed_courseware_url'
]
,
_
=
get_last_accessed_courseware
(
course
,
request
,
user
)
context
[
'last_accessed_courseware_url'
]
=
get_last_accessed_courseware
(
course
,
request
,
user
)
now
=
datetime
.
now
(
UTC
())
effective_start
=
_adjust_start_date_for_beta_testers
(
user
,
course
,
course_key
)
...
...
openedx/features/course_experience/templates/course_experience/course-home-fragment.html
View file @
3b31270e
...
...
@@ -50,17 +50,19 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
% endif
<div
class=
"form-actions"
>
% if not waffle.flag_is_active(request, UNIFIED_COURSE_EXPERIENCE_FLAG):
<a
class=
"btn action-show-bookmarks"
href=
"${reverse('openedx.course_bookmarks.home', args=[course
.id
])}"
>
<a
class=
"btn action-show-bookmarks"
href=
"${reverse('openedx.course_bookmarks.home', args=[course
_key
])}"
>
${_("Bookmarks")}
</a>
% endif
<a
class=
"btn btn-brand action-resume-course"
href=
"${reverse('courseware', kwargs={'course_id': unicode(course.id.to_deprecated_string())})}"
>
% if has_visited_course:
${_("Resume Course")}
% else:
${_("Start Course")}
% endif
</a>
% if resume_course_url:
<a
class=
"btn btn-brand action-resume-course"
href=
"${resume_course_url}"
>
% if has_visited_course:
${_("Resume Course")}
% else:
${_("Start Course")}
% endif
</a>
% endif
</div>
</div>
</header>
...
...
@@ -75,7 +77,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
<h3
class=
"hd-6"
>
${_("Course Tools")}
</h3>
<ul
class=
"list-unstyled"
>
<li>
<a
class=
"action-show-bookmarks"
href=
"${reverse('openedx.course_bookmarks.home', args=[course
.id
])}"
>
<a
class=
"action-show-bookmarks"
href=
"${reverse('openedx.course_bookmarks.home', args=[course
_key
])}"
>
<span
class=
"icon fa fa-bookmark"
aria-hidden=
"true"
></span>
${_("Bookmarks")}
</a>
...
...
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
View file @
3b31270e
...
...
@@ -27,9 +27,9 @@ from openedx.core.djangolib.markup import HTML, Text
<h3>
${ section['display_name'] }
</h3>
</div>
<ol
class=
"outline-item focusable"
role=
"group"
tabindex=
"0"
>
% for subsection in section.get('children'
) or []
:
% for subsection in section.get('children'
, [])
:
<li
class=
"subsection ${ 'current' if subsection['
current
'] else '' }"
class=
"subsection ${ 'current' if subsection['
last_accessed
'] else '' }"
role=
"treeitem"
tabindex=
"-1"
aria-expanded=
"true"
...
...
@@ -106,7 +106,7 @@ from openedx.core.djangolib.markup import HTML, Text
</div>
<!-- /subsection-text -->
<div
class=
"subsection-actions"
>
## Resume button (if last visited section)
% if subsection['
current
']:
% if subsection['
last_accessed
']:
<span
class=
"sr-only"
>
${ _("This is your last visited course section.") }
</span>
<span
class=
"resume-right"
>
<b>
${ _("Resume Course") }
</b>
...
...
openedx/features/course_experience/tests/views/test_course_outline.py
View file @
3b31270e
...
...
@@ -3,10 +3,10 @@ Tests for the Course Outline view and supporting views.
"""
import
datetime
import
ddt
from
mock
import
patch
import
json
from
django.core.urlresolvers
import
reverse
from
pyquery
import
PyQuery
as
pq
from
courseware.tests.factories
import
StaffFactory
from
student.models
import
CourseEnrollment
...
...
@@ -37,9 +37,10 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
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
)
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
course
.
last_accessed
=
section
.
url_name
vertical
=
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
course
.
children
=
[
chapter
]
chapter
.
children
=
[
section
]
section
.
children
=
[
vertical
]
cls
.
courses
.
append
(
course
)
course
=
CourseFactory
.
create
()
...
...
@@ -47,10 +48,12 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
chapter
=
ItemFactory
.
create
(
category
=
'chapter'
,
parent_location
=
course
.
location
)
section
=
ItemFactory
.
create
(
category
=
'sequential'
,
parent_location
=
chapter
.
location
)
section2
=
ItemFactory
.
create
(
category
=
'sequential'
,
parent_location
=
chapter
.
location
)
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section2
.
location
)
course
.
last_accessed
=
None
vertical
=
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
vertical2
=
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section2
.
location
)
course
.
children
=
[
chapter
]
chapter
.
children
=
[
section
,
section2
]
section
.
children
=
[
vertical
]
section2
.
children
=
[
vertical2
]
cls
.
courses
.
append
(
course
)
course
=
CourseFactory
.
create
()
...
...
@@ -63,8 +66,10 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
graded
=
True
,
format
=
'Homework'
,
)
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
course
.
last_accessed
=
section
.
url_name
vertical
=
ItemFactory
.
create
(
category
=
'vertical'
,
parent_location
=
section
.
location
)
course
.
children
=
[
chapter
]
chapter
.
children
=
[
section
]
section
.
children
=
[
vertical
]
cls
.
courses
.
append
(
course
)
@classmethod
...
...
@@ -81,26 +86,79 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
super
(
TestCourseOutlinePage
,
self
)
.
setUp
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
TEST_PASSWORD
)
@patch
(
'openedx.features.course_experience.views.course_outline.get_last_accessed_courseware'
)
def
test_render
(
self
,
patched_get_last_accessed
):
def
test_outline_details
(
self
):
for
course
in
self
.
courses
:
patched_get_last_accessed
.
return_value
=
(
None
,
course
.
last_accessed
)
url
=
course_home_url
(
course
)
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_content
=
response
.
content
.
decode
(
"utf-8"
)
self
.
assert
In
(
'Resume Course'
,
response_content
)
self
.
assert
True
(
course
.
children
)
for
chapter
in
course
.
children
:
self
.
assertIn
(
chapter
.
display_name
,
response_content
)
self
.
assertTrue
(
chapter
.
children
)
for
section
in
chapter
.
children
:
self
.
assertIn
(
section
.
display_name
,
response_content
)
if
section
.
graded
:
self
.
assertIn
(
section
.
due
,
response_content
)
self
.
assertIn
(
section
.
due
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
,
response_content
)
self
.
assertIn
(
section
.
format
,
response_content
)
self
.
assertTrue
(
section
.
children
)
for
vertical
in
section
.
children
:
self
.
assertNotIn
(
vertical
.
display_name
,
response_content
)
def
test_start_course
(
self
):
"""
Tests that the start course button appears when the course has never been accessed.
Technically, this is a course home test, and not a course outline test, but checking the counts of
start/resume course should be done together to not get a false positive.
"""
course
=
self
.
courses
[
0
]
response
=
self
.
client
.
get
(
course_home_url
(
course
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertContains
(
response
,
'Start Course'
,
count
=
1
)
self
.
assertContains
(
response
,
'Resume Course'
,
count
=
0
)
content
=
pq
(
response
.
content
)
self
.
assertTrue
(
content
(
'.action-resume-course'
)
.
attr
(
'href'
)
.
endswith
(
'/course/'
+
course
.
url_name
))
def
test_resume_course
(
self
):
"""
Tests that two resume course buttons appear when the course has been accessed.
Technically, this is a mix of a course home and course outline test, but checking the counts of start/resume
course should be done together to not get a false positive.
"""
course
=
self
.
courses
[
0
]
# first navigate to a section to make it the last accessed
chapter
=
course
.
children
[
0
]
section
=
chapter
.
children
[
0
]
last_accessed_url
=
reverse
(
'courseware_section'
,
kwargs
=
{
'course_id'
:
course
.
id
.
to_deprecated_string
(),
'chapter'
:
chapter
.
url_name
,
'section'
:
section
.
url_name
,
}
)
self
.
assertEqual
(
200
,
self
.
client
.
get
(
last_accessed_url
)
.
status_code
)
# check resume course buttons
response
=
self
.
client
.
get
(
course_home_url
(
course
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertContains
(
response
,
'Start Course'
,
count
=
0
)
self
.
assertContains
(
response
,
'Resume Course'
,
count
=
2
)
content
=
pq
(
response
.
content
)
self
.
assertTrue
(
content
(
'.action-resume-course'
)
.
attr
(
'href'
)
.
endswith
(
'/sequential/'
+
section
.
url_name
))
class
TestCourseOutlinePreview
(
SharedModuleStoreTestCase
):
"""
...
...
openedx/features/course_experience/utils.py
0 → 100644
View file @
3b31270e
"""
Common utilities for the course experience, including course outline.
"""
from
lms.djangoapps.course_api.blocks.api
import
get_blocks
from
lms.djangoapps.course_blocks.utils
import
get_student_module_as_dict
from
opaque_keys.edx.keys
import
CourseKey
from
openedx.core.lib.cache_utils
import
memoized
from
xmodule.modulestore.django
import
modulestore
@memoized
def
get_course_outline_block_tree
(
request
,
course_id
):
"""
Returns the root block of the course outline, with children as blocks.
"""
def
populate_children
(
block
,
all_blocks
):
"""
Replace each child id with the full block for the child.
Given a block, replaces each id in its children array with the full
representation of that child, which will be looked up by id in the
passed all_blocks dict. Recursively do the same replacement for children
of those children.
"""
children
=
block
.
get
(
'children'
,
[])
for
i
in
range
(
len
(
children
)):
child_id
=
block
[
'children'
][
i
]
child_detail
=
populate_children
(
all_blocks
[
child_id
],
all_blocks
)
block
[
'children'
][
i
]
=
child_detail
return
block
def
set_lasted_accessed_default
(
block
):
"""
Set default of False for last_accessed on all blocks.
"""
block
[
'last_accessed'
]
=
False
for
child
in
block
.
get
(
'children'
,
[]):
set_lasted_accessed_default
(
child
)
def
mark_lasted_accessed
(
user
,
course_key
,
block
):
"""
Recursively marks the branch to the last accessed block.
"""
block_key
=
block
.
serializer
.
instance
student_module_dict
=
get_student_module_as_dict
(
user
,
course_key
,
block_key
)
last_accessed_child_position
=
student_module_dict
.
get
(
'position'
)
if
last_accessed_child_position
and
block
.
get
(
'children'
):
block
[
'last_accessed'
]
=
True
if
len
(
block
[
'children'
])
<=
last_accessed_child_position
:
last_accessed_child_block
=
block
[
'children'
][
last_accessed_child_position
-
1
]
last_accessed_child_block
[
'last_accessed'
]
=
True
mark_lasted_accessed
(
user
,
course_key
,
last_accessed_child_block
)
else
:
# We should be using an id in place of position for last accessed. However, while using position, if
# the child block is no longer accessible we'll use the last child.
block
[
'children'
][
-
1
][
'last_accessed'
]
=
True
course_key
=
CourseKey
.
from_string
(
course_id
)
course_usage_key
=
modulestore
()
.
make_course_usage_key
(
course_key
)
all_blocks
=
get_blocks
(
request
,
course_usage_key
,
user
=
request
.
user
,
nav_depth
=
3
,
requested_fields
=
[
'children'
,
'display_name'
,
'type'
,
'due'
,
'graded'
,
'special_exam_info'
,
'format'
],
block_types_filter
=
[
'course'
,
'chapter'
,
'sequential'
]
)
course_outline_root_block
=
all_blocks
[
'blocks'
][
all_blocks
[
'root'
]]
populate_children
(
course_outline_root_block
,
all_blocks
[
'blocks'
])
set_lasted_accessed_default
(
course_outline_root_block
)
mark_lasted_accessed
(
request
.
user
,
course_key
,
course_outline_root_block
)
return
course_outline_root_block
openedx/features/course_experience/views/course_home.py
View file @
3b31270e
...
...
@@ -9,14 +9,15 @@ from django.utils.decorators import method_decorator
from
django.views.decorators.cache
import
cache_control
from
django.views.decorators.csrf
import
ensure_csrf_cookie
from
courseware.courses
import
get_course_info_section
,
get_course_with_access
,
get_last_accessed_courseware
from
courseware.courses
import
get_course_info_section
,
get_course_with_access
from
lms.djangoapps.courseware.views.views
import
CourseTabView
from
opaque_keys.edx.keys
import
CourseKey
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
from
util.views
import
ensure_valid_course_key
from
web_fragments.fragment
import
Fragment
from
course_outline
import
CourseOutlineFragmentView
from
.course_outline
import
CourseOutlineFragmentView
from
..utils
import
get_course_outline_block_tree
class
CourseHomeView
(
CourseTabView
):
...
...
@@ -43,29 +44,67 @@ class CourseHomeFragmentView(EdxFragmentView):
"""
A fragment to render the home page for a course.
"""
def
_get_resume_course_info
(
self
,
request
,
course_id
):
"""
Returns information relevant to resume course functionality.
Returns a tuple: (has_visited_course, resume_course_url)
has_visited_course: True if the user has ever visted the course, False otherwise.
resume_course_url: The URL of the last accessed block if the user has visited the course,
otherwise the URL of the course root.
"""
def
get_last_accessed_block
(
block
):
"""
Gets the deepest block marked as 'last_accessed'.
"""
if
not
block
[
'last_accessed'
]:
return
None
if
not
block
.
get
(
'children'
):
return
block
for
child
in
block
[
'children'
]:
last_accessed_block
=
get_last_accessed_block
(
child
)
if
last_accessed_block
:
return
last_accessed_block
return
block
course_outline_root_block
=
get_course_outline_block_tree
(
request
,
course_id
)
last_accessed_block
=
get_last_accessed_block
(
course_outline_root_block
)
has_visited_course
=
bool
(
last_accessed_block
)
if
last_accessed_block
:
resume_course_url
=
last_accessed_block
[
'lms_web_url'
]
else
:
resume_course_url
=
course_outline_root_block
[
'lms_web_url'
]
return
(
has_visited_course
,
resume_course_url
)
def
render_to_fragment
(
self
,
request
,
course_id
=
None
,
**
kwargs
):
"""
Renders the course's home page as a fragment.
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
# Render the outline as a fragment
outline_fragment
=
CourseOutlineFragmentView
()
.
render_to_fragment
(
request
,
course_id
=
course_id
,
**
kwargs
)
# Get
the last accessed courseware
last_accessed_url
,
__
=
get_last_accessed_courseware
(
course
,
request
,
request
.
user
)
# Get
resume course information
has_visited_course
,
resume_course_url
=
self
.
_get_resume_course_info
(
request
,
course_id
)
# Get the handouts
# TODO: Use get_course_overview_with_access and blocks api
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
handouts_html
=
get_course_info_section
(
request
,
request
.
user
,
course
,
'handouts'
)
# Render the course home fragment
context
=
{
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'course
'
:
course
,
'course
_key'
:
course_key
,
'outline_fragment'
:
outline_fragment
,
'handouts_html'
:
handouts_html
,
'has_visited_course'
:
last_accessed_url
is
not
None
,
'has_visited_course'
:
has_visited_course
,
'resume_course_url'
:
resume_course_url
,
'disable_courseware_js'
:
True
,
'uses_pattern_library'
:
True
,
}
...
...
openedx/features/course_experience/views/course_outline.py
View file @
3b31270e
...
...
@@ -5,12 +5,12 @@ Views to show a course outline.
from
django.core.context_processors
import
csrf
from
django.template.loader
import
render_to_string
from
courseware.courses
import
get_course_with_access
,
get_last_accessed_courseware
from
lms.djangoapps.course_api.blocks.api
import
get_blocks
from
courseware.courses
import
get_course_overview_with_access
from
opaque_keys.edx.keys
import
CourseKey
from
openedx.core.djangoapps.plugin_api.views
import
EdxFragmentView
from
web_fragments.fragment
import
Fragment
from
xmodule.modulestore.django
import
modulestore
from
..utils
import
get_course_outline_block_tree
class
CourseOutlineFragmentView
(
EdxFragmentView
):
...
...
@@ -18,47 +18,19 @@ class CourseOutlineFragmentView(EdxFragmentView):
Course outline fragment to be shown in the unified course view.
"""
def
populate_children
(
self
,
block
,
all_blocks
,
course_position
):
"""
For a passed block, replace each id in its children array with the full representation of that child,
which will be looked up by id in the passed all_blocks dict.
Recursively do the same replacement for children of those children.
"""
children
=
block
.
get
(
'children'
)
or
[]
for
i
in
range
(
len
(
children
)):
child_id
=
block
[
'children'
][
i
]
child_detail
=
self
.
populate_children
(
all_blocks
[
child_id
],
all_blocks
,
course_position
)
block
[
'children'
][
i
]
=
child_detail
block
[
'children'
][
i
][
'current'
]
=
course_position
==
child_detail
[
'block_id'
]
return
block
def
render_to_fragment
(
self
,
request
,
course_id
=
None
,
page_context
=
None
,
**
kwargs
):
"""
Renders the course outline as a fragment.
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
_
,
course_position
=
get_last_accessed_courseware
(
course
,
request
,
request
.
user
)
course_usage_key
=
modulestore
()
.
make_course_usage_key
(
course_key
)
all_blocks
=
get_blocks
(
request
,
course_usage_key
,
user
=
request
.
user
,
nav_depth
=
3
,
requested_fields
=
[
'children'
,
'display_name'
,
'type'
,
'due'
,
'graded'
,
'special_exam_info'
,
'format'
],
block_types_filter
=
[
'course'
,
'chapter'
,
'sequential'
]
)
course_overview
=
get_course_overview_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
course_block_tree
=
all_blocks
[
'blocks'
][
all_blocks
[
'root'
]]
# Get the root of the block tree
course_block_tree
=
get_course_outline_block_tree
(
request
,
course_id
)
context
=
{
'csrf'
:
csrf
(
request
)[
'csrf_token'
],
'course'
:
course
,
# Recurse through the block tree, fleshing out each child object
'blocks'
:
self
.
populate_children
(
course_block_tree
,
all_blocks
[
'blocks'
],
course_position
)
'course'
:
course_overview
,
'blocks'
:
course_block_tree
}
html
=
render_to_string
(
'course_experience/course-outline-fragment.html'
,
context
)
return
Fragment
(
html
)
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