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
f1ccf1c0
Commit
f1ccf1c0
authored
Sep 09, 2013
by
Renzo Lucioni
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Integrate split testing and LMS tabs experiments
parent
fd06640d
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
184 additions
and
73 deletions
+184
-73
lms/djangoapps/courseware/tabs.py
+65
-34
lms/djangoapps/courseware/tests/test_tabs.py
+29
-28
lms/djangoapps/courseware/views.py
+1
-0
lms/envs/common.py
+15
-1
lms/envs/dev.py
+1
-1
lms/static/sass/course/layout/_courseware_header.scss
+11
-0
lms/templates/courseware/course_navigation.html
+8
-3
lms/templates/courseware/welcome-back.html
+33
-1
lms/templates/dashboard.html
+7
-1
lms/templates/widgets/segment-io.html
+12
-4
lms/urls.py
+1
-0
requirements/edx/github.txt
+1
-0
No files found.
lms/djangoapps/courseware/tabs.py
View file @
f1ccf1c0
...
...
@@ -25,6 +25,8 @@ from courseware.model_data import FieldDataCache
from
open_ended_grading
import
open_ended_notifications
import
waffle
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -55,32 +57,46 @@ TabImpl = namedtuple('TabImpl', 'validator generator')
##### Generators for various tabs.
def
_courseware
(
tab
,
user
,
course
,
active_page
):
def
_courseware
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This returns a tab containing the course content.
"""
link
=
reverse
(
'courseware'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
'Courseware'
,
link
,
active_page
==
"courseware"
)]
if
waffle
.
flag_is_active
(
request
,
'merge_course_tabs'
):
return
[
CourseTab
(
'Course Content'
,
link
,
active_page
==
"courseware"
)]
else
:
return
[
CourseTab
(
'Courseware'
,
link
,
active_page
==
"courseware"
)]
def
_course_info
(
tab
,
user
,
course
,
active_page
):
def
_course_info
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This returns a tab containing information about the course.
"""
link
=
reverse
(
'info'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
tab
[
'name'
],
link
,
active_page
==
"info"
)]
def
_progress
(
tab
,
user
,
course
,
active_page
):
def
_progress
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This returns a tab containing information about the authenticated user's progress.
"""
if
user
.
is_authenticated
():
link
=
reverse
(
'progress'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
tab
[
'name'
],
link
,
active_page
==
"progress"
)]
return
[]
def
_wiki
(
tab
,
user
,
course
,
active_page
):
def
_wiki
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This returns a tab containing the course wiki.
"""
if
settings
.
WIKI_ENABLED
:
link
=
reverse
(
'course_wiki'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
tab
[
'name'
],
link
,
active_page
==
'wiki'
)]
return
[]
def
_discussion
(
tab
,
user
,
course
,
active_page
):
def
_discussion
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This tab format only supports the new Berkeley discussion forums.
"""
...
...
@@ -91,25 +107,25 @@ def _discussion(tab, user, course, active_page):
return
[]
def
_external_discussion
(
tab
,
user
,
course
,
active_page
):
def
_external_discussion
(
tab
,
user
,
course
,
active_page
,
request
):
"""
This returns a tab that links to an external discussion service
"""
return
[
CourseTab
(
'Discussion'
,
tab
[
'link'
],
active_page
==
'discussion'
)]
def
_external_link
(
tab
,
user
,
course
,
active_page
):
def
_external_link
(
tab
,
user
,
course
,
active_page
,
request
):
# external links are never active
return
[
CourseTab
(
tab
[
'name'
],
tab
[
'link'
],
False
)]
def
_static_tab
(
tab
,
user
,
course
,
active_page
):
def
_static_tab
(
tab
,
user
,
course
,
active_page
,
request
):
link
=
reverse
(
'static_tab'
,
args
=
[
course
.
id
,
tab
[
'url_slug'
]])
active_str
=
'static_tab_{0}'
.
format
(
tab
[
'url_slug'
])
return
[
CourseTab
(
tab
[
'name'
],
link
,
active_page
==
active_str
)]
def
_textbooks
(
tab
,
user
,
course
,
active_page
):
def
_textbooks
(
tab
,
user
,
course
,
active_page
,
request
):
"""
Generates one tab per textbook. Only displays if user is authenticated.
"""
...
...
@@ -120,7 +136,8 @@ def _textbooks(tab, user, course, active_page):
for
index
,
textbook
in
enumerate
(
course
.
textbooks
)]
return
[]
def
_pdf_textbooks
(
tab
,
user
,
course
,
active_page
):
def
_pdf_textbooks
(
tab
,
user
,
course
,
active_page
,
request
):
"""
Generates one tab per textbook. Only displays if user is authenticated.
"""
...
...
@@ -131,7 +148,8 @@ def _pdf_textbooks(tab, user, course, active_page):
for
index
,
textbook
in
enumerate
(
course
.
pdf_textbooks
)]
return
[]
def
_html_textbooks
(
tab
,
user
,
course
,
active_page
):
def
_html_textbooks
(
tab
,
user
,
course
,
active_page
,
request
):
"""
Generates one tab per textbook. Only displays if user is authenticated.
"""
...
...
@@ -142,7 +160,8 @@ def _html_textbooks(tab, user, course, active_page):
for
index
,
textbook
in
enumerate
(
course
.
html_textbooks
)]
return
[]
def
_staff_grading
(
tab
,
user
,
course
,
active_page
):
def
_staff_grading
(
tab
,
user
,
course
,
active_page
,
request
):
if
has_access
(
user
,
course
,
'staff'
):
link
=
reverse
(
'staff_grading'
,
args
=
[
course
.
id
])
...
...
@@ -157,14 +176,13 @@ def _staff_grading(tab, user, course, active_page):
return
[]
def
_syllabus
(
tab
,
user
,
course
,
active_page
):
def
_syllabus
(
tab
,
user
,
course
,
active_page
,
request
):
"""Display the syllabus tab"""
link
=
reverse
(
'syllabus'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
'Syllabus'
,
link
,
active_page
==
'syllabus'
)]
def
_peer_grading
(
tab
,
user
,
course
,
active_page
):
def
_peer_grading
(
tab
,
user
,
course
,
active_page
,
request
):
if
user
.
is_authenticated
():
link
=
reverse
(
'peer_grading'
,
args
=
[
course
.
id
])
tab_name
=
"Peer grading"
...
...
@@ -178,7 +196,7 @@ def _peer_grading(tab, user, course, active_page):
return
[]
def
_combined_open_ended_grading
(
tab
,
user
,
course
,
active_page
):
def
_combined_open_ended_grading
(
tab
,
user
,
course
,
active_page
,
request
):
if
user
.
is_authenticated
():
link
=
reverse
(
'open_ended_notifications'
,
args
=
[
course
.
id
])
tab_name
=
"Open Ended Panel"
...
...
@@ -191,15 +209,15 @@ def _combined_open_ended_grading(tab, user, course, active_page):
return
tab
return
[]
def
_notes_tab
(
tab
,
user
,
course
,
active_page
):
def
_notes_tab
(
tab
,
user
,
course
,
active_page
,
request
):
if
user
.
is_authenticated
()
and
settings
.
MITX_FEATURES
.
get
(
'ENABLE_STUDENT_NOTES'
):
link
=
reverse
(
'notes'
,
args
=
[
course
.
id
])
return
[
CourseTab
(
tab
[
'name'
],
link
,
active_page
==
'notes'
)]
return
[]
#### Validators
#### Validators
def
key_checker
(
expected_keys
):
"""
Returns a function that checks that specified keys are present in a dict
...
...
@@ -263,12 +281,15 @@ def validate_tabs(course):
if
len
(
tabs
)
<
2
:
raise
InvalidTabsException
(
"Expected at least two tabs. tabs: '{0}'"
.
format
(
tabs
))
if
tabs
[
0
][
'type'
]
!=
'courseware'
:
raise
InvalidTabsException
(
"Expected first tab to have type 'courseware'. tabs: '{0}'"
.
format
(
tabs
))
if
tabs
[
1
][
'type'
]
!=
'course_info'
:
raise
InvalidTabsException
(
"Expected second tab to have type 'course_info'. tabs: '{0}'"
.
format
(
tabs
))
for
t
in
tabs
:
if
t
[
'type'
]
not
in
VALID_TAB_TYPES
:
raise
InvalidTabsException
(
"Unknown tab type {0}. Known types: {1}"
...
...
@@ -280,12 +301,12 @@ def validate_tabs(course):
# are actually unique (otherwise, will break active tag code)
def
get_course_tabs
(
user
,
course
,
active_page
):
def
get_course_tabs
(
user
,
course
,
active_page
,
request
):
"""
Return the tabs to show a particular user, as a list of CourseTab items.
"""
if
not
hasattr
(
course
,
'tabs'
)
or
not
course
.
tabs
:
return
get_default_tabs
(
user
,
course
,
active_page
)
return
get_default_tabs
(
user
,
course
,
active_page
,
request
)
# TODO (vshnayder): There needs to be a place to call this right after course
# load, but not from inside xmodule, since that doesn't (and probably
...
...
@@ -293,12 +314,18 @@ def get_course_tabs(user, course, active_page):
validate_tabs
(
course
)
tabs
=
[]
for
tab
in
course
.
tabs
:
if
waffle
.
flag_is_active
(
request
,
'merge_course_tabs'
):
course_tabs
=
[
tab
for
tab
in
course
.
tabs
if
tab
[
'type'
]
!=
"course_info"
]
else
:
course_tabs
=
course
.
tabs
for
tab
in
course_tabs
:
# expect handlers to return lists--handles things that are turned off
# via feature flags, and things like 'textbook' which might generate
# multiple tabs.
gen
=
VALID_TAB_TYPES
[
tab
[
'type'
]]
.
generator
tabs
.
extend
(
gen
(
tab
,
user
,
course
,
active_page
))
tabs
.
extend
(
gen
(
tab
,
user
,
course
,
active_page
,
request
))
# Instructor tab is special--automatically added if user is staff for the course
if
has_access
(
user
,
course
,
'staff'
):
...
...
@@ -314,7 +341,7 @@ def get_discussion_link(course):
Return the URL for the discussion tab for the given `course`.
If they have a discussion link specified, use that even if we disable
discussions. Disabling disc
s
ussions is mostly a server safety feature at
discussions. Disabling discussions is mostly a server safety feature at
this point, and we don't need to worry about external sites. Otherwise,
if the course has a discussion tab or uses the default tabs, return the
discussion view URL. Otherwise, return None to indicate the lack of a
...
...
@@ -330,28 +357,33 @@ def get_discussion_link(course):
return
reverse
(
'django_comment_client.forum.views.forum_form_discussion'
,
args
=
[
course
.
id
])
def
get_default_tabs
(
user
,
course
,
active_page
):
def
get_default_tabs
(
user
,
course
,
active_page
,
request
):
"""
Return the default set of tabs.
"""
# When calling the various _tab methods, can omit the 'type':'blah' from the
# first arg, since that's only used for dispatch
tabs
=
[]
tabs
.
extend
(
_courseware
({
''
},
user
,
course
,
active_page
))
tabs
.
extend
(
_course_info
({
'name'
:
'Course Info'
},
user
,
course
,
active_page
))
tabs
.
extend
(
_courseware
({
''
},
user
,
course
,
active_page
,
request
))
if
not
waffle
.
flag_is_active
(
request
,
'merge_course_tabs'
):
tabs
.
extend
(
_course_info
({
'name'
:
'Course Info'
},
user
,
course
,
active_page
,
request
))
if
hasattr
(
course
,
'syllabus_present'
)
and
course
.
syllabus_present
:
link
=
reverse
(
'syllabus'
,
args
=
[
course
.
id
])
tabs
.
append
(
CourseTab
(
'Syllabus'
,
link
,
active_page
==
'syllabus'
))
tabs
.
extend
(
_textbooks
({},
user
,
course
,
active_page
))
tabs
.
extend
(
_textbooks
({},
user
,
course
,
active_page
,
request
))
discussion_link
=
get_discussion_link
(
course
)
if
discussion_link
:
tabs
.
append
(
CourseTab
(
'Discussion'
,
discussion_link
,
active_page
==
'discussion'
))
tabs
.
extend
(
_wiki
({
'name'
:
'Wiki'
,
'type'
:
'wiki'
},
user
,
course
,
active_page
))
tabs
.
extend
(
_wiki
({
'name'
:
'Wiki'
,
'type'
:
'wiki'
},
user
,
course
,
active_page
,
request
))
if
user
.
is_authenticated
()
and
not
course
.
hide_progress_tab
:
tabs
.
extend
(
_progress
({
'name'
:
'Progress'
},
user
,
course
,
active_page
))
tabs
.
extend
(
_progress
({
'name'
:
'Progress'
},
user
,
course
,
active_page
,
request
))
if
has_access
(
user
,
course
,
'staff'
):
link
=
reverse
(
'instructor_dashboard'
,
args
=
[
course
.
id
])
...
...
@@ -376,7 +408,6 @@ def get_static_tab_by_slug(course, tab_slug):
def
get_static_tab_contents
(
request
,
course
,
tab
):
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'static_tab'
,
tab
[
'url_slug'
])
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
course
.
id
,
request
.
user
,
modulestore
()
.
get_instance
(
course
.
id
,
loc
),
depth
=
0
)
...
...
lms/djangoapps/courseware/tests/test_tabs.py
View file @
f1ccf1c0
...
...
@@ -11,6 +11,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
courseware.tests.modulestore_config
import
TEST_DATA_MIXED_MODULESTORE
FAKE_REQUEST
=
None
class
ProgressTestCase
(
TestCase
):
...
...
@@ -29,20 +30,20 @@ class ProgressTestCase(TestCase):
def
test_progress
(
self
):
self
.
assertEqual
(
tabs
.
_progress
(
self
.
tab
,
self
.
mockuser0
,
self
.
course
,
self
.
active_page0
),
[])
self
.
active_page0
,
FAKE_REQUEST
),
[])
self
.
assertEqual
(
tabs
.
_progress
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
0
]
.
name
,
'same'
)
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
name
,
'same'
)
self
.
assertEqual
(
tabs
.
_progress
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
0
]
.
link
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
link
,
reverse
(
'progress'
,
args
=
[
self
.
course
.
id
]))
self
.
assertEqual
(
tabs
.
_progress
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page0
)[
0
]
.
is_active
,
False
)
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
self
.
assertEqual
(
tabs
.
_progress
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
0
]
.
is_active
,
True
)
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
is_active
,
True
)
class
WikiTestCase
(
TestCase
):
...
...
@@ -60,26 +61,26 @@ class WikiTestCase(TestCase):
def
test_wiki_enabled
(
self
):
self
.
assertEqual
(
tabs
.
_wiki
(
self
.
tab
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
name
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
name
,
'same'
)
self
.
assertEqual
(
tabs
.
_wiki
(
self
.
tab
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
link
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
link
,
reverse
(
'course_wiki'
,
args
=
[
self
.
course
.
id
]))
self
.
assertEqual
(
tabs
.
_wiki
(
self
.
tab
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
is_active
,
True
)
self
.
assertEqual
(
tabs
.
_wiki
(
self
.
tab
,
self
.
user
,
self
.
course
,
self
.
active_page0
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
@override_settings
(
WIKI_ENABLED
=
False
)
def
test_wiki_enabled_false
(
self
):
self
.
assertEqual
(
tabs
.
_wiki
(
self
.
tab
,
self
.
user
,
self
.
course
,
self
.
active_page1
),
[])
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
),
[])
class
ExternalLinkTestCase
(
TestCase
):
...
...
@@ -95,19 +96,19 @@ class ExternalLinkTestCase(TestCase):
def
test_external_link
(
self
):
self
.
assertEqual
(
tabs
.
_external_link
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page0
)[
0
]
.
name
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
name
,
'same'
)
self
.
assertEqual
(
tabs
.
_external_link
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page0
)[
0
]
.
link
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
link
,
'blink'
)
self
.
assertEqual
(
tabs
.
_external_link
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page0
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
self
.
assertEqual
(
tabs
.
_external_link
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page00
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page00
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
...
...
@@ -125,20 +126,20 @@ class StaticTabTestCase(TestCase):
def
test_static_tab
(
self
):
self
.
assertEqual
(
tabs
.
_static_tab
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
name
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
name
,
'same'
)
self
.
assertEqual
(
tabs
.
_static_tab
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
link
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
link
,
reverse
(
'static_tab'
,
args
=
[
self
.
course
.
id
,
self
.
tabby
[
'url_slug'
]]))
self
.
assertEqual
(
tabs
.
_static_tab
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page1
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
0
]
.
is_active
,
True
)
self
.
assertEqual
(
tabs
.
_static_tab
(
self
.
tabby
,
self
.
user
,
self
.
course
,
self
.
active_page0
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
...
...
@@ -166,45 +167,45 @@ class TextbooksTestCase(TestCase):
def
test_textbooks1
(
self
):
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page0
)[
0
]
.
name
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
name
,
'Algebra'
)
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page0
)[
0
]
.
link
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
link
,
reverse
(
'book'
,
args
=
[
self
.
course
.
id
,
0
]))
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page0
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_page0
,
FAKE_REQUEST
)[
0
]
.
is_active
,
True
)
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_pageX
)[
0
]
.
is_active
,
self
.
course
,
self
.
active_pageX
,
FAKE_REQUEST
)[
0
]
.
is_active
,
False
)
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
1
]
.
name
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
1
]
.
name
,
'Topology'
)
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
1
]
.
link
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
1
]
.
link
,
reverse
(
'book'
,
args
=
[
self
.
course
.
id
,
1
]))
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_page1
)[
1
]
.
is_active
,
self
.
course
,
self
.
active_page1
,
FAKE_REQUEST
)[
1
]
.
is_active
,
True
)
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_pageX
)[
1
]
.
is_active
,
self
.
course
,
self
.
active_pageX
,
FAKE_REQUEST
)[
1
]
.
is_active
,
False
)
@override_settings
(
MITX_FEATURES
=
{
'ENABLE_TEXTBOOK'
:
False
})
def
test_textbooks0
(
self
):
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser1
,
self
.
course
,
self
.
active_pageX
),
[])
self
.
course
,
self
.
active_pageX
,
FAKE_REQUEST
),
[])
self
.
assertEqual
(
tabs
.
_textbooks
(
self
.
tab
,
self
.
mockuser0
,
self
.
course
,
self
.
active_pageX
),
[])
self
.
course
,
self
.
active_pageX
,
FAKE_REQUEST
),
[])
class
KeyCheckerTestCase
(
TestCase
):
...
...
lms/djangoapps/courseware/views.py
View file @
f1ccf1c0
...
...
@@ -728,6 +728,7 @@ def submission_history(request, course_id, student_username, location):
Right now this only works for problems because that's all
StudentModuleHistory records.
"""
course
=
get_course_with_access
(
request
.
user
,
course_id
,
'load'
)
staff_access
=
has_access
(
request
.
user
,
course
,
'staff'
)
...
...
lms/envs/common.py
View file @
f1ccf1c0
...
...
@@ -80,7 +80,7 @@ MITX_FEATURES = {
'ENABLE_PSYCHOMETRICS'
:
False
,
# real-time psychometrics (eg item response theory analysis in instructor dashboard)
'ENABLE_DJANGO_ADMIN_SITE'
:
Fals
e
,
# set true to enable django's admin site, even on prod (e.g. for course ops)
'ENABLE_DJANGO_ADMIN_SITE'
:
Tru
e
,
# set true to enable django's admin site, even on prod (e.g. for course ops)
'ENABLE_SQL_TRACKING_LOGS'
:
False
,
'ENABLE_LMS_MIGRATION'
:
False
,
'ENABLE_MANUAL_GIT_RELOAD'
:
False
,
...
...
@@ -523,6 +523,14 @@ MOCK_STAFF_GRADING = False
################################# Jasmine ###################################
JASMINE_TEST_DIRECTORY
=
PROJECT_ROOT
+
'/static/coffee'
################################# Waffle ###################################
# Name prepended to cookies set by Waffle
WAFFLE_COOKIE
=
"waffle_flag_
%
s"
# Two weeks (in sec)
WAFFLE_MAX_AGE
=
1209600
################################# Middleware ###################################
# List of finder classes that know how to find static files in
# various locations.
...
...
@@ -570,6 +578,9 @@ MIDDLEWARE_CLASSES = (
# catches any uncaught RateLimitExceptions and returns a 403 instead of a 500
'ratelimitbackend.middleware.RateLimitMiddleware'
,
# For A/B testing
'waffle.middleware.WaffleMiddleware'
,
)
############################### Pipeline #######################################
...
...
@@ -832,6 +843,9 @@ INSTALLED_APPS = (
# Foldit integration
'foldit'
,
# For A/B testing
'waffle'
,
# For testing
'django.contrib.admin'
,
# only used in DEBUG mode
'django_nose'
,
...
...
lms/envs/dev.py
View file @
f1ccf1c0
...
...
@@ -255,7 +255,7 @@ ANALYTICS_API_KEY = ""
##### segment-io ######
# If there's an environment variable set, grab it and turn on
segment
io
# If there's an environment variable set, grab it and turn on
Segment.
io
SEGMENT_IO_LMS_KEY
=
os
.
environ
.
get
(
'SEGMENT_IO_LMS_KEY'
)
if
SEGMENT_IO_LMS_KEY
:
MITX_FEATURES
[
'SEGMENT_IO_LMS'
]
=
True
...
...
lms/static/sass/course/layout/_courseware_header.scss
View file @
f1ccf1c0
...
...
@@ -23,6 +23,17 @@ nav.course-material {
list-style
:
none
;
margin-right
:
6px
;
&
.prominent
{
margin-right
:
16px
;
background
:
rgba
(
255
,
255
,
255
,
.5
);
border-radius
:
3px
;
}
&
.prominent
+
li
{
padding-left
:
15px
;
border-left
:
1px
solid
#333
;
}
a
{
border-radius
:
3px
;
color
:
#555
;
...
...
lms/templates/courseware/course_navigation.html
View file @
f1ccf1c0
...
...
@@ -13,19 +13,24 @@ def url_class(is_active):
%
>
<
%!
from
courseware
.
tabs
import
get_course_tabs
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
import
waffle
%
>
<nav
class=
"${active_page} course-material"
>
<div
class=
"inner-wrapper"
>
<ol
class=
"course-tabs"
>
% for tab in get_course_tabs(user, course, active_page):
% for tab in get_course_tabs(user, course, active_page, request):
% if waffle.flag_is_active(request, 'visual_treatment') or waffle.flag_is_active(request, 'merge_course_tabs'):
<li
class=
"${"
prominent
"
if
tab
.
name
in
("
Courseware
",
"
Course
Content
")
else
""}"
>
% else:
<li>
% endif
<a
href=
"${tab.link | h}"
class=
"${url_class(tab.is_active)}"
>
${tab.name | h}
% if tab.is_active == True:
<span
class=
"sr"
>
, current location
</span>
<span
class=
"sr"
>
, current location
</span>
%endif
% if tab.has_img == True:
<img
src=
"${tab.img}"
/>
<img
src=
"${tab.img}"
/>
%endif
</a>
</li>
...
...
lms/templates/courseware/welcome-back.html
View file @
f1ccf1c0
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
import
waffle
%
>
<h2>
${chapter_module.display_name_with_default}
</h2>
<p>
${_("You were most recently in {section_link}. If you\'re done with that, choose another section on the left.").format(
...
...
@@ -7,3 +11,31 @@
section_name=prev_section.display_name_with_default,
)
)}
</p>
% if waffle.flag_is_active(request, 'merge_course_tabs'):
<
%!
from
courseware
.
courses
import
get_course_info_section
%
>
<section
class=
"container"
>
<div
class=
"info-wrapper"
>
% if user.is_authenticated():
<section
class=
"updates"
>
<h1>
${_("Course Updates
&
News")}
</h1>
${get_course_info_section(request, course, 'updates')}
</section>
<section
aria-label=
"${_('Handout Navigation')}"
class=
"handouts"
>
<h1>
${course.info_sidebar_name}
</h1>
${get_course_info_section(request, course, 'handouts')}
</section>
% else:
<section
class=
"updates"
>
<h1>
${_("Course Updates
&
News")}
</h1>
${get_course_info_section(request, course, 'guest_updates')}
</section>
<section
aria-label=
"${_('Handout Navigation')}"
class=
"handouts"
>
<h1>
${_("Course Handouts")}
</h1>
${get_course_info_section(request, course, 'guest_handouts')}
</section>
% endif
</div>
</section>
% endif
lms/templates/dashboard.html
View file @
f1ccf1c0
...
...
@@ -5,8 +5,11 @@
from
courseware
.
courses
import
course_image_url
,
get_course_about_section
from
courseware
.
access
import
has_access
from
certificates
.
models
import
CertificateStatuses
from
xmodule
.
modulestore
import
MONGO_MODULESTORE_TYPE
from
xmodule
.
modulestore
.
django
import
modulestore
import
waffle
%
>
<
%
inherit
file=
"main.html"
/>
...
...
@@ -163,7 +166,10 @@
<li
class=
"course-item"
>
<article
class=
"course ${enrollment.mode}"
>
<
%
course_target =
reverse('info',
args=
[course.id])
if
waffle
.
flag_is_active
(
request
,
'
merge_course_tabs
')
:
course_target =
reverse('courseware',
args=
[course.id])
else:
course_target =
reverse('info',
args=
[course.id])
%
>
% if course.id in show_courseware_links_for:
...
...
lms/templates/widgets/segment-io.html
View file @
f1ccf1c0
% if settings.MITX_FEATURES.get('SEGMENT_IO_LMS'):
<!-- begin Segment.io -->
<
%!
from
django
.
core
.
urlresolvers
import
reverse
%
>
<
%!
import
waffle
%
>
<
%
active_flags =
" + "
.
join
(
waffle
.
get_flags
(
request
))
%
>
<!-- <script src="${ reverse('wafflejs') }"></script> -->
<script
type=
"text/javascript"
>
var
analytics
=
analytics
||
[];
analytics
.
load
=
function
(
e
){
var
t
=
document
.
createElement
(
"script"
);
t
.
type
=
"text/javascript"
,
t
.
async
=!
0
,
t
.
src
=
(
"https:"
===
document
.
location
.
protocol
?
"https://"
:
"http://"
)
+
"d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/"
+
e
+
"/analytics.min.js"
;
var
n
=
document
.
getElementsByTagName
(
"script"
)[
0
];
n
.
parentNode
.
insertBefore
(
t
,
n
);
var
r
=
function
(
e
){
return
function
(){
analytics
.
push
([
e
].
concat
(
Array
.
prototype
.
slice
.
call
(
arguments
,
0
)))}},
i
=
[
"identify"
,
"track"
,
"trackLink"
,
"trackForm"
,
"trackClick"
,
"trackSubmit"
,
"pageview"
,
"ab"
,
"alias"
,
"ready"
];
for
(
var
s
=
0
;
s
<
i
.
length
;
s
++
)
analytics
[
i
[
s
]]
=
r
(
i
[
s
])};
analytics
.
load
(
"${ settings.SEGMENT_IO_LMS_KEY }"
);
%
if
user
.
is_authenticated
():
analytics
.
identify
(
"${ user.id }"
,
{
email
:
"${ user.email }"
,
username
:
"${ user.username }"
});
analytics
.
identify
(
"${ user.id }"
,
{
email
:
"${ user.email }"
,
username
:
"${ user.username }"
,
"Active Flags"
:
"${ active_flags }"
,
});
%
endif
</script>
...
...
lms/urls.py
View file @
f1ccf1c0
...
...
@@ -59,6 +59,7 @@ urlpatterns = ('', # nopep8
url
(
r'^user_api/'
,
include
(
'user_api.urls'
)),
url
(
r'^'
,
include
(
'waffle.urls'
)),
)
# if settings.MITX_FEATURES.get("MULTIPLE_ENROLLMENT_ROLES"):
...
...
requirements/edx/github.txt
View file @
f1ccf1c0
...
...
@@ -18,3 +18,4 @@
-e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.3#egg=diff_cover
-e git+https://github.com/edx/js-test-tool.git@v0.0.7#egg=js_test_tool
-e git+https://github.com/edx/django-waffle.git@823a102e48#egg=django-waffle
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