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
c23c0b99
Commit
c23c0b99
authored
May 02, 2017
by
Albert St. Aubin
Committed by
cahrens
Jun 05, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
EDUCATOR-141: Moved the Discussions Mgt to its own tab and updated tests
parent
8951ac8c
Show whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
1462 additions
and
1119 deletions
+1462
-1119
common/test/acceptance/pages/lms/instructor_dashboard.py
+76
-60
common/test/acceptance/tests/discussion/helpers.py
+7
-2
common/test/acceptance/tests/discussion/test_cohort_management.py
+0
-261
common/test/acceptance/tests/discussion/test_discussion_management.py
+273
-0
lms/djangoapps/django_comment_client/tests/test_utils.py
+25
-15
lms/djangoapps/instructor/views/instructor_dashboard.py
+16
-1
lms/envs/common.py
+1
-0
lms/static/js/discussions_management/models/course_discussions_detail.js
+2
-2
lms/static/js/discussions_management/models/course_discussions_settings.js
+14
-0
lms/static/js/discussions_management/views/discussions.js
+49
-0
lms/static/js/discussions_management/views/discussions_dashboard_factory.js
+33
-0
lms/static/js/discussions_management/views/divided_discussions.js
+19
-18
lms/static/js/discussions_management/views/divided_discussions_course_wide.js
+15
-13
lms/static/js/discussions_management/views/divided_discussions_inline.js
+31
-26
lms/static/js/groups/models/course_cohort_settings.js
+1
-4
lms/static/js/groups/views/cohorts.js
+1
-25
lms/static/js/groups/views/cohorts_dashboard_factory.js
+9
-15
lms/static/js/instructor_dashboard/discussions_management.js
+12
-0
lms/static/js/instructor_dashboard/instructor_dashboard.js
+3
-0
lms/static/js/spec/groups/views/cohorts_spec.js
+9
-489
lms/static/js/spec/groups/views/discussions_spec.js
+512
-0
lms/static/lms/js/build.js
+1
-0
lms/static/lms/js/spec/main.js
+1
-0
lms/static/sass/course/instructor/_instructor_2.scss
+71
-43
lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore
+1
-1
lms/templates/instructor/instructor_dashboard_2/cohort_management.html
+0
-1
lms/templates/instructor/instructor_dashboard_2/cohorts.underscore
+0
-10
lms/templates/instructor/instructor_dashboard_2/discussions.underscore
+5
-0
lms/templates/instructor/instructor_dashboard_2/discussions_management.html
+22
-0
lms/templates/instructor/instructor_dashboard_2/divided-discussions-course-wide.underscore
+1
-1
lms/templates/instructor/instructor_dashboard_2/divided-discussions-inline.underscore
+5
-5
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
+1
-1
lms/urls.py
+12
-3
openedx/core/djangoapps/course_groups/cohorts.py
+10
-0
openedx/core/djangoapps/course_groups/tests/helpers.py
+40
-13
openedx/core/djangoapps/course_groups/tests/test_views.py
+117
-83
openedx/core/djangoapps/course_groups/views.py
+67
-27
No files found.
common/test/acceptance/pages/lms/instructor_dashboard.py
View file @
c23c0b99
...
@@ -47,6 +47,15 @@ class InstructorDashboardPage(CoursePage):
...
@@ -47,6 +47,15 @@ class InstructorDashboardPage(CoursePage):
cohort_management_section
.
wait_for_page
()
cohort_management_section
.
wait_for_page
()
return
cohort_management_section
return
cohort_management_section
def
select_discussion_management
(
self
):
"""
Selects the Discussion tab and returns the DiscussionmanagementSection
"""
self
.
q
(
css
=
'[data-section="discussions_management"'
)
.
first
.
click
()
discussion_management_section
=
DiscussionManagementSection
(
self
.
browser
)
discussion_management_section
.
wait_for_page
()
return
discussion_management_section
def
select_data_download
(
self
):
def
select_data_download
(
self
):
"""
"""
Selects the data download tab and returns a DataDownloadPage.
Selects the data download tab and returns a DataDownloadPage.
...
@@ -666,54 +675,63 @@ class CohortManagementSection(PageObject):
...
@@ -666,54 +675,63 @@ class CohortManagementSection(PageObject):
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohorts-state'
))
.
first
.
click
()
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohorts-state'
))
.
first
.
click
()
self
.
wait_for_ajax
()
self
.
wait_for_ajax
()
def
toggles_showing_of_discussion_topics
(
self
):
def
cohort_management_controls_visible
(
self
):
"""
"""
Shows the discussion topics
.
Return the visibility status of cohort management controls(cohort selector section etc)
.
"""
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
".toggle-cohort-management-discussions"
))
.
first
.
click
()
return
(
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohort-management-nav'
))
.
visible
and
self
.
wait_for_element_visibility
(
"#cohort-discussions-management"
,
"Waiting for discussions to appear"
)
self
.
q
(
css
=
self
.
_bounded_selector
(
'.wrapper-cohort-supplemental'
))
.
visible
)
def
discussion_topics_visible
(
self
):
"""
Returns the visibility status of cohort discussion controls.
"""
EmptyPromise
(
lambda
:
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohort-discussions-nav'
))
.
results
!=
0
,
"Waiting for discussion section to show"
)
.
fulfill
()
return
(
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohort-course-wide-discussions-nav'
))
.
visible
and
class
DiscussionManagementSection
(
PageObject
):
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohort-inline-discussions-nav'
))
.
visible
)
def
select_discussion_topic
(
self
,
key
):
url
=
None
discussion_form_selectors
=
{
'course-wide'
:
'.cohort-course-wide-discussions-form'
,
'inline'
:
'.cohort-inline-discussions-form'
}
def
is_browser_on_page
(
self
):
return
self
.
q
(
css
=
self
.
discussion_form_selectors
[
'course-wide'
])
.
present
def
_bounded_selector
(
self
,
selector
):
"""
"""
Selects discussion topic checkbox by clicking on i
t.
Return `selector`, but limited to the divided discussion management contex
t.
"""
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-discussion-subcategory-
%
s"
%
key
))
.
first
.
click
(
)
return
'.discussions-management {}'
.
format
(
selector
)
def
select_always_inline_discussion
(
self
):
def
is_save_button_disabled
(
self
,
key
):
"""
"""
Selects the always_cohort_inline_discussions radio button
.
Returns the status for form's save button, enabled or disabled
.
"""
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-all-inline-discussions"
))
.
first
.
click
()
save_button_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.action-save'
)
disabled
=
self
.
q
(
css
=
self
.
_bounded_selector
(
save_button_css
))
.
attrs
(
'disabled'
)
return
disabled
[
0
]
==
'true'
def
always_inline_discussion_selected
(
self
):
def
discussion_topics_visible
(
self
):
"""
"""
Returns t
rue if always_cohort_inline_discussions radio button is selected
.
Returns t
he visibility status of divide discussion controls
.
"""
"""
return
len
(
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-all-inline-discussions:checked"
)))
>
0
return
(
self
.
q
(
css
=
self
.
_bounded_selector
(
'.course-wide-discussions-nav'
))
.
visible
and
self
.
q
(
css
=
self
.
_bounded_selector
(
'.inline-discussions-nav'
))
.
visible
)
def
cohort_some_inline_discussion_selected
(
self
):
def
divided_discussion_heading_is_visible
(
self
,
key
):
"""
"""
Returns t
rue if some_cohort_inline_discussions radio button is selected
.
Returns t
he text of discussion topic headings if it exists, otherwise return False
.
"""
"""
return
len
(
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-cohort-inline-discussions:checked"
)))
>
0
form_heading_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.subsection-title'
)
discussion_heading
=
self
.
q
(
css
=
self
.
_bounded_selector
(
form_heading_css
))
def
select_cohort_some_inline_discussion
(
self
):
if
len
(
discussion_heading
)
==
0
:
return
False
return
discussion_heading
.
first
.
text
[
0
]
def
select_always_inline_discussion
(
self
):
"""
"""
Selects the
cohort_som
e_inline_discussions radio button.
Selects the
always_divid
e_inline_discussions radio button.
"""
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-
cohort
-inline-discussions"
))
.
first
.
click
()
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-
all
-inline-discussions"
))
.
first
.
click
()
def
inline_discussion_topics_disabled
(
self
):
def
inline_discussion_topics_disabled
(
self
):
"""
"""
...
@@ -722,35 +740,45 @@ class CohortManagementSection(PageObject):
...
@@ -722,35 +740,45 @@ class CohortManagementSection(PageObject):
inline_topics
=
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-subcategory-inline'
))
inline_topics
=
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-subcategory-inline'
))
return
all
(
topic
.
get_attribute
(
'disabled'
)
==
'true'
for
topic
in
inline_topics
)
return
all
(
topic
.
get_attribute
(
'disabled'
)
==
'true'
for
topic
in
inline_topics
)
def
is_save_button_disabled
(
self
,
key
):
def
save_discussion_topics
(
self
,
key
):
"""
"""
Returns the status for form's save button, enabled or disabled
.
Saves the discussion topics
.
"""
"""
save_button_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.action-save'
)
save_button_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.action-save'
)
disabled
=
self
.
q
(
css
=
self
.
_bounded_selector
(
save_button_css
))
.
attrs
(
'disabled'
)
self
.
q
(
css
=
self
.
_bounded_selector
(
save_button_css
))
.
first
.
click
()
return
disabled
[
0
]
==
'true'
def
is_category
_selected
(
self
):
def
always_inline_discussion
_selected
(
self
):
"""
"""
Returns t
he status for category checkboxes
.
Returns t
rue if always_divide_inline_discussions radio button is selected
.
"""
"""
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-category:checked'
))
.
is_present
()
return
len
(
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-all-inline-discussions:checked"
)))
>
0
def
get_cohorted_topics_count
(
self
,
key
):
def
divide_some_inline_discussion_selected
(
self
):
"""
"""
Returns t
he count for cohorted topics
.
Returns t
rue if divide_some_inline_discussions radio button is selected
.
"""
"""
cohorted_topics
=
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-subcategory-
%
s:checked'
%
key
))
return
len
(
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-cohort-inline-discussions:checked"
)))
>
0
return
len
(
cohorted_topics
.
results
)
def
s
ave_discussion_topics
(
self
,
key
):
def
s
elect_divide_some_inline_discussion
(
self
):
"""
"""
S
aves the discussion topics
.
S
elects the divide_some_inline_discussions radio button
.
"""
"""
save_button_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.action-save'
)
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-cohort-inline-discussions"
))
.
first
.
click
()
self
.
q
(
css
=
self
.
_bounded_selector
(
save_button_css
))
.
first
.
click
()
def
get_divided_topics_count
(
self
,
key
):
"""
Returns the count for divided topics.
"""
divided_topics
=
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-subcategory-
%
s:checked'
%
key
))
return
len
(
divided_topics
.
results
)
def
get_cohort_discussions_message
(
self
,
key
,
msg_type
=
"confirmation"
):
def
select_discussion_topic
(
self
,
key
):
"""
Selects discussion topic checkbox by clicking on it.
"""
self
.
q
(
css
=
self
.
_bounded_selector
(
".check-discussion-subcategory-
%
s"
%
key
))
.
first
.
click
()
def
get_divide_discussions_message
(
self
,
key
,
msg_type
=
"confirmation"
):
"""
"""
Returns the message related to modifying discussion topics.
Returns the message related to modifying discussion topics.
"""
"""
...
@@ -767,23 +795,11 @@ class CohortManagementSection(PageObject):
...
@@ -767,23 +795,11 @@ class CohortManagementSection(PageObject):
return
''
return
''
return
message_title
.
first
.
text
[
0
]
return
message_title
.
first
.
text
[
0
]
def
cohort_discussion_heading_is_visible
(
self
,
key
):
def
is_category_selected
(
self
):
"""
Returns the visibility of discussion topic headings.
"""
form_heading_css
=
'
%
s
%
s'
%
(
self
.
discussion_form_selectors
[
key
],
'.subsection-title'
)
discussion_heading
=
self
.
q
(
css
=
self
.
_bounded_selector
(
form_heading_css
))
if
len
(
discussion_heading
)
==
0
:
return
False
return
discussion_heading
.
first
.
text
[
0
]
def
cohort_management_controls_visible
(
self
):
"""
"""
Return
the visibility status of cohort management controls(cohort selector section etc)
.
Return
s the status for category checkboxes
.
"""
"""
return
(
self
.
q
(
css
=
self
.
_bounded_selector
(
'.cohort-management-nav'
))
.
visible
and
return
self
.
q
(
css
=
self
.
_bounded_selector
(
'.check-discussion-category:checked'
))
.
is_present
()
self
.
q
(
css
=
self
.
_bounded_selector
(
'.wrapper-cohort-supplemental'
))
.
visible
)
class
MembershipPageAutoEnrollSection
(
PageObject
):
class
MembershipPageAutoEnrollSection
(
PageObject
):
...
...
common/test/acceptance/tests/discussion/helpers.py
View file @
c23c0b99
...
@@ -76,11 +76,16 @@ class CohortTestMixin(object):
...
@@ -76,11 +76,16 @@ class CohortTestMixin(object):
def
enable_cohorting
(
self
,
course_fixture
):
def
enable_cohorting
(
self
,
course_fixture
):
"""
"""
enables cohort
ing
for the current course fixture.
enables cohort
s and always_divide_inline_discussions
for the current course fixture.
"""
"""
url
=
LMS_BASE_URL
+
"/courses/"
+
course_fixture
.
_course_key
+
'/cohorts/settings'
# pylint: disable=protected-access
url
=
LMS_BASE_URL
+
"/courses/"
+
course_fixture
.
_course_key
+
'/cohorts/settings'
# pylint: disable=protected-access
data
=
json
.
dumps
({
'always_cohort_inline_discussions'
:
True
})
discussions_url
=
LMS_BASE_URL
+
"/courses/"
+
course_fixture
.
_course_key
+
'/discussions/settings'
# pylint: disable=protected-access
data
=
json
.
dumps
({
'is_cohorted'
:
True
})
discussions_data
=
json
.
dumps
({
'always_divide_inline_discussions'
:
True
})
response
=
course_fixture
.
session
.
patch
(
url
,
data
=
data
,
headers
=
course_fixture
.
headers
)
response
=
course_fixture
.
session
.
patch
(
url
,
data
=
data
,
headers
=
course_fixture
.
headers
)
course_fixture
.
session
.
patch
(
discussions_url
,
data
=
discussions_data
,
headers
=
course_fixture
.
headers
)
def
disable_cohorting
(
self
,
course_fixture
):
def
disable_cohorting
(
self
,
course_fixture
):
"""
"""
...
...
common/test/acceptance/tests/discussion/test_cohort_management.py
View file @
c23c0b99
...
@@ -694,267 +694,6 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
...
@@ -694,267 +694,6 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
@attr
(
shard
=
6
)
@attr
(
shard
=
6
)
class
CohortDiscussionTopicsTest
(
UniqueCourseTest
,
CohortTestMixin
):
"""
Tests for cohorting the inline and course-wide discussion topics.
"""
def
setUp
(
self
):
"""
Set up a discussion topics
"""
super
(
CohortDiscussionTopicsTest
,
self
)
.
setUp
()
self
.
discussion_id
=
"test_discussion_{}"
.
format
(
uuid
.
uuid4
()
.
hex
)
self
.
course_fixture
=
CourseFixture
(
**
self
.
course_info
)
.
add_children
(
XBlockFixtureDesc
(
"chapter"
,
"Test Section"
)
.
add_children
(
XBlockFixtureDesc
(
"sequential"
,
"Test Subsection"
)
.
add_children
(
XBlockFixtureDesc
(
"vertical"
,
"Test Unit"
)
.
add_children
(
XBlockFixtureDesc
(
"discussion"
,
"Test Discussion"
,
metadata
=
{
"discussion_id"
:
self
.
discussion_id
}
)
)
)
)
)
.
install
()
# create course with single cohort and two content groups (user_partition of type "cohort")
self
.
cohort_name
=
"OnlyCohort"
self
.
setup_cohort_config
(
self
.
course_fixture
)
self
.
cohort_id
=
self
.
add_manual_cohort
(
self
.
course_fixture
,
self
.
cohort_name
)
# login as an instructor
self
.
instructor_name
=
"instructor_user"
self
.
instructor_id
=
AutoAuthPage
(
self
.
browser
,
username
=
self
.
instructor_name
,
email
=
"instructor_user@example.com"
,
course_id
=
self
.
course_id
,
staff
=
True
)
.
visit
()
.
get_user_id
()
# go to the membership page on the instructor dashboard
self
.
instructor_dashboard_page
=
InstructorDashboardPage
(
self
.
browser
,
self
.
course_id
)
self
.
instructor_dashboard_page
.
visit
()
self
.
cohort_management_page
=
self
.
instructor_dashboard_page
.
select_cohort_management
()
self
.
cohort_management_page
.
wait_for_page
()
self
.
course_wide_key
=
'course-wide'
self
.
inline_key
=
'inline'
def
cohort_discussion_topics_are_visible
(
self
):
"""
Assert that discussion topics are visible with appropriate content.
"""
self
.
cohort_management_page
.
toggles_showing_of_discussion_topics
()
self
.
assertTrue
(
self
.
cohort_management_page
.
discussion_topics_visible
())
self
.
assertEqual
(
"Course-Wide Discussion Topics"
,
self
.
cohort_management_page
.
cohort_discussion_heading_is_visible
(
self
.
course_wide_key
)
)
self
.
assertTrue
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
course_wide_key
))
self
.
assertEqual
(
"Content-Specific Discussion Topics"
,
self
.
cohort_management_page
.
cohort_discussion_heading_is_visible
(
self
.
inline_key
)
)
self
.
assertTrue
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
def
save_and_verify_discussion_topics
(
self
,
key
):
"""
Saves the discussion topics and the verify the changes.
"""
# click on the inline save button.
self
.
cohort_management_page
.
save_discussion_topics
(
key
)
# verifies that changes saved successfully.
confirmation_message
=
self
.
cohort_management_page
.
get_cohort_discussions_message
(
key
=
key
)
self
.
assertEqual
(
"Your changes have been saved."
,
confirmation_message
)
# save button disabled again.
self
.
assertTrue
(
self
.
cohort_management_page
.
is_save_button_disabled
(
key
))
def
reload_page
(
self
):
"""
Refresh the page.
"""
self
.
browser
.
refresh
()
self
.
cohort_management_page
.
wait_for_page
()
self
.
instructor_dashboard_page
.
select_cohort_management
()
self
.
cohort_management_page
.
wait_for_page
()
self
.
cohort_discussion_topics_are_visible
()
def
verify_discussion_topics_after_reload
(
self
,
key
,
cohorted_topics
):
"""
Verifies the changed topics.
"""
self
.
reload_page
()
self
.
assertEqual
(
self
.
cohort_management_page
.
get_cohorted_topics_count
(
key
),
cohorted_topics
)
def
test_cohort_course_wide_discussion_topic
(
self
):
"""
Scenario: cohort a course-wide discussion topic.
Given I have a course with a cohort defined,
And a course-wide discussion with disabled Save button.
When I click on the course-wide discussion topic
Then I see the enabled save button
When I click on save button
Then I see success message
When I reload the page
Then I see the discussion topic selected
"""
self
.
cohort_discussion_topics_are_visible
()
cohorted_topics_before
=
self
.
cohort_management_page
.
get_cohorted_topics_count
(
self
.
course_wide_key
)
self
.
cohort_management_page
.
select_discussion_topic
(
self
.
course_wide_key
)
self
.
assertFalse
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
course_wide_key
))
self
.
save_and_verify_discussion_topics
(
key
=
self
.
course_wide_key
)
cohorted_topics_after
=
self
.
cohort_management_page
.
get_cohorted_topics_count
(
self
.
course_wide_key
)
self
.
assertNotEqual
(
cohorted_topics_before
,
cohorted_topics_after
)
self
.
verify_discussion_topics_after_reload
(
self
.
course_wide_key
,
cohorted_topics_after
)
def
test_always_cohort_inline_topic_enabled
(
self
):
"""
Scenario: Select the always_cohort_inline_topics radio button
Given I have a course with a cohort defined,
And an inline discussion topic with disabled Save button.
When I click on always_cohort_inline_topics
Then I see enabled save button
And I see disabled inline discussion topics
When I save the change
And I reload the page
Then I see the always_cohort_inline_topics option enabled
"""
self
.
cohort_discussion_topics_are_visible
()
# enable always inline discussion topics and save the change
self
.
cohort_management_page
.
select_always_inline_discussion
()
self
.
assertFalse
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
self
.
assertTrue
(
self
.
cohort_management_page
.
inline_discussion_topics_disabled
())
self
.
cohort_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertTrue
(
self
.
cohort_management_page
.
always_inline_discussion_selected
())
def
test_cohort_some_inline_topics_enabled
(
self
):
"""
Scenario: Select the cohort_some_inline_topics radio button
Given I have a course with a cohort defined and always_cohort_inline_topics set to True
And an inline discussion topic with disabled Save button.
When I click on cohort_some_inline_topics
Then I see enabled save button
And I see enabled inline discussion topics
When I save the change
And I reload the page
Then I see the cohort_some_inline_topics option enabled
"""
self
.
cohort_discussion_topics_are_visible
()
# By default always inline discussion topics is False. Enable it (and reload the page).
self
.
assertFalse
(
self
.
cohort_management_page
.
always_inline_discussion_selected
())
self
.
cohort_management_page
.
select_always_inline_discussion
()
self
.
cohort_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertFalse
(
self
.
cohort_management_page
.
cohort_some_inline_discussion_selected
())
# enable some inline discussion topic radio button.
self
.
cohort_management_page
.
select_cohort_some_inline_discussion
()
# I see that save button is enabled
self
.
assertFalse
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
# I see that inline discussion topics are enabled
self
.
assertFalse
(
self
.
cohort_management_page
.
inline_discussion_topics_disabled
())
self
.
cohort_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertTrue
(
self
.
cohort_management_page
.
cohort_some_inline_discussion_selected
())
def
test_cohort_inline_discussion_topic
(
self
):
"""
Scenario: cohort inline discussion topic.
Given I have a course with a cohort defined,
And a inline discussion topic with disabled Save button
And When I click on inline discussion topic
And I see enabled save button
And When i click save button
Then I see success message
When I reload the page
Then I see the discussion topic selected
"""
self
.
cohort_discussion_topics_are_visible
()
cohorted_topics_before
=
self
.
cohort_management_page
.
get_cohorted_topics_count
(
self
.
inline_key
)
# check the discussion topic.
self
.
cohort_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# Save button enabled.
self
.
assertFalse
(
self
.
cohort_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
# verifies that changes saved successfully.
self
.
save_and_verify_discussion_topics
(
key
=
self
.
inline_key
)
cohorted_topics_after
=
self
.
cohort_management_page
.
get_cohorted_topics_count
(
self
.
inline_key
)
self
.
assertNotEqual
(
cohorted_topics_before
,
cohorted_topics_after
)
self
.
verify_discussion_topics_after_reload
(
self
.
inline_key
,
cohorted_topics_after
)
def
test_verify_that_selecting_the_final_child_selects_category
(
self
):
"""
Scenario: Category should be selected on selecting final child.
Given I have a course with a cohort defined,
And a inline discussion with disabled Save button.
When I click on child topics
Then I see enabled saved button
Then I see parent category to be checked.
"""
self
.
cohort_discussion_topics_are_visible
()
# category should not be selected.
self
.
assertFalse
(
self
.
cohort_management_page
.
is_category_selected
())
# check the discussion topic.
self
.
cohort_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# verify that category is selected.
self
.
assertTrue
(
self
.
cohort_management_page
.
is_category_selected
())
def
test_verify_that_deselecting_the_final_child_deselects_category
(
self
):
"""
Scenario: Category should be deselected on deselecting final child.
Given I have a course with a cohort defined,
And a inline discussion with disabled Save button.
When I click on final child topics
Then I see enabled saved button
Then I see parent category to be deselected.
"""
self
.
cohort_discussion_topics_are_visible
()
# category should not be selected.
self
.
assertFalse
(
self
.
cohort_management_page
.
is_category_selected
())
# check the discussion topic.
self
.
cohort_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# verify that category is selected.
self
.
assertTrue
(
self
.
cohort_management_page
.
is_category_selected
())
# un-check the discussion topic.
self
.
cohort_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# category should not be selected.
self
.
assertFalse
(
self
.
cohort_management_page
.
is_category_selected
())
@attr
(
shard
=
6
)
class
CohortContentGroupAssociationTest
(
UniqueCourseTest
,
CohortTestMixin
):
class
CohortContentGroupAssociationTest
(
UniqueCourseTest
,
CohortTestMixin
):
"""
"""
Tests for linking between content groups and cohort in the instructor dashboard.
Tests for linking between content groups and cohort in the instructor dashboard.
...
...
common/test/acceptance/tests/discussion/test_discussion_management.py
0 → 100644
View file @
c23c0b99
# -*- coding: utf-8 -*-
"""
End-to-end tests related to the divided discussion management on the LMS Instructor Dashboard
"""
from
nose.plugins.attrib
import
attr
from
common.test.acceptance.tests.discussion.helpers
import
CohortTestMixin
from
common.test.acceptance.tests.helpers
import
UniqueCourseTest
from
common.test.acceptance.fixtures.course
import
CourseFixture
,
XBlockFixtureDesc
from
common.test.acceptance.pages.lms.auto_auth
import
AutoAuthPage
from
common.test.acceptance.pages.lms.instructor_dashboard
import
InstructorDashboardPage
import
uuid
@attr
(
shard
=
6
)
class
DividedDiscussionTopicsTest
(
UniqueCourseTest
,
CohortTestMixin
):
"""
Tests for dividing the inline and course-wide discussion topics.
"""
def
setUp
(
self
):
"""
Set up a discussion topic
"""
super
(
DividedDiscussionTopicsTest
,
self
)
.
setUp
()
self
.
discussion_id
=
"test_discussion_{}"
.
format
(
uuid
.
uuid4
()
.
hex
)
self
.
course_fixture
=
CourseFixture
(
**
self
.
course_info
)
.
add_children
(
XBlockFixtureDesc
(
"chapter"
,
"Test Section"
)
.
add_children
(
XBlockFixtureDesc
(
"sequential"
,
"Test Subsection"
)
.
add_children
(
XBlockFixtureDesc
(
"vertical"
,
"Test Unit"
)
.
add_children
(
XBlockFixtureDesc
(
"discussion"
,
"Test Discussion"
,
metadata
=
{
"discussion_id"
:
self
.
discussion_id
}
)
)
)
)
)
.
install
()
# create course with single cohort and two content groups (user_partition of type "cohort")
self
.
cohort_name
=
"OnlyCohort"
self
.
setup_cohort_config
(
self
.
course_fixture
)
self
.
cohort_id
=
self
.
add_manual_cohort
(
self
.
course_fixture
,
self
.
cohort_name
)
# login as an instructor
self
.
instructor_name
=
"instructor_user"
self
.
instructor_id
=
AutoAuthPage
(
self
.
browser
,
username
=
self
.
instructor_name
,
email
=
"instructor_user@example.com"
,
course_id
=
self
.
course_id
,
staff
=
True
)
.
visit
()
.
get_user_id
()
# go to the membership page on the instructor dashboard
self
.
instructor_dashboard_page
=
InstructorDashboardPage
(
self
.
browser
,
self
.
course_id
)
self
.
instructor_dashboard_page
.
visit
()
self
.
discussion_management_page
=
self
.
instructor_dashboard_page
.
select_discussion_management
()
self
.
discussion_management_page
.
wait_for_page
()
self
.
course_wide_key
=
'course-wide'
self
.
inline_key
=
'inline'
def
divided_discussion_topics_are_visible
(
self
):
"""
Assert that discussion topics are visible with appropriate content.
"""
self
.
assertTrue
(
self
.
discussion_management_page
.
discussion_topics_visible
())
self
.
assertEqual
(
"Course-Wide Discussion Topics"
,
self
.
discussion_management_page
.
divided_discussion_heading_is_visible
(
self
.
course_wide_key
)
)
self
.
assertTrue
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
course_wide_key
))
self
.
assertEqual
(
"Content-Specific Discussion Topics"
,
self
.
discussion_management_page
.
divided_discussion_heading_is_visible
(
self
.
inline_key
)
)
self
.
assertTrue
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
def
save_and_verify_discussion_topics
(
self
,
key
):
"""
Saves the discussion topics and the verify the changes.
"""
# click on the inline save button.
self
.
discussion_management_page
.
save_discussion_topics
(
key
)
# verifies that changes saved successfully.
confirmation_message
=
self
.
discussion_management_page
.
get_divide_discussions_message
(
key
=
key
)
self
.
assertEqual
(
"Your changes have been saved."
,
confirmation_message
)
# save button disabled again.
self
.
assertTrue
(
self
.
discussion_management_page
.
is_save_button_disabled
(
key
))
def
reload_page
(
self
):
"""
Refresh the page.
"""
self
.
browser
.
refresh
()
self
.
discussion_management_page
.
wait_for_page
()
self
.
instructor_dashboard_page
.
select_discussion_management
()
self
.
discussion_management_page
.
wait_for_page
()
self
.
divided_discussion_topics_are_visible
()
def
verify_discussion_topics_after_reload
(
self
,
key
,
divided_topics
):
"""
Verifies the changed topics.
"""
self
.
reload_page
()
self
.
assertEqual
(
self
.
discussion_management_page
.
get_divided_topics_count
(
key
),
divided_topics
)
def
test_divide_course_wide_discussion_topic
(
self
):
"""
Scenario: divide a course-wide discussion topic.
Given I have a course with a divide defined,
And a course-wide discussion with disabled Save button.
When I click on the course-wide discussion topic
Then I see the enabled save button
When I click on save button
Then I see success message
When I reload the page
Then I see the discussion topic selected
"""
self
.
divided_discussion_topics_are_visible
()
divided_topics_before
=
self
.
discussion_management_page
.
get_divided_topics_count
(
self
.
course_wide_key
)
self
.
discussion_management_page
.
select_discussion_topic
(
self
.
course_wide_key
)
self
.
assertFalse
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
course_wide_key
))
self
.
save_and_verify_discussion_topics
(
key
=
self
.
course_wide_key
)
divided_topics_after
=
self
.
discussion_management_page
.
get_divided_topics_count
(
self
.
course_wide_key
)
self
.
assertNotEqual
(
divided_topics_before
,
divided_topics_after
)
self
.
verify_discussion_topics_after_reload
(
self
.
course_wide_key
,
divided_topics_after
)
def
test_always_divide_inline_topic_enabled
(
self
):
"""
Scenario: Select the always_divide_inline_topics radio button
Given I have a course with a cohort defined,
And an inline discussion topic with disabled Save button.
When I click on always_divide_inline_topics
Then I see enabled save button
And I see disabled inline discussion topics
When I save the change
And I reload the page
Then I see the always_divide_inline_topics option enabled
"""
self
.
divided_discussion_topics_are_visible
()
# enable always inline discussion topics and save the change
self
.
discussion_management_page
.
select_always_inline_discussion
()
self
.
assertFalse
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
self
.
assertTrue
(
self
.
discussion_management_page
.
inline_discussion_topics_disabled
())
self
.
discussion_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertTrue
(
self
.
discussion_management_page
.
always_inline_discussion_selected
())
def
test_divide_some_inline_topics_enabled
(
self
):
"""
Scenario: Select the divide_some_inline_topics radio button
Given I have a course with a divide defined and always_divide_inline_topics set to True
And an inline discussion topic with disabled Save button.
When I click on divide_some_inline_topics
Then I see enabled save button
And I see enabled inline discussion topics
When I save the change
And I reload the page
Then I see the divide_some_inline_topics option enabled
"""
self
.
divided_discussion_topics_are_visible
()
# By default always inline discussion topics is False. Enable it (and reload the page).
self
.
assertFalse
(
self
.
discussion_management_page
.
always_inline_discussion_selected
())
self
.
discussion_management_page
.
select_always_inline_discussion
()
self
.
discussion_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertFalse
(
self
.
discussion_management_page
.
divide_some_inline_discussion_selected
())
# enable some inline discussion topic radio button.
self
.
discussion_management_page
.
select_divide_some_inline_discussion
()
# I see that save button is enabled
self
.
assertFalse
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
# I see that inline discussion topics are enabled
self
.
assertFalse
(
self
.
discussion_management_page
.
inline_discussion_topics_disabled
())
self
.
discussion_management_page
.
save_discussion_topics
(
key
=
self
.
inline_key
)
self
.
reload_page
()
self
.
assertTrue
(
self
.
discussion_management_page
.
divide_some_inline_discussion_selected
())
def
test_divide_inline_discussion_topic
(
self
):
"""
Scenario: divide inline discussion topic.
Given I have a course with a divide defined,
And a inline discussion topic with disabled Save button
And When I click on inline discussion topic
And I see enabled save button
And When i click save button
Then I see success message
When I reload the page
Then I see the discussion topic selected
"""
self
.
divided_discussion_topics_are_visible
()
divided_topics_before
=
self
.
discussion_management_page
.
get_divided_topics_count
(
self
.
inline_key
)
# check the discussion topic.
self
.
discussion_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# Save button enabled.
self
.
assertFalse
(
self
.
discussion_management_page
.
is_save_button_disabled
(
self
.
inline_key
))
# verifies that changes saved successfully.
self
.
save_and_verify_discussion_topics
(
key
=
self
.
inline_key
)
divided_topics_after
=
self
.
discussion_management_page
.
get_divided_topics_count
(
self
.
inline_key
)
self
.
assertNotEqual
(
divided_topics_before
,
divided_topics_after
)
self
.
verify_discussion_topics_after_reload
(
self
.
inline_key
,
divided_topics_after
)
def
test_verify_that_selecting_the_final_child_selects_category
(
self
):
"""
Scenario: Category should be selected on selecting final child.
Given I have a course with a cohort defined,
And a inline discussion with disabled Save button.
When I click on child topics
Then I see enabled saved button
Then I see parent category to be checked.
"""
self
.
divided_discussion_topics_are_visible
()
# category should not be selected.
self
.
assertFalse
(
self
.
discussion_management_page
.
is_category_selected
())
# check the discussion topic.
self
.
discussion_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# verify that category is selected.
self
.
assertTrue
(
self
.
discussion_management_page
.
is_category_selected
())
def
test_verify_that_deselecting_the_final_child_deselects_category
(
self
):
"""
Scenario: Category should be deselected on deselecting final child.
Given I have a course with a cohort defined,
And a inline discussion with disabled Save button.
When I click on final child topics
Then I see enabled saved button
Then I see parent category to be deselected.
"""
self
.
divided_discussion_topics_are_visible
()
# category should not be selected.
self
.
assertFalse
(
self
.
discussion_management_page
.
is_category_selected
())
# check the discussion topic.
self
.
discussion_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# verify that category is selected.
self
.
assertTrue
(
self
.
discussion_management_page
.
is_category_selected
())
# un-check the discussion topic.
self
.
discussion_management_page
.
select_discussion_topic
(
self
.
inline_key
)
# category should not be selected.
self
.
assertFalse
(
self
.
discussion_management_page
.
is_category_selected
())
lms/djangoapps/django_comment_client/tests/test_utils.py
View file @
c23c0b99
...
@@ -26,7 +26,7 @@ from courseware.tests.factories import InstructorFactory
...
@@ -26,7 +26,7 @@ from courseware.tests.factories import InstructorFactory
from
courseware.tabs
import
get_course_tab_list
from
courseware.tabs
import
get_course_tab_list
from
openedx.core.djangoapps.course_groups
import
cohorts
from
openedx.core.djangoapps.course_groups
import
cohorts
from
openedx.core.djangoapps.course_groups.cohorts
import
set_course_cohorted
from
openedx.core.djangoapps.course_groups.cohorts
import
set_course_cohorted
from
openedx.core.djangoapps.course_groups.tests.helpers
import
config_course_cohorts
,
topic_name_to_id
,
CohortFactory
from
openedx.core.djangoapps.course_groups.tests.helpers
import
config_course_cohorts
,
config_course_discussions
,
topic_name_to_id
,
CohortFactory
from
student.tests.factories
import
UserFactory
,
AdminFactory
,
CourseEnrollmentFactory
from
student.tests.factories
import
UserFactory
,
AdminFactory
,
CourseEnrollmentFactory
from
openedx.core.djangoapps.content.course_structures.models
import
CourseStructure
from
openedx.core.djangoapps.content.course_structures.models
import
CourseStructure
from
openedx.core.djangoapps.util.testing
import
ContentGroupTestCase
from
openedx.core.djangoapps.util.testing
import
ContentGroupTestCase
...
@@ -478,7 +478,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
...
@@ -478,7 +478,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
}
}
)
)
def
test_inline_with_always_
cohort
_inline_discussion_flag
(
self
):
def
test_inline_with_always_
divide
_inline_discussion_flag
(
self
):
self
.
create_discussion
(
"Chapter"
,
"Discussion"
)
self
.
create_discussion
(
"Chapter"
,
"Discussion"
)
set_discussion_division_settings
(
self
.
course
.
id
,
enable_cohorts
=
True
,
always_divide_inline_discussions
=
True
)
set_discussion_division_settings
(
self
.
course
.
id
,
enable_cohorts
=
True
,
always_divide_inline_discussions
=
True
)
...
@@ -502,7 +502,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
...
@@ -502,7 +502,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
}
}
)
)
def
test_inline_without_always_
cohort
_inline_discussion_flag
(
self
):
def
test_inline_without_always_
divide
_inline_discussion_flag
(
self
):
self
.
create_discussion
(
"Chapter"
,
"Discussion"
)
self
.
create_discussion
(
"Chapter"
,
"Discussion"
)
set_discussion_division_settings
(
self
.
course
.
id
,
enable_cohorts
=
True
)
set_discussion_division_settings
(
self
.
course
.
id
,
enable_cohorts
=
True
)
...
@@ -1301,15 +1301,16 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
...
@@ -1301,15 +1301,16 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
)
)
# not cohorted
# not cohorted
config_course_cohorts
(
course
,
is_cohorted
=
False
,
discussion_topics
=
[
"General"
,
"Feedback"
]
)
config_course_cohorts
(
course
,
is_cohorted
=
False
)
config_course_discussions
(
course
,
discussion_topics
=
[
"General"
,
"Feedback"
])
self
.
assertFalse
(
self
.
assertFalse
(
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"General"
)),
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"General"
)),
"Course isn't cohorted"
"Course isn't cohorted"
)
)
# cohorted, but top level topics aren't
# cohorted, but top level topics aren't
config_course_cohorts
(
course
,
is_cohorted
=
True
,
discussion_topics
=
[
"General"
,
"Feedback"
])
config_course_cohorts
(
course
,
is_cohorted
=
True
)
config_course_discussions
(
course
,
discussion_topics
=
[
"General"
,
"Feedback"
])
self
.
assertTrue
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
assertTrue
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
assertFalse
(
self
.
assertFalse
(
...
@@ -1320,10 +1321,9 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
...
@@ -1320,10 +1321,9 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
# cohorted, including "Feedback" top-level topics aren't
# cohorted, including "Feedback" top-level topics aren't
config_course_cohorts
(
config_course_cohorts
(
course
,
course
,
is_cohorted
=
True
,
is_cohorted
=
True
discussion_topics
=
[
"General"
,
"Feedback"
],
divided_discussions
=
[
"Feedback"
]
)
)
config_course_discussions
(
course
,
discussion_topics
=
[
"General"
,
"Feedback"
],
divided_discussions
=
[
"Feedback"
])
self
.
assertTrue
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
assertTrue
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
assertFalse
(
self
.
assertFalse
(
...
@@ -1345,42 +1345,52 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
...
@@ -1345,42 +1345,52 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
config_course_cohorts
(
config_course_cohorts
(
course
,
course
,
is_cohorted
=
True
,
is_cohorted
=
True
,
)
config_course_discussions
(
course
,
discussion_topics
=
[
"General"
,
"Feedback"
],
discussion_topics
=
[
"General"
,
"Feedback"
],
divided_discussions
=
[
"Feedback"
,
"random_inline"
]
divided_discussions
=
[
"Feedback"
,
"random_inline"
]
)
)
self
.
assertFalse
(
self
.
assertFalse
(
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random"
)),
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random"
)),
"By default, Non-top-level discussions are not cohorted in a cohorted courses."
"By default, Non-top-level discussions are not cohorted in a cohorted courses."
)
)
# if always_
cohort
_inline_discussions is set to False, non-top-level discussion are always
# if always_
divide
_inline_discussions is set to False, non-top-level discussion are always
# no
n cohorted unless they are explicitly set in cohort
ed_discussions
# no
t divided unless they are explicitly set in divid
ed_discussions
config_course_cohorts
(
config_course_cohorts
(
course
,
course
,
is_cohorted
=
True
,
is_cohorted
=
True
,
)
config_course_discussions
(
course
,
discussion_topics
=
[
"General"
,
"Feedback"
],
discussion_topics
=
[
"General"
,
"Feedback"
],
divided_discussions
=
[
"Feedback"
,
"random_inline"
],
divided_discussions
=
[
"Feedback"
,
"random_inline"
],
always_divide_inline_discussions
=
False
always_divide_inline_discussions
=
False
)
)
self
.
assertFalse
(
self
.
assertFalse
(
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random"
)),
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random"
)),
"Non-top-level discussion is not cohorted if always_
cohort
_inline_discussions is False."
"Non-top-level discussion is not cohorted if always_
divide
_inline_discussions is False."
)
)
self
.
assertTrue
(
self
.
assertTrue
(
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random_inline"
)),
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"random_inline"
)),
"If always_
cohort
_inline_discussions set to False, Non-top-level discussion is "
"If always_
divide
_inline_discussions set to False, Non-top-level discussion is "
"cohorted if explicitly set in cohorted_discussions."
"cohorted if explicitly set in cohorted_discussions."
)
)
self
.
assertTrue
(
self
.
assertTrue
(
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"Feedback"
)),
utils
.
is_commentable_divided
(
course
.
id
,
to_id
(
"Feedback"
)),
"If always_
cohort
_inline_discussions set to False, top-level discussion are not affected."
"If always_
divide
_inline_discussions set to False, top-level discussion are not affected."
)
)
def
test_is_commentable_divided_team
(
self
):
def
test_is_commentable_divided_team
(
self
):
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
course
=
modulestore
()
.
get_course
(
self
.
toy_course_key
)
self
.
assertFalse
(
cohorts
.
is_course_cohorted
(
course
.
id
))
self
.
assertFalse
(
cohorts
.
is_course_cohorted
(
course
.
id
))
config_course_cohorts
(
course
,
is_cohorted
=
True
,
always_divide_inline_discussions
=
True
)
config_course_cohorts
(
course
,
is_cohorted
=
True
)
config_course_discussions
(
course
,
always_divide_inline_discussions
=
True
)
team
=
CourseTeamFactory
(
course_id
=
course
.
id
)
team
=
CourseTeamFactory
(
course_id
=
course
.
id
)
# Verify that team discussions are not cohorted, but other discussions are
# Verify that team discussions are not cohorted, but other discussions are
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
c23c0b99
...
@@ -125,6 +125,7 @@ def instructor_dashboard_2(request, course_id):
...
@@ -125,6 +125,7 @@ def instructor_dashboard_2(request, course_id):
_section_course_info
(
course
,
access
),
_section_course_info
(
course
,
access
),
_section_membership
(
course
,
access
,
is_white_label
),
_section_membership
(
course
,
access
,
is_white_label
),
_section_cohort_management
(
course
,
access
),
_section_cohort_management
(
course
,
access
),
_section_discussions_management
(
course
,
access
),
_section_student_admin
(
course
,
access
),
_section_student_admin
(
course
,
access
),
_section_data_download
(
course
,
access
),
_section_data_download
(
course
,
access
),
]
]
...
@@ -513,7 +514,6 @@ def _section_cohort_management(course, access):
...
@@ -513,7 +514,6 @@ def _section_cohort_management(course, access):
),
),
'cohorts_url'
:
reverse
(
'cohorts'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}),
'cohorts_url'
:
reverse
(
'cohorts'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}),
'upload_cohorts_csv_url'
:
reverse
(
'add_users_to_cohorts'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'upload_cohorts_csv_url'
:
reverse
(
'add_users_to_cohorts'
,
kwargs
=
{
'course_id'
:
unicode
(
course_key
)}),
'discussion_topics_url'
:
reverse
(
'cohort_discussion_topics'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}),
'verified_track_cohorting_url'
:
reverse
(
'verified_track_cohorting_url'
:
reverse
(
'verified_track_cohorting'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}
'verified_track_cohorting'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}
),
),
...
@@ -521,6 +521,21 @@ def _section_cohort_management(course, access):
...
@@ -521,6 +521,21 @@ def _section_cohort_management(course, access):
return
section_data
return
section_data
def
_section_discussions_management
(
course
,
access
):
""" Provide data for the corresponding discussion management section """
course_key
=
course
.
id
section_data
=
{
'section_key'
:
'discussions_management'
,
'section_display_name'
:
_
(
'Discussions'
),
'discussion_topics_url'
:
reverse
(
'discussion_topics'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}),
'course_discussion_settings'
:
reverse
(
'course_discussions_settings'
,
kwargs
=
{
'course_key_string'
:
unicode
(
course_key
)}
),
}
return
section_data
def
_is_small_course
(
course_key
):
def
_is_small_course
(
course_key
):
""" Compares against MAX_ENROLLMENT_INSTR_BUTTONS to determine if course enrollment is considered small. """
""" Compares against MAX_ENROLLMENT_INSTR_BUTTONS to determine if course enrollment is considered small. """
is_small_course
=
False
is_small_course
=
False
...
...
lms/envs/common.py
View file @
c23c0b99
...
@@ -1759,6 +1759,7 @@ REQUIRE_JS_PATH_OVERRIDES = {
...
@@ -1759,6 +1759,7 @@ REQUIRE_JS_PATH_OVERRIDES = {
'js/student_profile/views/learner_profile_factory'
:
'js/student_profile/views/learner_profile_factory.js'
,
'js/student_profile/views/learner_profile_factory'
:
'js/student_profile/views/learner_profile_factory.js'
,
'js/courseware/courseware_factory'
:
'js/courseware/courseware_factory.js'
,
'js/courseware/courseware_factory'
:
'js/courseware/courseware_factory.js'
,
'js/groups/views/cohorts_dashboard_factory'
:
'js/groups/views/cohorts_dashboard_factory.js'
,
'js/groups/views/cohorts_dashboard_factory'
:
'js/groups/views/cohorts_dashboard_factory.js'
,
'js/groups/discussions_management/discussions_dashboard_factory'
:
'js/discussions_management/views/discussions_dashboard_factory.js'
,
'draggabilly'
:
'js/vendor/draggabilly.js'
,
'draggabilly'
:
'js/vendor/draggabilly.js'
,
'hls'
:
'common/js/vendor/hls.js'
'hls'
:
'common/js/vendor/hls.js'
}
}
...
...
lms/static/js/
groups/models/cohort_discussions
.js
→
lms/static/js/
discussions_management/models/course_discussions_detail
.js
View file @
c23c0b99
(
function
(
define
)
{
(
function
(
define
)
{
'use strict'
;
'use strict'
;
define
([
'backbone'
],
function
(
Backbone
)
{
define
([
'backbone'
],
function
(
Backbone
)
{
var
DiscussionTopicsSetting
sModel
=
Backbone
.
Model
.
extend
({
var
CourseDiscussionTopicDetail
sModel
=
Backbone
.
Model
.
extend
({
defaults
:
{
defaults
:
{
course_wide_discussions
:
{},
course_wide_discussions
:
{},
inline_discussions
:
{}
inline_discussions
:
{}
}
}
});
});
return
DiscussionTopicsSetting
sModel
;
return
CourseDiscussionTopicDetail
sModel
;
});
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/discussions_management/models/course_discussions_settings.js
0 → 100644
View file @
c23c0b99
(
function
(
define
)
{
'use strict'
;
define
([
'backbone'
],
function
(
Backbone
)
{
var
CourseDiscussionsSettingsModel
=
Backbone
.
Model
.
extend
({
idAttribute
:
'id'
,
defaults
:
{
divided_inline_discussions
:
[],
divided_course_wide_discussions
:
[],
always_divide_inline_discussions
:
false
}
});
return
CourseDiscussionsSettingsModel
;
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/discussions_management/views/discussions.js
0 → 100644
View file @
c23c0b99
(
function
(
define
)
{
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/discussions_management/views/divided_discussions_inline'
,
'js/discussions_management/views/divided_discussions_course_wide'
,
'edx-ui-toolkit/js/utils/html-utils'
],
function
(
$
,
_
,
Backbone
,
gettext
,
InlineDiscussionsView
,
CourseWideDiscussionsView
,
HtmlUtils
)
{
var
DiscussionsView
=
Backbone
.
View
.
extend
({
initialize
:
function
(
options
)
{
this
.
template
=
HtmlUtils
.
template
(
$
(
'#discussions-tpl'
).
text
());
this
.
context
=
options
.
context
;
this
.
discussionSettings
=
options
.
discussionSettings
;
},
render
:
function
()
{
HtmlUtils
.
setHtml
(
this
.
$el
,
this
.
template
({}));
this
.
showDiscussionTopics
();
return
this
;
},
getSectionCss
:
function
(
section
)
{
return
".instructor-nav .nav-item [data-section='"
+
section
+
"']"
;
},
showDiscussionTopics
:
function
()
{
var
dividedDiscussionsElement
=
this
.
$
(
'.discussions-nav'
);
if
(
!
this
.
CourseWideDiscussionsView
)
{
this
.
CourseWideDiscussionsView
=
new
CourseWideDiscussionsView
({
el
:
dividedDiscussionsElement
,
model
:
this
.
context
.
courseDiscussionTopicDetailsModel
,
discussionSettings
:
this
.
discussionSettings
}).
render
();
}
if
(
!
this
.
InlineDiscussionsView
)
{
this
.
InlineDiscussionsView
=
new
InlineDiscussionsView
({
el
:
dividedDiscussionsElement
,
model
:
this
.
context
.
courseDiscussionTopicDetailsModel
,
discussionSettings
:
this
.
discussionSettings
}).
render
();
}
}
});
return
DiscussionsView
;
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/discussions_management/views/discussions_dashboard_factory.js
0 → 100644
View file @
c23c0b99
(
function
(
define
)
{
'use strict'
;
define
(
'js/discussions_management/views/discussions_dashboard_factory'
,
[
'jquery'
,
'js/discussions_management/views/discussions'
,
'js/discussions_management/models/course_discussions_detail'
,
'js/discussions_management/models/course_discussions_settings'
],
function
(
$
,
DiscussionsView
,
CourseDiscussionTopicDetailsModel
,
CourseDiscussionsSettingsModel
)
{
return
function
()
{
var
courseDiscussionSettings
=
new
CourseDiscussionsSettingsModel
(),
discussionTopicsSettings
=
new
CourseDiscussionTopicDetailsModel
(),
$discussionsManagementElement
=
$
(
'.discussions-management'
),
discussionsView
;
courseDiscussionSettings
.
url
=
$discussionsManagementElement
.
data
(
'course-discussion-settings-url'
);
discussionTopicsSettings
.
url
=
$discussionsManagementElement
.
data
(
'discussion-topics-url'
);
discussionsView
=
new
DiscussionsView
({
el
:
$discussionsManagementElement
,
discussionSettings
:
courseDiscussionSettings
,
context
:
{
courseDiscussionTopicDetailsModel
:
discussionTopicsSettings
}
});
courseDiscussionSettings
.
fetch
().
done
(
function
()
{
discussionTopicsSettings
.
fetch
().
done
(
function
()
{
discussionsView
.
render
();
});
});
};
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/
groups/views/cohort
_discussions.js
→
lms/static/js/
discussions_management/views/divided
_discussions.js
View file @
c23c0b99
(
function
(
define
)
{
(
function
(
define
)
{
'use strict'
;
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/models/notification'
,
'js/views/notification'
],
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/models/notification'
,
'js/views/notification'
],
function
(
$
,
_
,
Backbone
)
{
function
(
$
,
_
,
Backbone
,
gettext
)
{
var
CohortDiscussionConfigurationView
=
Backbone
.
View
.
extend
({
/* global NotificationModel, NotificationView */
var
DividedDiscussionConfigurationView
=
Backbone
.
View
.
extend
({
/**
/**
* Add/Remove the disabled attribute on given element.
* Add/Remove the disabled attribute on given element.
...
@@ -14,53 +15,53 @@
...
@@ -14,53 +15,53 @@
},
},
/**
/**
* Returns the
cohort
ed discussions list.
* Returns the
divid
ed discussions list.
* @param {string} selector - To select the discussion elements whose ids to return.
* @param {string} selector - To select the discussion elements whose ids to return.
* @returns {Array} -
Cohort
ed discussions.
* @returns {Array} -
Divid
ed discussions.
*/
*/
get
Cohort
edDiscussions
:
function
(
selector
)
{
get
Divid
edDiscussions
:
function
(
selector
)
{
var
self
=
this
,
var
self
=
this
,
cohort
edDiscussions
=
[];
divid
edDiscussions
=
[];
_
.
each
(
self
.
$
(
selector
),
function
(
topic
)
{
_
.
each
(
self
.
$
(
selector
),
function
(
topic
)
{
cohort
edDiscussions
.
push
(
$
(
topic
).
data
(
'id'
));
divid
edDiscussions
.
push
(
$
(
topic
).
data
(
'id'
));
});
});
return
cohort
edDiscussions
;
return
divid
edDiscussions
;
},
},
/**
/**
* Save the
cohort
Settings' changed attributes to the server via PATCH method.
* Save the
discussion
Settings' changed attributes to the server via PATCH method.
* It shows the error message(s) if any.
* It shows the error message(s) if any.
* @param {object} $element - Messages would be shown before this element.
* @param {object} $element - Messages would be shown before this element.
* @param {object} fieldData - Data to update on the server.
* @param {object} fieldData - Data to update on the server.
*/
*/
saveForm
:
function
(
$element
,
fieldData
)
{
saveForm
:
function
(
$element
,
fieldData
)
{
var
self
=
this
,
var
self
=
this
,
cohortSettingsModel
=
this
.
cohort
Settings
,
discussionSettingsModel
=
this
.
discussion
Settings
,
saveOperation
=
$
.
Deferred
(),
saveOperation
=
$
.
Deferred
(),
showErrorMessage
;
showErrorMessage
;
showErrorMessage
=
function
(
message
)
{
showErrorMessage
=
function
(
message
,
$element
)
{
self
.
showMessage
(
message
,
$element
,
'error'
);
self
.
showMessage
(
message
,
$element
,
'error'
);
};
};
this
.
removeNotification
();
this
.
removeNotification
();
cohort
SettingsModel
.
save
(
discussion
SettingsModel
.
save
(
fieldData
,
{
patch
:
true
,
wait
:
true
}
fieldData
,
{
patch
:
true
,
wait
:
true
}
).
done
(
function
()
{
).
done
(
function
()
{
saveOperation
.
resolve
();
saveOperation
.
resolve
();
}).
fail
(
function
(
result
)
{
}).
fail
(
function
(
result
)
{
var
errorMessage
=
null
;
var
errorMessage
=
null
,
jsonResponse
;
try
{
try
{
var
jsonResponse
=
JSON
.
parse
(
result
.
responseText
);
jsonResponse
=
JSON
.
parse
(
result
.
responseText
);
errorMessage
=
jsonResponse
.
error
;
errorMessage
=
jsonResponse
.
error
;
}
catch
(
e
)
{
}
catch
(
e
)
{
// Ignore the exception and show the default error message instead.
// Ignore the exception and show the default error message instead.
}
}
if
(
!
errorMessage
)
{
if
(
!
errorMessage
)
{
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
// eslint-disable-line max-len
}
}
showErrorMessage
(
errorMessage
,
$element
);
showErrorMessage
(
errorMessage
);
saveOperation
.
reject
();
saveOperation
.
reject
();
});
});
return
saveOperation
.
promise
();
return
saveOperation
.
promise
();
...
@@ -92,6 +93,6 @@
...
@@ -92,6 +93,6 @@
}
}
});
});
return
Cohort
DiscussionConfigurationView
;
return
Divided
DiscussionConfigurationView
;
});
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/static/js/
groups/views/cohort
_discussions_course_wide.js
→
lms/static/js/
discussions_management/views/divided
_discussions_course_wide.js
View file @
c23c0b99
(
function
(
define
)
{
(
function
(
define
)
{
'use strict'
;
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/
groups/views/cohort
_discussions'
,
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/
discussions_management/views/divided
_discussions'
,
'edx-ui-toolkit/js/utils/html-utils'
],
'edx-ui-toolkit/js/utils/html-utils'
],
function
(
$
,
_
,
Backbone
,
gettext
,
Cohort
DiscussionConfigurationView
,
HtmlUtils
)
{
function
(
$
,
_
,
Backbone
,
gettext
,
Divided
DiscussionConfigurationView
,
HtmlUtils
)
{
var
CourseWideDiscussionsView
=
Cohort
DiscussionConfigurationView
.
extend
({
var
CourseWideDiscussionsView
=
Divided
DiscussionConfigurationView
.
extend
({
events
:
{
events
:
{
'change .check-discussion-subcategory-course-wide'
:
'discussionCategoryStateChanged'
,
'change .check-discussion-subcategory-course-wide'
:
'discussionCategoryStateChanged'
,
'click .cohort-course-wide-discussions-form .action-save'
:
'saveCourseWideDiscussionsForm'
'click .cohort-course-wide-discussions-form .action-save'
:
'saveCourseWideDiscussionsForm'
},
},
initialize
:
function
(
options
)
{
initialize
:
function
(
options
)
{
this
.
template
=
HtmlUtils
.
template
(
$
(
'#
cohort
-discussions-course-wide-tpl'
).
text
());
this
.
template
=
HtmlUtils
.
template
(
$
(
'#
divided
-discussions-course-wide-tpl'
).
text
());
this
.
cohortSettings
=
options
.
cohort
Settings
;
this
.
discussionSettings
=
options
.
discussion
Settings
;
},
},
render
:
function
()
{
render
:
function
()
{
HtmlUtils
.
setHtml
(
this
.
$
(
'.co
hort-co
urse-wide-discussions-nav'
),
this
.
template
({
HtmlUtils
.
setHtml
(
this
.
$
(
'.course-wide-discussions-nav'
),
this
.
template
({
courseWideTopicsHtml
:
this
.
getCourseWideDiscussionsHtml
(
courseWideTopicsHtml
:
this
.
getCourseWideDiscussionsHtml
(
this
.
model
.
get
(
'course_wide_discussions'
)
this
.
model
.
get
(
'course_wide_discussions'
)
)
)
...
@@ -56,25 +56,27 @@
...
@@ -56,25 +56,27 @@
},
},
/**
/**
* Sends the co
horted_course_wide_d
iscussions to the server and renders the view.
* Sends the co
urseWideDividedD
iscussions to the server and renders the view.
*/
*/
saveCourseWideDiscussionsForm
:
function
(
event
)
{
saveCourseWideDiscussionsForm
:
function
(
event
)
{
event
.
preventDefault
();
var
self
=
this
,
var
self
=
this
,
courseWide
CohortedDiscussions
=
self
.
getCohort
edDiscussions
(
courseWide
DividedDiscussions
=
self
.
getDivid
edDiscussions
(
'.check-discussion-subcategory-course-wide:checked'
'.check-discussion-subcategory-course-wide:checked'
),
),
fieldData
=
{
cohorted_course_wide_discussions
:
courseWideCohortedDiscussions
};
fieldData
=
{
divided_course_wide_discussions
:
courseWideDividedDiscussions
};
event
.
preventDefault
();
self
.
saveForm
(
self
.
$
(
'.course-wide-discussion-topics'
),
fieldData
)
self
.
saveForm
(
self
.
$
(
'.course-wide-discussion-topics'
),
fieldData
)
.
done
(
function
()
{
.
done
(
function
()
{
self
.
model
.
fetch
()
self
.
model
.
fetch
()
.
done
(
function
()
{
.
done
(
function
()
{
self
.
render
();
self
.
render
();
self
.
showMessage
(
gettext
(
'Your changes have been saved.'
),
self
.
$
(
'.course-wide-discussion-topics'
));
self
.
showMessage
(
gettext
(
'Your changes have been saved.'
),
self
.
$
(
'.course-wide-discussion-topics'
)
);
}).
fail
(
function
()
{
}).
fail
(
function
()
{
var
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
var
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
// eslint-disable-line max-len
self
.
showMessage
(
errorMessage
,
self
.
$
(
'.course-wide-discussion-topics'
),
'error'
);
self
.
showMessage
(
errorMessage
,
self
.
$
(
'.course-wide-discussion-topics'
),
'error'
);
});
});
});
});
...
...
lms/static/js/
groups/views/cohort
_discussions_inline.js
→
lms/static/js/
discussions_management/views/divided
_discussions_inline.js
View file @
c23c0b99
(
function
(
define
)
{
(
function
(
define
)
{
'use strict'
;
'use strict'
;
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/
groups/views/cohort
_discussions'
,
define
([
'jquery'
,
'underscore'
,
'backbone'
,
'gettext'
,
'js/
discussions_management/views/divided
_discussions'
,
'edx-ui-toolkit/js/utils/html-utils'
,
'js/vendor/jquery.qubit'
],
'edx-ui-toolkit/js/utils/html-utils'
,
'js/vendor/jquery.qubit'
],
function
(
$
,
_
,
Backbone
,
gettext
,
Cohort
DiscussionConfigurationView
,
HtmlUtils
)
{
function
(
$
,
_
,
Backbone
,
gettext
,
Divided
DiscussionConfigurationView
,
HtmlUtils
)
{
var
InlineDiscussionsView
=
Cohort
DiscussionConfigurationView
.
extend
({
var
InlineDiscussionsView
=
Divided
DiscussionConfigurationView
.
extend
({
events
:
{
events
:
{
'change .check-discussion-category'
:
'setSaveButton'
,
'change .check-discussion-category'
:
'setSaveButton'
,
'change .check-discussion-subcategory-inline'
:
'setSaveButton'
,
'change .check-discussion-subcategory-inline'
:
'setSaveButton'
,
...
@@ -13,17 +13,19 @@
...
@@ -13,17 +13,19 @@
},
},
initialize
:
function
(
options
)
{
initialize
:
function
(
options
)
{
this
.
template
=
HtmlUtils
.
template
(
$
(
'#
cohort
-discussions-inline-tpl'
).
text
());
this
.
template
=
HtmlUtils
.
template
(
$
(
'#
divided
-discussions-inline-tpl'
).
text
());
this
.
cohortSettings
=
options
.
cohort
Settings
;
this
.
discussionSettings
=
options
.
discussion
Settings
;
},
},
render
:
function
()
{
render
:
function
()
{
var
alwaysCohortInlineDiscussions
=
this
.
cohortSettings
.
get
(
'always_cohort_inline_discussions'
),
var
inlineDiscussions
=
this
.
model
.
get
(
'inline_discussions'
),
inline_discussions
=
this
.
model
.
get
(
'inline_discussions'
);
alwaysDivideInlineDiscussions
=
this
.
discussionSettings
.
get
(
'always_divide_inline_discussions'
);
HtmlUtils
.
setHtml
(
this
.
$
(
'.
cohort-
inline-discussions-nav'
),
this
.
template
({
HtmlUtils
.
setHtml
(
this
.
$
(
'.inline-discussions-nav'
),
this
.
template
({
inlineDiscussionTopicsHtml
:
this
.
getInlineDiscussionsHtml
(
inline
_d
iscussions
),
inlineDiscussionTopicsHtml
:
this
.
getInlineDiscussionsHtml
(
inline
D
iscussions
),
always
CohortInlineDiscussions
:
alwaysCohort
InlineDiscussions
always
DivideInlineDiscussions
:
alwaysDivide
InlineDiscussions
}));
}));
// Provides the semantics for a nested list of tri-state checkboxes.
// Provides the semantics for a nested list of tri-state checkboxes.
...
@@ -32,7 +34,7 @@
...
@@ -32,7 +34,7 @@
// based on the checked values of any checkboxes in child elements of the DOM.
// based on the checked values of any checkboxes in child elements of the DOM.
this
.
$
(
'ul.inline-topics'
).
qubit
();
this
.
$
(
'ul.inline-topics'
).
qubit
();
this
.
setElementsEnabled
(
always
Cohort
InlineDiscussions
,
true
);
this
.
setElementsEnabled
(
always
Divide
InlineDiscussions
,
true
);
},
},
/**
/**
...
@@ -99,45 +101,48 @@
...
@@ -99,45 +101,48 @@
*
*
* Enable/Disable the category and sub-category checkboxes.
* Enable/Disable the category and sub-category checkboxes.
* Enable/Disable the save button.
* Enable/Disable the save button.
* @param {bool} enable
_c
heckboxes - The flag to enable/disable the checkboxes.
* @param {bool} enable
C
heckboxes - The flag to enable/disable the checkboxes.
* @param {bool} enable
_save_b
utton - The flag to enable/disable the save button.
* @param {bool} enable
SaveB
utton - The flag to enable/disable the save button.
*/
*/
setElementsEnabled
:
function
(
enable
_checkboxes
,
enable_save_b
utton
)
{
setElementsEnabled
:
function
(
enable
Checkboxes
,
enableSaveB
utton
)
{
this
.
setDisabled
(
this
.
$
(
'.check-discussion-category'
),
enable
_c
heckboxes
);
this
.
setDisabled
(
this
.
$
(
'.check-discussion-category'
),
enable
C
heckboxes
);
this
.
setDisabled
(
this
.
$
(
'.check-discussion-subcategory-inline'
),
enable
_c
heckboxes
);
this
.
setDisabled
(
this
.
$
(
'.check-discussion-subcategory-inline'
),
enable
C
heckboxes
);
this
.
setDisabled
(
this
.
$
(
'.cohort-inline-discussions-form .action-save'
),
enable
_save_b
utton
);
this
.
setDisabled
(
this
.
$
(
'.cohort-inline-discussions-form .action-save'
),
enable
SaveB
utton
);
},
},
/**
/**
* Enables the save button for inline discussions.
* Enables the save button for inline discussions.
*/
*/
setSaveButton
:
function
(
event
)
{
setSaveButton
:
function
()
{
this
.
setDisabled
(
this
.
$
(
'.cohort-inline-discussions-form .action-save'
),
false
);
this
.
setDisabled
(
this
.
$
(
'.cohort-inline-discussions-form .action-save'
),
false
);
},
},
/**
/**
* Sends the
cohorted_inline_d
iscussions to the server and renders the view.
* Sends the
dividedInlineD
iscussions to the server and renders the view.
*/
*/
saveInlineDiscussionsForm
:
function
(
event
)
{
saveInlineDiscussionsForm
:
function
(
event
)
{
event
.
preventDefault
();
var
self
=
this
,
var
self
=
this
,
cohortedInlineDiscussions
=
self
.
getCohort
edDiscussions
(
dividedInlineDiscussions
=
self
.
getDivid
edDiscussions
(
'.check-discussion-subcategory-inline:checked'
'.check-discussion-subcategory-inline:checked'
),
),
fieldData
=
{
fieldData
=
{
cohorted_inline_discussions
:
cohortedInlineDiscussions
,
divided_inline_discussions
:
dividedInlineDiscussions
,
always_cohort_inline_discussions
:
self
.
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)
always_divide_inline_discussions
:
self
.
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)
};
};
event
.
preventDefault
();
self
.
saveForm
(
self
.
$
(
'.inline-discussion-topics'
),
fieldData
)
self
.
saveForm
(
self
.
$
(
'.inline-discussion-topics'
),
fieldData
)
.
done
(
function
()
{
.
done
(
function
()
{
self
.
model
.
fetch
()
self
.
model
.
fetch
()
.
done
(
function
()
{
.
done
(
function
()
{
self
.
render
();
self
.
render
();
self
.
showMessage
(
gettext
(
'Your changes have been saved.'
),
self
.
$
(
'.inline-discussion-topics'
));
self
.
showMessage
(
gettext
(
'Your changes have been saved.'
),
self
.
$
(
'.inline-discussion-topics'
));
}).
fail
(
function
()
{
}).
fail
(
function
()
{
var
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
var
errorMessage
=
gettext
(
"We've encountered an error. Refresh your browser and then try again."
);
// eslint-disable-line max-len
self
.
showMessage
(
errorMessage
,
self
.
$
(
'.inline-discussion-topics'
),
'error'
);
self
.
showMessage
(
errorMessage
,
self
.
$
(
'.inline-discussion-topics'
),
'error'
);
});
});
});
});
...
...
lms/static/js/groups/models/course_cohort_settings.js
View file @
c23c0b99
...
@@ -4,10 +4,7 @@
...
@@ -4,10 +4,7 @@
var
CourseCohortSettingsModel
=
Backbone
.
Model
.
extend
({
var
CourseCohortSettingsModel
=
Backbone
.
Model
.
extend
({
idAttribute
:
'id'
,
idAttribute
:
'id'
,
defaults
:
{
defaults
:
{
is_cohorted
:
false
,
is_cohorted
:
false
cohorted_inline_discussions
:
[],
cohorted_course_wide_discussions
:
[],
always_cohort_inline_discussions
:
false
}
}
});
});
return
CourseCohortSettingsModel
;
return
CourseCohortSettingsModel
;
...
...
lms/static/js/groups/views/cohorts.js
View file @
c23c0b99
...
@@ -4,13 +4,11 @@
...
@@ -4,13 +4,11 @@
'js/groups/models/verified_track_settings'
,
'js/groups/models/verified_track_settings'
,
'js/groups/views/cohort_editor'
,
'js/groups/views/cohort_form'
,
'js/groups/views/cohort_editor'
,
'js/groups/views/cohort_form'
,
'js/groups/views/course_cohort_settings_notification'
,
'js/groups/views/course_cohort_settings_notification'
,
'js/groups/views/cohort_discussions_inline'
,
'js/groups/views/cohort_discussions_course_wide'
,
'js/groups/views/verified_track_settings_notification'
,
'js/groups/views/verified_track_settings_notification'
,
'edx-ui-toolkit/js/utils/html-utils'
,
'edx-ui-toolkit/js/utils/html-utils'
,
'js/views/file_uploader'
,
'js/models/notification'
,
'js/views/notification'
,
'string_utils'
],
'js/views/file_uploader'
,
'js/models/notification'
,
'js/views/notification'
,
'string_utils'
],
function
(
$
,
_
,
Backbone
,
gettext
,
CohortModel
,
VerifiedTrackSettingsModel
,
CohortEditorView
,
CohortFormView
,
function
(
$
,
_
,
Backbone
,
gettext
,
CohortModel
,
VerifiedTrackSettingsModel
,
CohortEditorView
,
CohortFormView
,
CourseCohortSettingsNotificationView
,
InlineDiscussionsView
,
CourseWideDiscussionsView
,
CourseCohortSettingsNotificationView
,
VerifiedTrackSettingsNotificationView
,
HtmlUtils
)
{
VerifiedTrackSettingsNotificationView
,
HtmlUtils
)
{
var
hiddenClass
=
'is-hidden'
,
var
hiddenClass
=
'is-hidden'
,
disabledClass
=
'is-disabled'
,
disabledClass
=
'is-disabled'
,
enableCohortsSelector
=
'.cohorts-state'
;
enableCohortsSelector
=
'.cohorts-state'
;
...
@@ -25,7 +23,6 @@
...
@@ -25,7 +23,6 @@
'click .cohort-management-add-form .action-cancel'
:
'cancelAddCohortForm'
,
'click .cohort-management-add-form .action-cancel'
:
'cancelAddCohortForm'
,
'click .link-cross-reference'
:
'showSection'
,
'click .link-cross-reference'
:
'showSection'
,
'click .toggle-cohort-management-secondary'
:
'showCsvUpload'
,
'click .toggle-cohort-management-secondary'
:
'showCsvUpload'
,
'click .toggle-cohort-management-discussions'
:
'showDiscussionTopics'
},
},
initialize
:
function
(
options
)
{
initialize
:
function
(
options
)
{
...
@@ -306,27 +303,6 @@
...
@@ -306,27 +303,6 @@
}).
render
();
}).
render
();
}
}
},
},
showDiscussionTopics
:
function
(
event
)
{
event
.
preventDefault
();
$
(
event
.
currentTarget
).
addClass
(
hiddenClass
);
var
cohortDiscussionsElement
=
this
.
$
(
'.cohort-discussions-nav'
).
removeClass
(
hiddenClass
);
if
(
!
this
.
CourseWideDiscussionsView
)
{
this
.
CourseWideDiscussionsView
=
new
CourseWideDiscussionsView
({
el
:
cohortDiscussionsElement
,
model
:
this
.
context
.
discussionTopicsSettingsModel
,
cohortSettings
:
this
.
cohortSettings
}).
render
();
}
if
(
!
this
.
InlineDiscussionsView
)
{
this
.
InlineDiscussionsView
=
new
InlineDiscussionsView
({
el
:
cohortDiscussionsElement
,
model
:
this
.
context
.
discussionTopicsSettingsModel
,
cohortSettings
:
this
.
cohortSettings
}).
render
();
}
},
getSectionCss
:
function
(
section
)
{
getSectionCss
:
function
(
section
)
{
return
".instructor-nav .nav-item [data-section='"
+
section
+
"']"
;
return
".instructor-nav .nav-item [data-section='"
+
section
+
"']"
;
...
...
lms/static/js/groups/views/cohorts_dashboard_factory.js
View file @
c23c0b99
(
function
(
define
,
undefined
)
{
(
function
(
define
,
undefined
)
{
'use strict'
;
'use strict'
;
define
([
'jquery'
,
'js/groups/views/cohorts'
,
'js/groups/collections/cohort'
,
'js/groups/models/course_cohort_settings'
,
define
([
'jquery'
,
'js/groups/views/cohorts'
,
'js/groups/collections/cohort'
,
'js/groups/models/course_cohort_settings'
,
'js/groups/models/cohort_discussions'
,
'js/groups/models/content_group'
],
'js/groups/models/content_group'
],
function
(
$
,
CohortsView
,
CohortCollection
,
CourseCohortSettingsModel
,
DiscussionTopicsSettingsModel
,
ContentGroupModel
)
{
function
(
$
,
CohortsView
,
CohortCollection
,
CourseCohortSettingsModel
,
ContentGroupModel
)
{
return
function
(
contentGroups
,
studioGroupConfigurationsUrl
)
{
return
function
(
contentGroups
,
studioGroupConfigurationsUrl
)
{
var
contentGroupModels
=
$
.
map
(
contentGroups
,
function
(
group
)
{
var
contentGroupModels
=
$
.
map
(
contentGroups
,
function
(
group
)
{
return
new
ContentGroupModel
({
return
new
ContentGroupModel
({
...
@@ -14,35 +14,29 @@
...
@@ -14,35 +14,29 @@
var
cohorts
=
new
CohortCollection
(),
var
cohorts
=
new
CohortCollection
(),
courseCohortSettings
=
new
CourseCohortSettingsModel
(),
courseCohortSettings
=
new
CourseCohortSettingsModel
(),
discussionTopicsSettings
=
new
DiscussionTopicsSettingsModel
(
);
$cohortManagementElement
=
$
(
'.cohort-management'
);
var
cohortManagementElement
=
$
(
'.cohort-management'
);
cohorts
.
url
=
$cohortManagementElement
.
data
(
'cohorts_url'
);
courseCohortSettings
.
url
=
$cohortManagementElement
.
data
(
'course_cohort_settings_url'
);
cohorts
.
url
=
cohortManagementElement
.
data
(
'cohorts_url'
);
courseCohortSettings
.
url
=
cohortManagementElement
.
data
(
'course_cohort_settings_url'
);
discussionTopicsSettings
.
url
=
cohortManagementElement
.
data
(
'discussion-topics-url'
);
var
cohortsView
=
new
CohortsView
({
var
cohortsView
=
new
CohortsView
({
el
:
cohortManagementElement
,
el
:
$
cohortManagementElement
,
model
:
cohorts
,
model
:
cohorts
,
contentGroups
:
contentGroupModels
,
contentGroups
:
contentGroupModels
,
cohortSettings
:
courseCohortSettings
,
cohortSettings
:
courseCohortSettings
,
context
:
{
context
:
{
discussionTopicsSettingsModel
:
discussionTopicsSettings
,
uploadCohortsCsvUrl
:
$cohortManagementElement
.
data
(
'upload_cohorts_csv_url'
),
uploadCohortsCsvUrl
:
cohortManagementElement
.
data
(
'upload_cohorts_csv_url'
),
verifiedTrackCohortingUrl
:
$cohortManagementElement
.
data
(
'verified_track_cohorting_url'
),
verifiedTrackCohortingUrl
:
cohortManagementElement
.
data
(
'verified_track_cohorting_url'
),
studioGroupConfigurationsUrl
:
studioGroupConfigurationsUrl
,
studioGroupConfigurationsUrl
:
studioGroupConfigurationsUrl
,
isCcxEnabled
:
cohortManagementElement
.
data
(
'is_ccx_enabled'
)
isCcxEnabled
:
$
cohortManagementElement
.
data
(
'is_ccx_enabled'
)
}
}
});
});
cohorts
.
fetch
().
done
(
function
()
{
cohorts
.
fetch
().
done
(
function
()
{
courseCohortSettings
.
fetch
().
done
(
function
()
{
courseCohortSettings
.
fetch
().
done
(
function
()
{
discussionTopicsSettings
.
fetch
().
done
(
function
()
{
cohortsView
.
render
();
cohortsView
.
render
();
});
});
});
});
});
};
};
});
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
}).
call
(
this
,
define
||
RequireJS
.
define
);
...
...
lms/static/js/instructor_dashboard/discussions_management.js
0 → 100644
View file @
c23c0b99
(
function
()
{
'use strict'
;
function
DiscussionsManagement
(
$section
)
{
this
.
$section
=
$section
;
this
.
$section
.
data
(
'wrapper'
,
this
);
}
DiscussionsManagement
.
prototype
.
onClickTitle
=
function
()
{};
window
.
InstructorDashboard
.
sections
.
DiscussionsManagement
=
DiscussionsManagement
;
}).
call
(
this
);
lms/static/js/instructor_dashboard/instructor_dashboard.js
View file @
c23c0b99
...
@@ -188,6 +188,9 @@ such that the value can be defined later than this assignment (file load order).
...
@@ -188,6 +188,9 @@ such that the value can be defined later than this assignment (file load order).
constructor
:
window
.
InstructorDashboard
.
sections
.
CohortManagement
,
constructor
:
window
.
InstructorDashboard
.
sections
.
CohortManagement
,
$element
:
idashContent
.
find
(
'.'
+
CSS_IDASH_SECTION
+
'#cohort_management'
)
$element
:
idashContent
.
find
(
'.'
+
CSS_IDASH_SECTION
+
'#cohort_management'
)
},
{
},
{
constructor
:
window
.
InstructorDashboard
.
sections
.
DiscussionsManagement
,
$element
:
idashContent
.
find
(
'.'
+
CSS_IDASH_SECTION
+
'#discussions_management'
)
},
{
constructor
:
window
.
InstructorDashboard
.
sections
.
Certificates
,
constructor
:
window
.
InstructorDashboard
.
sections
.
Certificates
,
$element
:
idashContent
.
find
(
'.'
+
CSS_IDASH_SECTION
+
'#certificates'
)
$element
:
idashContent
.
find
(
'.'
+
CSS_IDASH_SECTION
+
'#certificates'
)
},
{
},
{
...
...
lms/static/js/spec/groups/views/cohorts_spec.js
View file @
c23c0b99
...
@@ -2,13 +2,10 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -2,13 +2,10 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
'common/js/spec_helpers/template_helpers'
,
'common/js/spec_helpers/template_helpers'
,
'js/groups/views/cohorts'
,
'js/groups/collections/cohort'
,
'js/groups/models/content_group'
,
'js/groups/views/cohorts'
,
'js/groups/collections/cohort'
,
'js/groups/models/content_group'
,
'js/groups/models/course_cohort_settings'
,
'js/utils/animation'
,
'js/vendor/jquery.qubit'
,
'js/groups/models/course_cohort_settings'
,
'js/utils/animation'
,
'js/vendor/jquery.qubit'
,
'js/groups/views/course_cohort_settings_notification'
,
'js/groups/models/cohort_discussions'
,
'js/groups/views/course_cohort_settings_notification'
'js/groups/views/cohort_discussions'
,
'js/groups/views/cohort_discussions_course_wide'
,
],
'js/groups/views/cohort_discussions_inline'
],
function
(
Backbone
,
$
,
AjaxHelpers
,
TemplateHelpers
,
CohortsView
,
CohortCollection
,
ContentGroupModel
,
function
(
Backbone
,
$
,
AjaxHelpers
,
TemplateHelpers
,
CohortsView
,
CohortCollection
,
ContentGroupModel
,
CourseCohortSettingsModel
,
AnimationUtil
,
Qubit
,
CourseCohortSettingsNotificationView
,
DiscussionTopicsSettingsModel
,
CourseCohortSettingsModel
,
AnimationUtil
,
Qubit
,
CourseCohortSettingsNotificationView
)
{
CohortDiscussionsView
,
CohortCourseWideDiscussionsView
,
CohortInlineDiscussionsView
)
{
'use strict'
;
'use strict'
;
describe
(
'Cohorts View'
,
function
()
{
describe
(
'Cohorts View'
,
function
()
{
...
@@ -20,16 +17,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -20,16 +17,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
getAddModal
,
selectContentGroup
,
clearContentGroup
,
getAddModal
,
selectContentGroup
,
clearContentGroup
,
saveFormAndExpectErrors
,
createMockCohortSettings
,
MOCK_COHORTED_USER_PARTITION_ID
,
saveFormAndExpectErrors
,
createMockCohortSettings
,
MOCK_COHORTED_USER_PARTITION_ID
,
MOCK_UPLOAD_COHORTS_CSV_URL
,
MOCK_STUDIO_ADVANCED_SETTINGS_URL
,
MOCK_STUDIO_GROUP_CONFIGURATIONS_URL
,
MOCK_UPLOAD_COHORTS_CSV_URL
,
MOCK_STUDIO_ADVANCED_SETTINGS_URL
,
MOCK_STUDIO_GROUP_CONFIGURATIONS_URL
,
MOCK_VERIFIED_TRACK_COHORTING_URL
,
MOCK_MANUAL_ASSIGNMENT
,
MOCK_RANDOM_ASSIGNMENT
,
MOCK_VERIFIED_TRACK_COHORTING_URL
,
MOCK_MANUAL_ASSIGNMENT
,
MOCK_RANDOM_ASSIGNMENT
;
createMockCohortDiscussionsJson
,
createMockCohortDiscussions
,
showAndAssertDiscussionTopics
;
// Selectors
var
discussionsToggle
=
'.toggle-cohort-management-discussions'
,
inlineDiscussionsFormCss
=
'.cohort-inline-discussions-form'
,
courseWideDiscussionsFormCss
=
'.cohort-course-wide-discussions-form'
,
courseWideDiscussionsSaveButtonCss
=
'.cohort-course-wide-discussions-form .action-save'
,
inlineDiscussionsSaveButtonCss
=
'.cohort-inline-discussions-form .action-save'
,
inlineDiscussionsForm
,
courseWideDiscussionsForm
;
MOCK_MANUAL_ASSIGNMENT
=
'manual'
;
MOCK_MANUAL_ASSIGNMENT
=
'manual'
;
MOCK_RANDOM_ASSIGNMENT
=
'random'
;
MOCK_RANDOM_ASSIGNMENT
=
'random'
;
...
@@ -70,66 +58,16 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -70,66 +58,16 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
];
];
};
};
createMockCohortSettingsJson
=
function
(
isCohorted
,
cohortedInlineDiscussions
,
cohortedCourseWideDiscussions
,
alwaysCohortInlineDiscussions
)
{
createMockCohortSettingsJson
=
function
(
isCohorted
)
{
return
{
return
{
id
:
0
,
id
:
0
,
is_cohorted
:
isCohorted
||
false
,
is_cohorted
:
isCohorted
||
false
cohorted_inline_discussions
:
cohortedInlineDiscussions
||
[],
cohorted_course_wide_discussions
:
cohortedCourseWideDiscussions
||
[],
always_cohort_inline_discussions
:
alwaysCohortInlineDiscussions
||
false
};
};
};
};
createMockCohortSettings
=
function
(
isCohorted
,
cohortedInlineDiscussions
,
cohortedCourseWideDiscussions
,
alwaysCohortInlineDiscussions
)
{
createMockCohortSettings
=
function
(
isCohorted
)
{
return
new
CourseCohortSettingsModel
(
return
new
CourseCohortSettingsModel
(
createMockCohortSettingsJson
(
isCohorted
,
cohortedInlineDiscussions
,
cohortedCourseWideDiscussions
,
alwaysCohortInlineDiscussions
)
createMockCohortSettingsJson
(
isCohorted
)
);
};
createMockCohortDiscussionsJson
=
function
(
allCohorted
)
{
return
{
course_wide_discussions
:
{
children
:
[[
'Topic_C_1'
,
'entry'
],
[
'Topic_C_2'
,
'entry'
]],
entries
:
{
Topic_C_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Topic_C_1'
},
Topic_C_2
:
{
sort_key
:
null
,
is_divided
:
false
,
id
:
'Topic_C_2'
}
}
},
inline_discussions
:
{
subcategories
:
{
Topic_I_1
:
{
subcategories
:
{},
children
:
[[
'Inline_Discussion_1'
,
'entry'
],
[
'Inline_Discussion_2'
,
'entry'
]],
entries
:
{
Inline_Discussion_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Inline_Discussion_1'
},
Inline_Discussion_2
:
{
sort_key
:
null
,
is_divided
:
allCohorted
||
false
,
id
:
'Inline_Discussion_2'
}
}
}
},
children
:
[[
'Topic_I_1'
,
'subcategory'
]]
}
};
};
createMockCohortDiscussions
=
function
(
allCohorted
)
{
return
new
DiscussionTopicsSettingsModel
(
createMockCohortDiscussionsJson
(
allCohorted
)
);
);
};
};
...
@@ -146,7 +84,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -146,7 +84,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
};
};
createCohortsView
=
function
(
test
,
options
)
{
createCohortsView
=
function
(
test
,
options
)
{
var
cohortsJson
,
cohorts
,
contentGroups
,
cohortSettings
,
cohortDiscussions
;
var
cohortsJson
,
cohorts
,
contentGroups
,
cohortSettings
;
options
=
options
||
{};
options
=
options
||
{};
cohortsJson
=
options
.
cohorts
?
{
cohorts
:
options
.
cohorts
}
:
createMockCohorts
();
cohortsJson
=
options
.
cohorts
?
{
cohorts
:
options
.
cohorts
}
:
createMockCohorts
();
cohorts
=
new
CohortCollection
(
cohortsJson
,
{
parse
:
true
});
cohorts
=
new
CohortCollection
(
cohortsJson
,
{
parse
:
true
});
...
@@ -155,16 +93,12 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -155,16 +93,12 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
cohortSettings
.
url
=
'/mock_service/cohorts/settings'
;
cohortSettings
.
url
=
'/mock_service/cohorts/settings'
;
cohorts
.
url
=
'/mock_service/cohorts'
;
cohorts
.
url
=
'/mock_service/cohorts'
;
cohortDiscussions
=
options
.
cohortDiscussions
||
createMockCohortDiscussions
();
cohortDiscussions
.
url
=
'/mock_service/cohorts/discussion/topics'
;
requests
=
AjaxHelpers
.
requests
(
test
);
requests
=
AjaxHelpers
.
requests
(
test
);
cohortsView
=
new
CohortsView
({
cohortsView
=
new
CohortsView
({
model
:
cohorts
,
model
:
cohorts
,
contentGroups
:
contentGroups
,
contentGroups
:
contentGroups
,
cohortSettings
:
cohortSettings
,
cohortSettings
:
cohortSettings
,
context
:
{
context
:
{
discussionTopicsSettingsModel
:
cohortDiscussions
,
uploadCohortsCsvUrl
:
MOCK_UPLOAD_COHORTS_CSV_URL
,
uploadCohortsCsvUrl
:
MOCK_UPLOAD_COHORTS_CSV_URL
,
studioAdvancedSettingsUrl
:
MOCK_STUDIO_ADVANCED_SETTINGS_URL
,
studioAdvancedSettingsUrl
:
MOCK_STUDIO_ADVANCED_SETTINGS_URL
,
studioGroupConfigurationsUrl
:
MOCK_STUDIO_GROUP_CONFIGURATIONS_URL
,
studioGroupConfigurationsUrl
:
MOCK_STUDIO_GROUP_CONFIGURATIONS_URL
,
...
@@ -314,40 +248,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -314,40 +248,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
verifyDetailedMessage
(
expectedTitle
,
'error'
,
errors
);
verifyDetailedMessage
(
expectedTitle
,
'error'
,
errors
);
};
};
showAndAssertDiscussionTopics
=
function
(
that
)
{
createCohortsView
(
that
);
// Should see the control to toggle cohort discussions.
expect
(
cohortsView
.
$
(
discussionsToggle
)).
not
.
toHaveClass
(
'is-hidden'
);
// But discussions form should not be visible until toggle is clicked.
expect
(
cohortsView
.
$
(
inlineDiscussionsFormCss
).
length
).
toBe
(
0
);
expect
(
cohortsView
.
$
(
courseWideDiscussionsFormCss
).
length
).
toBe
(
0
);
expect
(
cohortsView
.
$
(
discussionsToggle
).
text
()).
toContain
(
'Specify whether discussion topics are divided by cohort'
);
cohortsView
.
$
(
discussionsToggle
).
click
();
// After toggle is clicked, it should be hidden.
expect
(
cohortsView
.
$
(
discussionsToggle
)).
toHaveClass
(
'is-hidden'
);
// Should see the course wide discussions form and its content
courseWideDiscussionsForm
=
cohortsView
.
$
(
courseWideDiscussionsFormCss
);
expect
(
courseWideDiscussionsForm
.
length
).
toBe
(
1
);
expect
(
courseWideDiscussionsForm
.
text
()).
toContain
(
'Course-Wide Discussion Topics'
);
expect
(
courseWideDiscussionsForm
.
text
()).
toContain
(
'Select the course-wide discussion topics that you want to divide by cohort.'
);
// Should see the inline discussions form and its content
inlineDiscussionsForm
=
cohortsView
.
$
(
inlineDiscussionsFormCss
);
expect
(
inlineDiscussionsForm
.
length
).
toBe
(
1
);
expect
(
inlineDiscussionsForm
.
text
()).
toContain
(
'Content-Specific Discussion Topics'
);
expect
(
inlineDiscussionsForm
.
text
()).
toContain
(
'Specify whether content-specific discussion topics are divided by cohort.'
);
};
unknownUserMessage
=
function
(
name
)
{
unknownUserMessage
=
function
(
name
)
{
return
'Unknown user: '
+
name
;
return
'Unknown user: '
+
name
;
};
};
...
@@ -364,10 +264,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -364,10 +264,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-group-header'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-group-header'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/notification'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/notification'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-state'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-state'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-category'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-inline'
);
TemplateHelpers
.
installTemplate
(
'templates/file-upload'
);
TemplateHelpers
.
installTemplate
(
'templates/file-upload'
);
});
});
...
@@ -381,8 +277,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -381,8 +277,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
// If no cohorts have been created, can't upload a CSV file.
// If no cohorts have been created, can't upload a CSV file.
expect
(
cohortsView
.
$
(
'.wrapper-cohort-supplemental'
)).
toHaveClass
(
'is-hidden'
);
expect
(
cohortsView
.
$
(
'.wrapper-cohort-supplemental'
)).
toHaveClass
(
'is-hidden'
);
// if no cohorts have been created, can't show the link to discussion topics.
expect
(
cohortsView
.
$
(
'.cohort-discussions-nav'
)).
toHaveClass
(
'is-hidden'
);
});
});
it
(
'syncs data when membership tab is clicked'
,
function
()
{
it
(
'syncs data when membership tab is clicked'
,
function
()
{
...
@@ -422,10 +316,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -422,10 +316,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
.
toBe
(
"Your file 'upload_file.txt' has been uploaded. Allow a few minutes for processing."
);
.
toBe
(
"Your file 'upload_file.txt' has been uploaded. Allow a few minutes for processing."
);
});
});
it
(
'can show discussion topics if cohort exists'
,
function
()
{
showAndAssertDiscussionTopics
(
this
);
});
describe
(
'Cohort Selector'
,
function
()
{
describe
(
'Cohort Selector'
,
function
()
{
it
(
'has no initial selection'
,
function
()
{
it
(
'has no initial selection'
,
function
()
{
createCohortsView
(
this
);
createCohortsView
(
this
);
...
@@ -1172,375 +1062,5 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
...
@@ -1172,375 +1062,5 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
});
});
});
});
});
});
describe
(
'Discussion Topics'
,
function
()
{
var
createCourseWideView
,
createInlineView
,
inlineView
,
courseWideView
,
assertCohortedTopics
;
createCourseWideView
=
function
(
that
)
{
createCohortsView
(
that
);
courseWideView
=
new
CohortCourseWideDiscussionsView
({
el
:
cohortsView
.
$
(
'.cohort-discussions-nav'
).
removeClass
(
'is-hidden'
),
model
:
cohortsView
.
context
.
discussionTopicsSettingsModel
,
cohortSettings
:
cohortsView
.
cohortSettings
});
courseWideView
.
render
();
};
createInlineView
=
function
(
that
,
discussionTopicsSettingsModel
)
{
createCohortsView
(
that
);
inlineView
=
new
CohortInlineDiscussionsView
({
el
:
cohortsView
.
$
(
'.cohort-discussions-nav'
).
removeClass
(
'is-hidden'
),
model
:
discussionTopicsSettingsModel
||
cohortsView
.
context
.
discussionTopicsSettingsModel
,
cohortSettings
:
cohortsView
.
cohortSettings
});
inlineView
.
render
();
};
assertCohortedTopics
=
function
(
view
,
type
)
{
expect
(
view
.
$
(
'.check-discussion-subcategory-'
+
type
).
length
).
toBe
(
2
);
expect
(
view
.
$
(
'.check-discussion-subcategory-'
+
type
+
':checked'
).
length
).
toBe
(
1
);
};
it
(
'renders the view properly'
,
function
()
{
showAndAssertDiscussionTopics
(
this
);
});
describe
(
'Course Wide'
,
function
()
{
it
(
'shows the "Save" button as disabled initially'
,
function
()
{
createCourseWideView
(
this
);
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
});
it
(
'has one cohorted and one non-cohorted topic'
,
function
()
{
createCourseWideView
(
this
);
assertCohortedTopics
(
courseWideView
,
'course-wide'
);
expect
(
courseWideView
.
$
(
'.cohorted-text'
).
length
).
toBe
(
2
);
expect
(
courseWideView
.
$
(
'.cohorted-text.hidden'
).
length
).
toBe
(
1
);
});
it
(
'enables the "Save" button after changing checkbox'
,
function
()
{
createCourseWideView
(
this
);
// save button is disabled.
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
$
(
courseWideView
.
$
(
'.check-discussion-subcategory-course-wide'
)[
0
]).
prop
(
'checked'
,
false
).
change
();
// save button is enabled.
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
});
it
(
'saves the topic successfully'
,
function
()
{
createCourseWideView
(
this
);
$
(
courseWideView
.
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
// Save the updated settings
courseWideView
.
$
(
'.action-save'
).
click
();
// fake requests for cohort settings with PATCH method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/cohorts/settings'
,
{
cohorted_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
cohorted_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/cohorts/discussion/topics'
);
AjaxHelpers
.
respondWithJson
(
requests
,
createMockCohortDiscussions
()
);
// verify the success message.
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
verifyMessage
(
'Your changes have been saved.'
,
'confirmation'
);
});
it
(
'shows an appropriate message when subsequent "GET" returns HTTP500'
,
function
()
{
createCourseWideView
(
this
);
$
(
courseWideView
.
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
courseWideView
.
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
// Save the updated settings
courseWideView
.
$
(
'.action-save'
).
click
();
// fake requests for cohort settings with PATCH method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/cohorts/settings'
,
{
cohorted_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
cohorted_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/cohorts/discussion/topics'
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
var
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
courseWideView
.
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate error message for HTTP500'
,
function
()
{
createCourseWideView
(
this
);
$
(
courseWideView
.
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
courseWideView
.
$
(
'.action-save'
).
click
();
AjaxHelpers
.
respondWithError
(
requests
,
500
);
var
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
courseWideView
.
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
});
describe
(
'Inline'
,
function
()
{
var
enableSaveButton
,
mockGetRequest
,
verifySuccess
,
mockPatchRequest
;
enableSaveButton
=
function
()
{
// enable the inline discussion topics.
inlineView
.
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
$
(
inlineView
.
$
(
'.check-discussion-subcategory-inline'
)[
0
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
inlineView
.
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
};
verifySuccess
=
function
()
{
// verify the success message.
expect
(
inlineView
.
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
verifyMessage
(
'Your changes have been saved.'
,
'confirmation'
);
};
mockPatchRequest
=
function
(
cohortedInlineDiscussions
)
{
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/cohorts/settings'
,
{
cohorted_inline_discussions
:
cohortedInlineDiscussions
,
always_cohort_inline_discussions
:
false
}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
cohorted_inline_discussions
:
cohortedInlineDiscussions
,
always_cohort_inline_discussions
:
false
}
);
};
mockGetRequest
=
function
(
allCohorted
)
{
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/cohorts/discussion/topics'
);
AjaxHelpers
.
respondWithJson
(
requests
,
createMockCohortDiscussions
(
allCohorted
)
);
};
it
(
'shows the "Save" button as disabled initially'
,
function
()
{
createInlineView
(
this
);
expect
(
inlineView
.
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
});
it
(
'shows always cohort radio button as selected'
,
function
()
{
createInlineView
(
this
);
inlineView
.
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
// verify always cohort inline discussions is being selected.
expect
(
inlineView
.
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)).
toBeTruthy
();
// verify that inline topics are disabled
expect
(
inlineView
.
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'disabled'
)).
toBeTruthy
();
expect
(
inlineView
.
$
(
'.check-discussion-category'
).
prop
(
'disabled'
)).
toBeTruthy
();
// verify that cohort some topics are not being selected.
expect
(
inlineView
.
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
)).
toBeFalsy
();
});
it
(
'shows cohort some topics radio button as selected'
,
function
()
{
createInlineView
(
this
);
inlineView
.
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
// verify some cohort inline discussions radio is being selected.
expect
(
inlineView
.
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
)).
toBeTruthy
();
// verify always cohort radio is not selected.
expect
(
inlineView
.
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)).
toBeFalsy
();
// verify that inline topics are enabled
expect
(
inlineView
.
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'disabled'
)).
toBeFalsy
();
expect
(
inlineView
.
$
(
'.check-discussion-category'
).
prop
(
'disabled'
)).
toBeFalsy
();
});
it
(
'has cohorted and non-cohorted topics'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
assertCohortedTopics
(
inlineView
,
'inline'
);
});
it
(
'enables "Save" button after changing from always inline option'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
});
it
(
'saves the topic'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
// Save the updated settings
inlineView
.
$
(
'.action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
mockGetRequest
();
verifySuccess
();
});
it
(
'selects the parent category when all children are selected'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
inlineView
.
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
inlineView
.
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'checked'
,
'checked'
).
change
();
// parent should be checked as we checked all children
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
});
it
(
'selects/deselects all children when a parent category is selected/deselected'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
inlineView
.
$
(
'.check-discussion-category'
).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
expect
(
inlineView
.
$
(
'.check-discussion-subcategory-inline:checked'
).
length
).
toBe
(
2
);
// un-check the parent, all children should be unchecd.
inlineView
.
$
(
'.check-discussion-category'
).
prop
(
'checked'
,
false
).
change
();
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
inlineView
.
$
(
'.check-discussion-subcategory-inline:checked'
).
length
).
toBe
(
0
);
});
it
(
'saves correctly when a subset of topics are selected within a category'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
inlineView
.
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
// Save the updated settings
inlineView
.
$
(
'.action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
mockGetRequest
();
verifySuccess
();
// parent category should be indeterminate.
expect
(
inlineView
.
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
});
it
(
'saves correctly when all child topics are selected within a category'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
inlineView
.
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
inlineView
.
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'checked'
,
'checked'
).
change
();
// Save the updated settings
inlineView
.
$
(
'.action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
,
'Inline_Discussion_2'
]);
mockGetRequest
(
true
);
verifySuccess
();
// parent category should be checked.
expect
(
inlineView
.
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
});
it
(
'shows an appropriate message when no inline topics exist'
,
function
()
{
var
topicsJson
,
discussionTopicsSettingsModel
;
topicsJson
=
{
course_wide_discussions
:
{
children
:
[[
'Topic_C_1'
,
'entry'
]],
entries
:
{
Topic_C_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Topic_C_1'
}
}
},
inline_discussions
:
{
subcategories
:
{},
children
:
[]
}
};
discussionTopicsSettingsModel
=
new
DiscussionTopicsSettingsModel
(
topicsJson
);
createInlineView
(
this
,
discussionTopicsSettingsModel
);
var
expectedTitle
=
'No content-specific discussion topics exist.'
;
expect
(
inlineView
.
$
(
'.no-topics'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate message when subsequent "GET" returns HTTP500'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
// Save the updated settings
inlineView
.
$
(
'.action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/cohorts/discussion/topics'
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
var
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
inlineView
.
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate error message for HTTP500'
,
function
()
{
createInlineView
(
this
);
enableSaveButton
();
$
(
inlineView
.
$
(
'.check-discussion-subcategory-inline'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
inlineView
.
$
(
'.action-save'
).
click
();
AjaxHelpers
.
respondWithError
(
requests
,
500
);
var
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
inlineView
.
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
});
});
});
});
});
});
lms/static/js/spec/groups/views/discussions_spec.js
0 → 100644
View file @
c23c0b99
define
([
'backbone'
,
'jquery'
,
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
,
'common/js/spec_helpers/template_helpers'
,
'js/discussions_management/views/discussions'
,
'js/discussions_management/models/course_discussions_detail'
,
'js/discussions_management/models/course_discussions_settings'
],
function
(
Backbone
,
$
,
AjaxHelpers
,
TemplateHelpers
,
DiscussionsView
,
CourseDiscussionTopicDetailsModel
,
CourseDiscussionsSettingsModel
)
{
'use strict'
;
describe
(
'Discussions View'
,
function
()
{
var
createMockDiscussionsSettingsJson
,
createDiscussionsView
,
discussionsView
,
requests
,
verifyMessage
,
createMockDiscussionsSettings
,
createMockDiscussionsJson
,
createMockDiscussions
,
showAndAssertDiscussionTopics
;
// Selectors
var
inlineDiscussionsFormCss
=
'.cohort-inline-discussions-form'
,
courseWideDiscussionsFormCss
=
'.cohort-course-wide-discussions-form'
,
courseWideDiscussionsSaveButtonCss
=
'.cohort-course-wide-discussions-form .action-save'
,
inlineDiscussionsSaveButtonCss
=
'.cohort-inline-discussions-form .action-save'
;
createMockDiscussionsSettingsJson
=
function
(
dividedInlineDiscussions
,
dividedCourseWideDiscussions
,
alwaysDivideInlineDiscussions
)
{
return
{
id
:
0
,
divided_inline_discussions
:
dividedInlineDiscussions
||
[],
divided_course_wide_discussions
:
dividedCourseWideDiscussions
||
[],
always_divide_inline_discussions
:
alwaysDivideInlineDiscussions
||
false
};
};
createMockDiscussionsSettings
=
function
(
dividedInlineDiscussions
,
dividedCourseWideDiscussions
,
alwaysDivideInlineDiscussions
)
{
return
new
CourseDiscussionsSettingsModel
(
createMockDiscussionsSettingsJson
(
dividedInlineDiscussions
,
dividedCourseWideDiscussions
,
alwaysDivideInlineDiscussions
)
);
};
createMockDiscussionsJson
=
function
(
allDivided
)
{
return
{
course_wide_discussions
:
{
children
:
[[
'Topic_C_1'
,
'entry'
],
[
'Topic_C_2'
,
'entry'
]],
entries
:
{
Topic_C_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Topic_C_1'
},
Topic_C_2
:
{
sort_key
:
null
,
is_divided
:
false
,
id
:
'Topic_C_2'
}
}
},
inline_discussions
:
{
subcategories
:
{
Topic_I_1
:
{
subcategories
:
{},
children
:
[[
'Inline_Discussion_1'
,
'entry'
],
[
'Inline_Discussion_2'
,
'entry'
]],
entries
:
{
Inline_Discussion_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Inline_Discussion_1'
},
Inline_Discussion_2
:
{
sort_key
:
null
,
is_divided
:
allDivided
||
false
,
id
:
'Inline_Discussion_2'
}
}
}
},
children
:
[[
'Topic_I_1'
,
'subcategory'
]]
}
};
};
createMockDiscussions
=
function
(
allDivided
)
{
return
new
CourseDiscussionTopicDetailsModel
(
createMockDiscussionsJson
(
allDivided
)
);
};
verifyMessage
=
function
(
expectedTitle
,
expectedMessageType
,
expectedAction
,
hasDetails
)
{
expect
(
discussionsView
.
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
expect
(
discussionsView
.
$
(
'div.message'
)).
toHaveClass
(
'message-'
+
expectedMessageType
);
if
(
expectedAction
)
{
expect
(
discussionsView
.
$
(
'.message-actions .action-primary'
).
text
().
trim
()).
toBe
(
expectedAction
);
}
else
{
expect
(
discussionsView
.
$
(
'.message-actions .action-primary'
).
length
).
toBe
(
0
);
}
if
(
!
hasDetails
)
{
expect
(
discussionsView
.
$
(
'.summary-items'
).
length
).
toBe
(
0
);
}
};
createDiscussionsView
=
function
(
test
,
options
)
{
var
discussionSettings
,
dividedDiscussions
,
discussionOptions
;
discussionOptions
=
options
||
{};
discussionSettings
=
discussionOptions
.
cohortSettings
||
createMockDiscussionsSettings
();
discussionSettings
.
url
=
'/mock_service/discussions/settings'
;
dividedDiscussions
=
discussionOptions
.
dividedDiscussions
||
createMockDiscussions
();
dividedDiscussions
.
url
=
'/mock_service/discussion/topics'
;
requests
=
AjaxHelpers
.
requests
(
test
);
discussionsView
=
new
DiscussionsView
({
el
:
$
(
'.discussions-management'
),
discussionSettings
:
discussionSettings
,
context
:
{
courseDiscussionTopicDetailsModel
:
dividedDiscussions
}
});
discussionsView
.
render
();
};
showAndAssertDiscussionTopics
=
function
()
{
var
$courseWideDiscussionsForm
,
$inlineDiscussionsForm
;
$courseWideDiscussionsForm
=
discussionsView
.
$
(
courseWideDiscussionsFormCss
);
$inlineDiscussionsForm
=
discussionsView
.
$
(
inlineDiscussionsFormCss
);
// Discussions form should not be visible.
expect
(
$inlineDiscussionsForm
.
length
).
toBe
(
1
);
expect
(
$courseWideDiscussionsForm
.
length
).
toBe
(
1
);
expect
(
$courseWideDiscussionsForm
.
text
()).
toContain
(
'Course-Wide Discussion Topics'
);
expect
(
$courseWideDiscussionsForm
.
text
()).
toContain
(
'Select the course-wide discussion topics that you want to divide by cohort.'
);
// Should see the inline discussions form and its content
expect
(
$inlineDiscussionsForm
.
length
).
toBe
(
1
);
expect
(
$inlineDiscussionsForm
.
text
()).
toContain
(
'Content-Specific Discussion Topics'
);
expect
(
$inlineDiscussionsForm
.
text
()).
toContain
(
'Specify whether content-specific discussion topics are divided by cohort.'
);
};
beforeEach
(
function
()
{
setFixtures
(
'<ul class="instructor-nav">'
+
'<li class="nav-item"><button type="button" data-section="discussion_management" '
+
'class="active-section">Discussions</button></li></ul><div></div>'
+
'<div class="discussions-management"></div>'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/discussions'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/divided-discussions-course-wide'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/divided-discussions-inline'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-category'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory'
);
TemplateHelpers
.
installTemplate
(
'templates/instructor/instructor_dashboard_2/notification'
);
});
describe
(
'Discussion Topics'
,
function
()
{
var
courseWideView
,
assertDividedTopics
;
assertDividedTopics
=
function
(
view
,
type
)
{
expect
(
$
(
'.check-discussion-subcategory-'
+
type
).
length
).
toBe
(
2
);
expect
(
$
(
'.check-discussion-subcategory-'
+
type
+
':checked'
).
length
).
toBe
(
1
);
};
it
(
'renders the view properly'
,
function
()
{
createDiscussionsView
(
this
);
showAndAssertDiscussionTopics
(
this
);
});
describe
(
'Course Wide'
,
function
()
{
it
(
'shows the "Save" button as disabled initially'
,
function
()
{
createDiscussionsView
(
this
);
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
});
it
(
'has one divided and one non-divided topic'
,
function
()
{
createDiscussionsView
(
this
);
assertDividedTopics
(
courseWideView
,
'course-wide'
);
expect
(
$
(
'.course-wide-discussion-topics .divided-discussion-text'
).
length
).
toBe
(
2
);
expect
(
$
(
'.course-wide-discussion-topics .divided-discussion-text.hidden'
).
length
).
toBe
(
1
);
});
it
(
'enables the "Save" button after changing checkbox'
,
function
()
{
createDiscussionsView
(
this
);
// save button is disabled.
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
$
(
$
(
'.check-discussion-subcategory-course-wide'
)[
0
]).
prop
(
'checked'
,
false
).
change
();
// save button is enabled.
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
});
it
(
'saves the topic successfully'
,
function
()
{
createDiscussionsView
(
this
);
$
(
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
// Save the updated settings
$
(
'#cohort-course-wide-discussions-form .action-save'
).
click
();
// fake requests for discussions settings with PATCH method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/discussions/settings'
,
{
divided_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
divided_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/discussion/topics'
);
AjaxHelpers
.
respondWithJson
(
requests
,
createMockDiscussions
()
);
// verify the success message.
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
verifyMessage
(
'Your changes have been saved.'
,
'confirmation'
);
});
it
(
'shows an appropriate message when subsequent "GET" returns HTTP500'
,
function
()
{
var
expectedTitle
;
createDiscussionsView
(
this
);
$
(
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
$
(
courseWideDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
// Save the updated settings
$
(
'#cohort-course-wide-discussions-form .action-save'
).
click
();
// fake requests for discussion settings with PATCH method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/discussions/settings'
,
{
divided_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
divided_course_wide_discussions
:
[
'Topic_C_1'
,
'Topic_C_2'
]}
);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/discussion/topics'
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate error message for HTTP500'
,
function
()
{
var
expectedTitle
;
createDiscussionsView
(
this
);
$
(
$
(
'.check-discussion-subcategory-course-wide'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
$
(
'.action-save'
).
click
();
AjaxHelpers
.
respondWithError
(
requests
,
500
);
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
});
describe
(
'Inline'
,
function
()
{
var
enableSaveButton
,
mockGetRequest
,
verifySuccess
,
mockPatchRequest
;
enableSaveButton
=
function
()
{
// enable the inline discussion topics.
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
$
(
$
(
'.check-discussion-subcategory-inline'
)[
0
]).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeFalsy
();
};
verifySuccess
=
function
()
{
// verify the success message.
expect
(
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
verifyMessage
(
'Your changes have been saved.'
,
'confirmation'
);
};
mockPatchRequest
=
function
(
dividededInlineDiscussions
)
{
AjaxHelpers
.
expectJsonRequest
(
requests
,
'PATCH'
,
'/mock_service/discussions/settings'
,
{
divided_inline_discussions
:
dividededInlineDiscussions
,
always_divide_inline_discussions
:
false
}
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
divided_inline_discussions
:
dividededInlineDiscussions
,
always_divide_inline_discussions
:
false
}
);
};
mockGetRequest
=
function
(
alldivided
)
{
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/discussion/topics'
);
AjaxHelpers
.
respondWithJson
(
requests
,
createMockDiscussions
(
alldivided
)
);
};
it
(
'shows the "Save" button as disabled initially'
,
function
()
{
createDiscussionsView
(
this
);
expect
(
$
(
inlineDiscussionsSaveButtonCss
).
prop
(
'disabled'
)).
toBeTruthy
();
});
it
(
'shows always divide radio button as selected'
,
function
()
{
createDiscussionsView
(
this
);
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
// verify always divide inline discussions is being selected.
expect
(
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)).
toBeTruthy
();
// verify that inline topics are disabled
expect
(
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'disabled'
)).
toBeTruthy
();
expect
(
$
(
'.check-discussion-category'
).
prop
(
'disabled'
)).
toBeTruthy
();
// verify that divide some topics are not being selected.
expect
(
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
)).
toBeFalsy
();
});
it
(
'shows divide some topics radio button as selected'
,
function
()
{
createDiscussionsView
(
this
);
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
,
'checked'
).
change
();
// verify some divide inline discussions radio is being selected.
expect
(
$
(
'.check-cohort-inline-discussions'
).
prop
(
'checked'
)).
toBeTruthy
();
// verify always divide radio is not selected.
expect
(
$
(
'.check-all-inline-discussions'
).
prop
(
'checked'
)).
toBeFalsy
();
// verify that inline topics are enabled
expect
(
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'disabled'
)).
toBeFalsy
();
expect
(
$
(
'.check-discussion-category'
).
prop
(
'disabled'
)).
toBeFalsy
();
});
it
(
'has divided and non-divided topics'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
assertDividedTopics
(
this
,
'inline'
);
});
it
(
'enables "Save" button after changing from always inline option'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
});
it
(
'saves the topic'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
// Save the updated settings
$
(
'.action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
mockGetRequest
();
verifySuccess
();
});
it
(
'selects the parent category when all children are selected'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'checked'
,
'checked'
).
change
();
// parent should be checked as we checked all children
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
});
it
(
'selects/deselects all children when a parent category is selected/deselected'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
$
(
'.check-discussion-category'
).
prop
(
'checked'
,
'checked'
).
change
();
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
expect
(
$
(
'.check-discussion-subcategory-inline:checked'
).
length
).
toBe
(
2
);
// un-check the parent, all children should be unchecd.
$
(
'.check-discussion-category'
).
prop
(
'checked'
,
false
).
change
();
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
$
(
'.check-discussion-subcategory-inline:checked'
).
length
).
toBe
(
0
);
});
it
(
'saves correctly when a subset of topics are selected within a category'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
// Save the updated settings
$
(
'#cohort-inline-discussions-form .action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
mockGetRequest
();
verifySuccess
();
// parent category should be indeterminate.
expect
(
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
});
it
(
'saves correctly when all child topics are selected within a category'
,
function
()
{
createDiscussionsView
(
this
);
enableSaveButton
();
// parent category should be indeterminate.
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
0
);
expect
(
$
(
'.check-discussion-category:indeterminate'
).
length
).
toBe
(
1
);
$
(
'.check-discussion-subcategory-inline'
).
prop
(
'checked'
,
'checked'
).
change
();
// Save the updated settings
$
(
'#cohort-inline-discussions-form .action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
,
'Inline_Discussion_2'
]);
mockGetRequest
(
true
);
verifySuccess
();
// parent category should be checked.
expect
(
$
(
'.check-discussion-category:checked'
).
length
).
toBe
(
1
);
});
it
(
'shows an appropriate message when no inline topics exist'
,
function
()
{
var
topicsJson
,
options
,
expectedTitle
;
topicsJson
=
{
course_wide_discussions
:
{
children
:
[[
'Topic_C_1'
,
'entry'
]],
entries
:
{
Topic_C_1
:
{
sort_key
:
null
,
is_divided
:
true
,
id
:
'Topic_C_1'
}
}
},
inline_discussions
:
{
subcategories
:
{},
children
:
[]
}
};
options
=
{
dividedDiscussions
:
new
CourseDiscussionTopicDetailsModel
(
topicsJson
)};
createDiscussionsView
(
this
,
options
);
expectedTitle
=
'No content-specific discussion topics exist.'
;
expect
(
$
(
'.no-topics'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate message when subsequent "GET" returns HTTP500'
,
function
()
{
var
expectedTitle
;
createDiscussionsView
(
this
);
enableSaveButton
();
// Save the updated settings
$
(
'#cohort-inline-discussions-form .action-save'
).
click
();
mockPatchRequest
([
'Inline_Discussion_1'
]);
// fake request for discussion/topics with GET method.
AjaxHelpers
.
expectJsonRequest
(
requests
,
'GET'
,
'/mock_service/discussion/topics'
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
it
(
'shows an appropriate error message for HTTP500'
,
function
()
{
var
expectedTitle
;
createDiscussionsView
(
this
);
enableSaveButton
();
$
(
$
(
'.check-discussion-subcategory-inline'
)[
1
]).
prop
(
'checked'
,
'checked'
).
change
();
$
(
'#cohort-inline-discussions-form .action-save'
).
click
();
AjaxHelpers
.
respondWithError
(
requests
,
500
);
expectedTitle
=
"We've encountered an error. Refresh your browser and then try again."
;
expect
(
$
(
'.message-title'
).
text
().
trim
()).
toBe
(
expectedTitle
);
});
});
});
});
});
lms/static/lms/js/build.js
View file @
c23c0b99
...
@@ -28,6 +28,7 @@
...
@@ -28,6 +28,7 @@
'js/edxnotes/views/page_factory'
,
'js/edxnotes/views/page_factory'
,
'js/financial-assistance/financial_assistance_form_factory'
,
'js/financial-assistance/financial_assistance_form_factory'
,
'js/groups/views/cohorts_dashboard_factory'
,
'js/groups/views/cohorts_dashboard_factory'
,
'js/discussions_management/views/discussions_dashboard_factory'
,
'js/header_factory'
,
'js/header_factory'
,
'js/learner_dashboard/program_details_factory'
,
'js/learner_dashboard/program_details_factory'
,
'js/learner_dashboard/program_list_factory'
,
'js/learner_dashboard/program_list_factory'
,
...
...
lms/static/lms/js/spec/main.js
View file @
c23c0b99
...
@@ -731,6 +731,7 @@
...
@@ -731,6 +731,7 @@
'js/spec/edxnotes/views/visibility_decorator_spec.js'
,
'js/spec/edxnotes/views/visibility_decorator_spec.js'
,
'js/spec/financial-assistance/financial_assistance_form_view_spec.js'
,
'js/spec/financial-assistance/financial_assistance_form_view_spec.js'
,
'js/spec/groups/views/cohorts_spec.js'
,
'js/spec/groups/views/cohorts_spec.js'
,
'js/spec/groups/views/discussions_spec.js'
,
'js/spec/instructor_dashboard/certificates_bulk_exception_spec.js'
,
'js/spec/instructor_dashboard/certificates_bulk_exception_spec.js'
,
'js/spec/instructor_dashboard/certificates_exception_spec.js'
,
'js/spec/instructor_dashboard/certificates_exception_spec.js'
,
'js/spec/instructor_dashboard/certificates_invalidation_spec.js'
,
'js/spec/instructor_dashboard/certificates_invalidation_spec.js'
,
...
...
lms/static/sass/course/instructor/_instructor_2.scss
View file @
c23c0b99
...
@@ -1181,49 +1181,6 @@
...
@@ -1181,49 +1181,6 @@
}
}
}
}
// cohort discussions interface.
.cohort-discussions-nav
{
.cohort-course-wide-discussions-form
{
.form-actions
{
padding-top
:
(
$baseline
/
2
);
}
}
.category-title
,
.topic-name
,
.all-inline-discussions
,
.always_cohort_inline_discussions
,
.cohort_inline_discussions
{
padding-left
:
(
$baseline
/
2
);
}
.always_cohort_inline_discussions
,
.cohort_inline_discussions
{
padding-top
:
(
$baseline
/
2
);
}
.category-item
,
.subcategory-item
{
padding-top
:
(
$baseline
/
2
);
}
.cohorted-text
{
color
:
$uxpl-blue-base
;
}
.discussions-wrapper
{
@extend
%ui-no-list
;
padding
:
0
(
$baseline
/
2
);
.subcategories
{
padding
:
0
(
$baseline
*
1
.5
);
}
}
}
.wrapper-tabs
{
// This applies to the tab-like interface that toggles between the student management and the group settings
.wrapper-tabs
{
// This applies to the tab-like interface that toggles between the student management and the group settings
@extend
%ui-no-list
;
@extend
%ui-no-list
;
@extend
%ui-depth1
;
@extend
%ui-depth1
;
...
@@ -1280,6 +1237,77 @@
...
@@ -1280,6 +1237,77 @@
}
}
}
}
// view - discussions management
// --------------------
.instructor-dashboard-wrapper-2
section
.idash-section
#discussions_management
{
.form-submit
{
@include
idashbutton
(
$uxpl-blue-base
);
@include
font-size
(
14
);
@include
line-height
(
14
);
margin-right
:
(
$baseline
/
2
);
margin-bottom
:
0
;
text-shadow
:
none
;
}
.discussions-management-supplemental
{
@extend
%t-copy-sub1
;
margin-top
:
$baseline
;
padding
:
(
$baseline
/
2
)
$baseline
;
background
:
$gray-l6
;
border-radius
:
(
$baseline
/
10
);
}
// cohort discussions interface.
.discussions-nav
{
.cohort-course-wide-discussions-form
{
.form-actions
{
padding-top
:
(
$baseline
/
2
);
}
}
.category-title
,
.topic-name
,
.all-inline-discussions
,
.always_divide_inline_discussions
,
.divide_inline_discussions
{
padding-left
:
(
$baseline
/
2
);
}
.always_divide_inline_discussions
,
.divide_inline_discussions
{
padding-top
:
(
$baseline
/
2
);
}
.category-item
,
.subcategory-item
{
padding-top
:
(
$baseline
/
2
);
}
.divided-discussion-text
{
color
:
$uxpl-blue-base
;
}
.discussions-wrapper
{
@extend
%ui-no-list
;
padding
:
0
(
$baseline
/
2
);
.subcategories
{
padding
:
0
(
$baseline
*
1
.5
);
}
}
}
.wrapper-tabs
{
@extend
%ui-no-list
;
@extend
%ui-depth1
;
position
:
relative
;
top
:
1px
;
padding
:
0
$baseline
;
}
}
// view - student admin
// view - student admin
// --------------------
// --------------------
...
...
lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore
View file @
c23c0b99
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
<label>
<label>
<input data-id="<%- id %>" class="check-discussion-subcategory-<%- type %>" type="checkbox" <%- is_divided ? 'checked="checked"' : '' %> />
<input data-id="<%- id %>" class="check-discussion-subcategory-<%- type %>" type="checkbox" <%- is_divided ? 'checked="checked"' : '' %> />
<span class="topic-name"><%- name %></span>
<span class="topic-name"><%- name %></span>
<span class="
cohorted-text <%- is_divided ? '' : 'hidden'%>">- <%- gettext('Cohort
ed') %></span>
<span class="
divided-discussion-text <%- is_divided ? '' : 'hidden'%>">- <%- gettext('Divid
ed') %></span>
</label>
</label>
</div>
</div>
</li>
</li>
lms/templates/instructor/instructor_dashboard_2/cohort_management.html
View file @
c23c0b99
...
@@ -13,7 +13,6 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_
...
@@ -13,7 +13,6 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_
data-cohorts_url=
"${section_data['cohorts_url']}"
data-cohorts_url=
"${section_data['cohorts_url']}"
data-upload_cohorts_csv_url=
"${section_data['upload_cohorts_csv_url']}"
data-upload_cohorts_csv_url=
"${section_data['upload_cohorts_csv_url']}"
data-course_cohort_settings_url=
"${section_data['course_cohort_settings_url']}"
data-course_cohort_settings_url=
"${section_data['course_cohort_settings_url']}"
data-discussion-topics-url=
"${section_data['discussion_topics_url']}"
data-verified_track_cohorting_url=
"${section_data['verified_track_cohorting_url']}"
data-verified_track_cohorting_url=
"${section_data['verified_track_cohorting_url']}"
data-is_ccx_enabled=
"${'true' if section_data['ccx_is_enabled'] else 'false'}"
data-is_ccx_enabled=
"${'true' if section_data['ccx_is_enabled'] else 'false'}"
>
>
...
...
lms/templates/instructor/instructor_dashboard_2/cohorts.underscore
View file @
c23c0b99
...
@@ -49,15 +49,5 @@
...
@@ -49,15 +49,5 @@
%>
%>
</p>
</p>
</div>
</div>
<hr class="divider divider-lv1" />
<!-- Discussion Topics. -->
<button class="toggle-cohort-management-discussions" data-href="#cohort-discussions-management"><%- gettext('Specify whether discussion topics are divided by cohort') %></button>
<div class="cohort-discussions-nav is-hidden" id="cohort-discussions-management" tabindex="-1">
<div class="cohort-course-wide-discussions-nav"></div>
<div class="cohort-inline-discussions-nav"></div>
</div>
</div>
</div>
<% } %>
<% } %>
lms/templates/instructor/instructor_dashboard_2/discussions.underscore
0 → 100644
View file @
c23c0b99
<!-- Discussion Topics. -->
<div class="discussions-nav" id="discussions-management" tabindex="-1">
<div class="course-wide-discussions-nav"></div>
<div class="inline-discussions-nav"></div>
</div>
lms/templates/instructor/instructor_dashboard_2/discussions_management.html
0 → 100644
View file @
c23c0b99
<
%
page
expression_filter=
"h"
args=
"section_data"
/>
<
%
namespace
name=
'static'
file=
'../../static_content.html'
/>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
djangolib
.
js_utils
import
js_escaped_string
,
dump_js_escaped_json
from
courseware
.
courses
import
get_studio_url
from
openedx
.
core
.
djangoapps
.
course_groups
.
partition_scheme
import
get_cohorted_user_partition
%
>
<div
class=
"discussions-management"
data-discussion-topics-url=
"${section_data['discussion_topics_url']}"
data-course-discussion-settings-url=
"${section_data['course_discussion_settings']}"
>
</div>
<
%
block
name=
"js_extra"
>
<
%
static:require_module
module_name=
"js/discussions_management/views/discussions_dashboard_factory"
class_name=
"DiscussionsFactory"
>
DiscussionsFactory();
</
%
static:require
_module
>
</
%
block>
lms/templates/instructor/instructor_dashboard_2/
cohort
-discussions-course-wide.underscore
→
lms/templates/instructor/instructor_dashboard_2/
divided
-discussions-course-wide.underscore
View file @
c23c0b99
<h3 class="hd hd-3 subsection-title"><%- gettext('Specify whether discussion topics are divided by cohort') %></h3>
<h3 class="hd hd-3 subsection-title"><%- gettext('Specify whether discussion topics are divided by cohort') %></h3>
<form action="" method="post" id="cohort-course-wide-discussions-form" class="cohort-course-wide-discussions-form">
<form action="" method="post" id="cohort-course-wide-discussions-form" class="cohort-course-wide-discussions-form">
<div class="wrapper
cohort
-management-supplemental">
<div class="wrapper
discussions
-management-supplemental">
<div class="form-fields">
<div class="form-fields">
<div class="form-field">
<div class="form-field">
<div class="course-wide-discussion-topics">
<div class="course-wide-discussion-topics">
...
...
lms/templates/instructor/instructor_dashboard_2/
cohort
-discussions-inline.underscore
→
lms/templates/instructor/instructor_dashboard_2/
divided
-discussions-inline.underscore
View file @
c23c0b99
<hr class="divider divider-lv1" />
<hr class="divider divider-lv1" />
<form action="" method="post" id="cohort-inline-discussions-form" class="cohort-inline-discussions-form">
<form action="" method="post" id="cohort-inline-discussions-form" class="cohort-inline-discussions-form">
<div class="wrapper
cohort
-management-supplemental">
<div class="wrapper
discussions
-management-supplemental">
<div class="form-fields">
<div class="form-fields">
<div class="form-field">
<div class="form-field">
<div class="inline-discussion-topics">
<div class="inline-discussion-topics">
<h4 class="hd hd-4 subsection-title"><%- gettext('Content-Specific Discussion Topics') %></h4>
<h4 class="hd hd-4 subsection-title"><%- gettext('Content-Specific Discussion Topics') %></h4>
<p><%- gettext('Specify whether content-specific discussion topics are divided by cohort.') %></p>
<p><%- gettext('Specify whether content-specific discussion topics are divided by cohort.') %></p>
<div class="always_
cohort
_inline_discussions">
<div class="always_
divide
_inline_discussions">
<label>
<label>
<input type="radio" name="inline" class="check-all-inline-discussions" <%- always
Cohort
InlineDiscussions ? 'checked="checked"' : '' %>/>
<input type="radio" name="inline" class="check-all-inline-discussions" <%- always
Divide
InlineDiscussions ? 'checked="checked"' : '' %>/>
<span class="all-inline-discussions"><%- gettext('Always cohort content-specific discussion topics') %></span>
<span class="all-inline-discussions"><%- gettext('Always cohort content-specific discussion topics') %></span>
</label>
</label>
</div>
</div>
<div class="
cohort
_inline_discussions">
<div class="
divide
_inline_discussions">
<label>
<label>
<input type="radio" name="inline" class="check-cohort-inline-discussions" <%- always
Cohort
InlineDiscussions ? '' : 'checked="checked"' %>/>
<input type="radio" name="inline" class="check-cohort-inline-discussions" <%- always
Divide
InlineDiscussions ? '' : 'checked="checked"' %>/>
<span class="all-inline-discussions"><%- gettext('Cohort selected content-specific discussion topics') %></span>
<span class="all-inline-discussions"><%- gettext('Cohort selected content-specific discussion topics') %></span>
</label>
</label>
</div>
</div>
...
...
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
View file @
c23c0b99
...
@@ -81,7 +81,7 @@ from openedx.core.djangolib.markup import HTML
...
@@ -81,7 +81,7 @@ from openedx.core.djangolib.markup import HTML
## Include Underscore templates
## Include Underscore templates
<
%
block
name=
"header_extras"
>
<
%
block
name=
"header_extras"
>
% for template_name in ["cohorts", "
enrollment-code-lookup-links", "cohort-editor", "cohort-group-header", "cohort-selector", "cohort-form", "notification", "cohort-state", "cohort-discussions-inline", "cohort
-discussions-course-wide", "cohort-discussions-category", "cohort-discussions-subcategory", "certificate-white-list", "certificate-white-list-editor", "certificate-bulk-white-list", "certificate-invalidation"]:
% for template_name in ["cohorts", "
discussions", "enrollment-code-lookup-links", "cohort-editor", "cohort-group-header", "cohort-selector", "cohort-form", "notification", "cohort-state", "divided-discussions-inline", "divided
-discussions-course-wide", "cohort-discussions-category", "cohort-discussions-subcategory", "certificate-white-list", "certificate-white-list-editor", "certificate-bulk-white-list", "certificate-invalidation"]:
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<script
type=
"text/template"
id=
"${template_name}-tpl"
>
<%
static
:
include
path
=
"instructor/instructor_dashboard_2/${template_name}.underscore"
/>
<%
static
:
include
path
=
"instructor/instructor_dashboard_2/${template_name}.underscore"
/>
</script>
</script>
...
...
lms/urls.py
View file @
c23c0b99
...
@@ -504,6 +504,15 @@ urlpatterns += (
...
@@ -504,6 +504,15 @@ urlpatterns += (
include
(
COURSE_URLS
)
include
(
COURSE_URLS
)
),
),
# Discussions Management
url
(
r'^courses/{}/discussions/settings$'
.
format
(
settings
.
COURSE_KEY_PATTERN
,
),
'openedx.core.djangoapps.course_groups.views.course_discussions_settings_handler'
,
name
=
'course_discussions_settings'
,
),
# Cohorts management
# Cohorts management
url
(
url
(
r'^courses/{}/cohorts/settings$'
.
format
(
r'^courses/{}/cohorts/settings$'
.
format
(
...
@@ -548,11 +557,11 @@ urlpatterns += (
...
@@ -548,11 +557,11 @@ urlpatterns += (
name
=
'debug_cohort_mgmt'
,
name
=
'debug_cohort_mgmt'
,
),
),
url
(
url
(
r'^courses/{}/
cohorts
/topics$'
.
format
(
r'^courses/{}/
discussion
/topics$'
.
format
(
settings
.
COURSE_KEY_PATTERN
,
settings
.
COURSE_KEY_PATTERN
,
),
),
'openedx.core.djangoapps.course_groups.views.
cohort_
discussion_topics'
,
'openedx.core.djangoapps.course_groups.views.discussion_topics'
,
name
=
'
cohort_
discussion_topics'
,
name
=
'discussion_topics'
,
),
),
url
(
url
(
r'^courses/{}/verified_track_content/settings'
.
format
(
r'^courses/{}/verified_track_content/settings'
.
format
(
...
...
openedx/core/djangoapps/course_groups/cohorts.py
View file @
c23c0b99
...
@@ -121,6 +121,16 @@ def is_course_cohorted(course_key):
...
@@ -121,6 +121,16 @@ def is_course_cohorted(course_key):
return
_get_course_cohort_settings
(
course_key
)
.
is_cohorted
return
_get_course_cohort_settings
(
course_key
)
.
is_cohorted
def
get_course_cohort_id
(
course_key
):
"""
Given a course key, return the int id for the cohort settings.
Raises:
Http404 if the course doesn't exist.
"""
return
_get_course_cohort_settings
(
course_key
)
.
id
def
set_course_cohorted
(
course_key
,
cohorted
):
def
set_course_cohorted
(
course_key
,
cohorted
):
"""
"""
Given a course course and a boolean, sets whether or not the course is cohorted.
Given a course course and a boolean, sets whether or not the course is cohorted.
...
...
openedx/core/djangoapps/course_groups/tests/helpers.py
View file @
c23c0b99
...
@@ -136,24 +136,18 @@ def config_course_cohorts_legacy(
...
@@ -136,24 +136,18 @@ def config_course_cohorts_legacy(
# pylint: disable=dangerous-default-value
# pylint: disable=dangerous-default-value
def
config_course_
cohort
s
(
def
config_course_
discussion
s
(
course
,
course
,
is_cohorted
,
discussion_topics
=
{},
auto_cohorts
=
[],
manual_cohorts
=
[],
discussion_topics
=
[],
divided_discussions
=
[],
divided_discussions
=
[],
always_divide_inline_discussions
=
False
always_divide_inline_discussions
=
False
):
):
"""
"""
Set discussions and configure cohort
s for a course.
Set discussions and configure divided discussion
s for a course.
Arguments:
Arguments:
course: CourseDescriptor
course: CourseDescriptor
is_cohorted (bool): Is the course cohorted?
discussion_topics (Dict): Discussion topic names. Picks ids and
auto_cohorts (list): Names of auto cohorts to create.
manual_cohorts (list): Names of manual cohorts to create.
discussion_topics (list): Discussion topic names. Picks ids and
sort_keys automatically.
sort_keys automatically.
divided_discussions: Discussion topics to divide. Converts the
divided_discussions: Discussion topics to divide. Converts the
list to use the same ids as discussion topic names.
list to use the same ids as discussion topic names.
...
@@ -163,11 +157,11 @@ def config_course_cohorts(
...
@@ -163,11 +157,11 @@ def config_course_cohorts(
Returns:
Returns:
Nothing -- modifies course in place.
Nothing -- modifies course in place.
"""
"""
def
to_id
(
name
):
def
to_id
(
name
):
"""Convert name to id."""
"""Convert name to id."""
return
topic_name_to_id
(
course
,
name
)
return
topic_name_to_id
(
course
,
name
)
set_course_cohorted
(
course
.
id
,
is_cohorted
)
set_course_discussion_settings
(
set_course_discussion_settings
(
course
.
id
,
course
.
id
,
divided_discussions
=
[
to_id
(
name
)
for
name
in
divided_discussions
],
divided_discussions
=
[
to_id
(
name
)
for
name
in
divided_discussions
],
...
@@ -175,6 +169,41 @@ def config_course_cohorts(
...
@@ -175,6 +169,41 @@ def config_course_cohorts(
division_scheme
=
CourseDiscussionSettings
.
COHORT
,
division_scheme
=
CourseDiscussionSettings
.
COHORT
,
)
)
course
.
discussion_topics
=
dict
((
name
,
{
"sort_key"
:
"A"
,
"id"
:
to_id
(
name
)})
for
name
in
discussion_topics
)
try
:
# Not implemented for XMLModulestore, which is used by test_cohorts.
modulestore
()
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
except
NotImplementedError
:
pass
# pylint: disable=dangerous-default-value
def
config_course_cohorts
(
course
,
is_cohorted
,
auto_cohorts
=
[],
manual_cohorts
=
[],
):
"""
Set and configure cohorts for a course.
Arguments:
course: CourseDescriptor
is_cohorted (bool): Is the course cohorted?
auto_cohorts (list): Names of auto cohorts to create.
manual_cohorts (list): Names of manual cohorts to create.
Returns:
Nothing -- modifies course in place.
"""
set_course_cohorted
(
course
.
id
,
is_cohorted
)
set_course_discussion_settings
(
course
.
id
,
division_scheme
=
CourseDiscussionSettings
.
COHORT
,
)
for
cohort_name
in
auto_cohorts
:
for
cohort_name
in
auto_cohorts
:
cohort
=
CohortFactory
(
course_id
=
course
.
id
,
name
=
cohort_name
)
cohort
=
CohortFactory
(
course_id
=
course
.
id
,
name
=
cohort_name
)
CourseCohortFactory
(
course_user_group
=
cohort
,
assignment_type
=
CourseCohort
.
RANDOM
)
CourseCohortFactory
(
course_user_group
=
cohort
,
assignment_type
=
CourseCohort
.
RANDOM
)
...
@@ -183,8 +212,6 @@ def config_course_cohorts(
...
@@ -183,8 +212,6 @@ def config_course_cohorts(
cohort
=
CohortFactory
(
course_id
=
course
.
id
,
name
=
cohort_name
)
cohort
=
CohortFactory
(
course_id
=
course
.
id
,
name
=
cohort_name
)
CourseCohortFactory
(
course_user_group
=
cohort
,
assignment_type
=
CourseCohort
.
MANUAL
)
CourseCohortFactory
(
course_user_group
=
cohort
,
assignment_type
=
CourseCohort
.
MANUAL
)
course
.
discussion_topics
=
dict
((
name
,
{
"sort_key"
:
"A"
,
"id"
:
to_id
(
name
)})
for
name
in
discussion_topics
)
try
:
try
:
# Not implemented for XMLModulestore, which is used by test_cohorts.
# Not implemented for XMLModulestore, which is used by test_cohorts.
modulestore
()
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
modulestore
()
.
update_item
(
course
,
ModuleStoreEnum
.
UserID
.
test
)
...
...
openedx/core/djangoapps/course_groups/tests/test_views.py
View file @
c23c0b99
...
@@ -25,15 +25,17 @@ from openedx.core.djangolib.testing.utils import skip_unless_lms
...
@@ -25,15 +25,17 @@ from openedx.core.djangolib.testing.utils import skip_unless_lms
from
..models
import
CourseUserGroup
,
CourseCohort
from
..models
import
CourseUserGroup
,
CourseCohort
from
..views
import
(
from
..views
import
(
course_cohort_settings_handler
,
cohort_handler
,
users_in_cohort
,
add_users_to_cohort
,
remove_user_from_cohort
,
course_cohort_settings_handler
,
course_discussions_settings_handler
,
link_cohort_to_partition_group
,
cohort_discussion_topics
cohort_handler
,
users_in_cohort
,
add_users_to_cohort
,
remove_user_from_cohort
,
link_cohort_to_partition_group
,
discussion_topics
)
)
from
..cohorts
import
(
from
..cohorts
import
(
get_cohort
,
get_cohort_by_name
,
get_cohort_by_id
,
get_cohort
,
get_cohort_by_name
,
get_cohort_by_id
,
DEFAULT_COHORT_NAME
,
get_group_info_for_cohort
DEFAULT_COHORT_NAME
,
get_group_info_for_cohort
)
)
from
.helpers
import
(
from
.helpers
import
(
config_course_cohorts
,
config_course_cohorts_legacy
,
CohortFactory
,
CourseCohortFactory
,
topic_name_to_id
config_course_cohorts
,
config_course_
discussions
,
config_course_
cohorts_legacy
,
CohortFactory
,
CourseCohortFactory
,
topic_name_to_id
)
)
...
@@ -99,13 +101,13 @@ class CohortViewsTestCase(ModuleStoreTestCase):
...
@@ -99,13 +101,13 @@ class CohortViewsTestCase(ModuleStoreTestCase):
view_args
.
insert
(
0
,
request
)
view_args
.
insert
(
0
,
request
)
self
.
assertRaises
(
Http404
,
view
,
*
view_args
)
self
.
assertRaises
(
Http404
,
view
,
*
view_args
)
def
create_
cohort
ed_discussions
(
self
):
def
create_
divid
ed_discussions
(
self
):
"""
"""
Set up a
cohort
ed discussion in the system, complete with all the fixings
Set up a
divid
ed discussion in the system, complete with all the fixings
"""
"""
cohort
ed_inline_discussions
=
[
'Topic A'
]
divid
ed_inline_discussions
=
[
'Topic A'
]
cohort
ed_course_wide_discussions
=
[
"Topic B"
]
divid
ed_course_wide_discussions
=
[
"Topic B"
]
cohorted_discussions
=
cohorted_inline_discussions
+
cohort
ed_course_wide_discussions
divided_discussions
=
divided_inline_discussions
+
divid
ed_course_wide_discussions
# inline discussion
# inline discussion
ItemFactory
.
create
(
ItemFactory
.
create
(
...
@@ -124,10 +126,14 @@ class CohortViewsTestCase(ModuleStoreTestCase):
...
@@ -124,10 +126,14 @@ class CohortViewsTestCase(ModuleStoreTestCase):
config_course_cohorts
(
config_course_cohorts
(
self
.
course
,
self
.
course
,
is_cohorted
=
True
,
is_cohorted
=
True
,
)
config_course_discussions
(
self
.
course
,
discussion_topics
=
discussion_topics
,
discussion_topics
=
discussion_topics
,
divided_discussions
=
cohort
ed_discussions
divided_discussions
=
divid
ed_discussions
)
)
return
cohorted_inline_discussions
,
cohort
ed_course_wide_discussions
return
divided_inline_discussions
,
divid
ed_course_wide_discussions
def
get_handler
(
self
,
course
,
cohort
=
None
,
expected_response_code
=
200
,
handler
=
cohort_handler
):
def
get_handler
(
self
,
course
,
cohort
=
None
,
expected_response_code
=
200
,
handler
=
cohort_handler
):
"""
"""
...
@@ -178,108 +184,74 @@ class CohortViewsTestCase(ModuleStoreTestCase):
...
@@ -178,108 +184,74 @@ class CohortViewsTestCase(ModuleStoreTestCase):
@attr
(
shard
=
2
)
@attr
(
shard
=
2
)
class
Course
CohortSetting
sHandlerTestCase
(
CohortViewsTestCase
):
class
Course
Discussion
sHandlerTestCase
(
CohortViewsTestCase
):
"""
"""
Tests the
`course_cohort_settings_handler` view.
Tests the
course_discussion_settings_handler
"""
"""
def
get_expected_response
(
self
):
def
get_expected_response
(
self
):
"""
"""
Returns the static response dict.
Returns the static response dict.
"""
"""
return
{
return
{
'is_cohorted'
:
True
,
u'always_divide_inline_discussions'
:
False
,
'always_cohort_inline_discussions'
:
False
,
u'divided_inline_discussions'
:
[],
'cohorted_inline_discussions'
:
[],
u'divided_course_wide_discussions'
:
[],
'cohorted_course_wide_discussions'
:
[],
u'id'
:
1
'id'
:
1
}
}
def
test_non_staff
(
self
):
def
test_non_staff
(
self
):
"""
"""
Verify that we cannot access course_cohort_settings_handler if we're a non-staff user.
Verify that we cannot access course_discussions_settings_handler if we're a non-staff user.
"""
self
.
_verify_non_staff_cannot_access
(
course_cohort_settings_handler
,
"GET"
,
[
unicode
(
self
.
course
.
id
)])
self
.
_verify_non_staff_cannot_access
(
course_cohort_settings_handler
,
"PATCH"
,
[
unicode
(
self
.
course
.
id
)])
def
test_get_settings
(
self
):
"""
Verify that course_cohort_settings_handler is working for HTTP GET.
"""
cohorted_inline_discussions
,
cohorted_course_wide_discussions
=
self
.
create_cohorted_discussions
()
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_cohort_settings_handler
)
expected_response
=
self
.
get_expected_response
()
expected_response
[
'cohorted_inline_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
name
)
for
name
in
cohorted_inline_discussions
]
expected_response
[
'cohorted_course_wide_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
name
)
for
name
in
cohorted_course_wide_discussions
]
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_is_cohorted_settings
(
self
):
"""
Verify that course_cohort_settings_handler is working for is_cohorted via HTTP PATCH.
"""
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
self
.
_verify_non_staff_cannot_access
(
course_discussions_settings_handler
,
"GET"
,
[
unicode
(
self
.
course
.
id
)])
self
.
_verify_non_staff_cannot_access
(
course_discussions_settings_handler
,
"PATCH"
,
[
unicode
(
self
.
course
.
id
)])
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_cohort_settings_handler
)
expected_response
=
self
.
get_expected_response
()
self
.
assertEqual
(
response
,
expected_response
)
expected_response
[
'is_cohorted'
]
=
False
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_cohort_settings_handler
)
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_always_
cohort
_inline_discussion_settings
(
self
):
def
test_update_always_
divide
_inline_discussion_settings
(
self
):
"""
"""
Verify that course_
cohort_settings_handler is working for always_cohort
_inline_discussions via HTTP PATCH.
Verify that course_
discussions_settings_handler is working for always_divide
_inline_discussions via HTTP PATCH.
"""
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
discussions
_settings_handler
)
expected_response
=
self
.
get_expected_response
()
expected_response
=
self
.
get_expected_response
()
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
expected_response
[
'always_
cohort
_inline_discussions'
]
=
True
expected_response
[
'always_
divide
_inline_discussions'
]
=
True
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
discussions
_settings_handler
)
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_course_wide_discussion_settings
(
self
):
def
test_update_course_wide_discussion_settings
(
self
):
"""
"""
Verify that course_
cohort_settings_handler is working for cohort
ed_course_wide_discussions via HTTP PATCH.
Verify that course_
discussions_settings_handler is working for divid
ed_course_wide_discussions via HTTP PATCH.
"""
"""
# course-wide discussion
# course-wide discussion
discussion_topics
=
{
discussion_topics
=
{
"Topic B"
:
{
"id"
:
"Topic B"
},
"Topic B"
:
{
"id"
:
"Topic B"
},
}
}
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
,
discussion_topics
=
discussion_topics
)
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
config_course_discussions
(
self
.
course
,
discussion_topics
=
discussion_topics
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
discussions
_settings_handler
)
expected_response
=
self
.
get_expected_response
()
expected_response
=
self
.
get_expected_response
()
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
expected_response
[
'
cohort
ed_course_wide_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
"Topic B"
)]
expected_response
[
'
divid
ed_course_wide_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
"Topic B"
)]
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
discussions
_settings_handler
)
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_inline_discussion_settings
(
self
):
def
test_update_inline_discussion_settings
(
self
):
"""
"""
Verify that course_
cohort_settings_handler is working for cohort
ed_inline_discussions via HTTP PATCH.
Verify that course_
discussions_settings_handler is working for divid
ed_inline_discussions via HTTP PATCH.
"""
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_
discussions
_settings_handler
)
expected_response
=
self
.
get_expected_response
()
expected_response
=
self
.
get_expected_response
()
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
...
@@ -295,37 +267,99 @@ class CourseCohortSettingsHandlerTestCase(CohortViewsTestCase):
...
@@ -295,37 +267,99 @@ class CourseCohortSettingsHandlerTestCase(CohortViewsTestCase):
start
=
now
start
=
now
)
)
expected_response
[
'
cohort
ed_inline_discussions'
]
=
[
"Topic_A"
]
expected_response
[
'
divid
ed_inline_discussions'
]
=
[
"Topic_A"
]
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
cohort
_settings_handler
)
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_
discussions
_settings_handler
)
self
.
assertEqual
(
response
,
expected_response
)
self
.
assertEqual
(
response
,
expected_response
)
def
test_
update_settings_with_missing_field
(
self
):
def
test_
get_settings
(
self
):
"""
"""
Verify that course_
cohort_settings_handler return HTTP 400 if required data field is missing from post data
.
Verify that course_
discussions_settings_handler is working for HTTP GET
.
"""
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
divided_inline_discussions
,
divided_course_wide_discussions
=
self
.
create_divided_discussions
(
)
response
=
self
.
patch_handler
(
self
.
course
,
expected_response_code
=
400
,
handler
=
course_cohort_settings_handler
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_discussions_settings_handler
)
self
.
assertEqual
(
"Bad Request"
,
response
.
get
(
"error"
))
expected_response
=
self
.
get_expected_response
()
expected_response
[
'divided_inline_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
name
)
for
name
in
divided_inline_discussions
]
expected_response
[
'divided_course_wide_discussions'
]
=
[
topic_name_to_id
(
self
.
course
,
name
)
for
name
in
divided_course_wide_discussions
]
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_settings_with_invalid_field_data_type
(
self
):
def
test_update_settings_with_invalid_field_data_type
(
self
):
"""
"""
Verify that course_
cohort
_settings_handler return HTTP 400 if field data type is incorrect.
Verify that course_
discussions
_settings_handler return HTTP 400 if field data type is incorrect.
"""
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
patch_handler
(
response
=
self
.
patch_handler
(
self
.
course
,
self
.
course
,
data
=
{
'always_
cohort
_inline_discussions'
:
''
},
data
=
{
'always_
divide
_inline_discussions'
:
''
},
expected_response_code
=
400
,
expected_response_code
=
400
,
handler
=
course_
cohort
_settings_handler
handler
=
course_
discussions
_settings_handler
)
)
self
.
assertEqual
(
self
.
assertEqual
(
"Incorrect field type for `{}`. Type must be `{}`"
.
format
(
'always_divide_inline_discussions'
,
bool
.
__name__
),
"Incorrect field type for `{}`. Type must be `{}`"
.
format
(
'always_divide_inline_discussions'
,
bool
.
__name__
),
response
.
get
(
"error"
)
response
.
get
(
"error"
)
)
)
@attr
(
shard
=
2
)
class
CourseCohortSettingsHandlerTestCase
(
CohortViewsTestCase
):
"""
Tests the `course_cohort_settings_handler` view.
"""
def
get_expected_response
(
self
):
"""
Returns the static response dict.
"""
return
{
'is_cohorted'
:
True
,
'id'
:
1
}
def
test_non_staff
(
self
):
"""
Verify that we cannot access course_cohort_settings_handler if we're a non-staff user.
"""
self
.
_verify_non_staff_cannot_access
(
course_cohort_settings_handler
,
"GET"
,
[
unicode
(
self
.
course
.
id
)])
self
.
_verify_non_staff_cannot_access
(
course_cohort_settings_handler
,
"PATCH"
,
[
unicode
(
self
.
course
.
id
)])
def
test_update_is_cohorted_settings
(
self
):
"""
Verify that course_cohort_settings_handler is working for is_cohorted via HTTP PATCH.
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
course_cohort_settings_handler
)
expected_response
=
self
.
get_expected_response
()
self
.
assertEqual
(
response
,
expected_response
)
expected_response
[
'is_cohorted'
]
=
False
response
=
self
.
patch_handler
(
self
.
course
,
data
=
expected_response
,
handler
=
course_cohort_settings_handler
)
self
.
assertEqual
(
response
,
expected_response
)
def
test_update_settings_with_missing_field
(
self
):
"""
Verify that course_cohort_settings_handler return HTTP 400 if required data field is missing from post data.
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
patch_handler
(
self
.
course
,
expected_response_code
=
400
,
handler
=
course_cohort_settings_handler
)
self
.
assertEqual
(
"Bad Request"
,
response
.
get
(
"error"
))
def
test_update_settings_with_invalid_field_data_type
(
self
):
"""
Verify that course_cohort_settings_handler return HTTP 400 if field data type is incorrect.
"""
config_course_cohorts
(
self
.
course
,
is_cohorted
=
True
)
response
=
self
.
patch_handler
(
response
=
self
.
patch_handler
(
self
.
course
,
self
.
course
,
data
=
{
'is_cohorted'
:
''
},
data
=
{
'is_cohorted'
:
''
},
...
@@ -1240,25 +1274,25 @@ class RemoveUserFromCohortTestCase(CohortViewsTestCase):
...
@@ -1240,25 +1274,25 @@ class RemoveUserFromCohortTestCase(CohortViewsTestCase):
@attr
(
shard
=
2
)
@attr
(
shard
=
2
)
@skip_unless_lms
@skip_unless_lms
class
Course
Cohort
DiscussionTopicsTestCase
(
CohortViewsTestCase
):
class
Course
Divided
DiscussionTopicsTestCase
(
CohortViewsTestCase
):
"""
"""
Tests the `
cohort
_discussion_topics` view.
Tests the `
divide
_discussion_topics` view.
"""
"""
def
test_non_staff
(
self
):
def
test_non_staff
(
self
):
"""
"""
Verify that we cannot access
cohort
_discussion_topics if we're a non-staff user.
Verify that we cannot access
divide
_discussion_topics if we're a non-staff user.
"""
"""
self
.
_verify_non_staff_cannot_access
(
cohort_
discussion_topics
,
"GET"
,
[
unicode
(
self
.
course
.
id
)])
self
.
_verify_non_staff_cannot_access
(
discussion_topics
,
"GET"
,
[
unicode
(
self
.
course
.
id
)])
def
test_get_discussion_topics
(
self
):
def
test_get_discussion_topics
(
self
):
"""
"""
Verify that
course_cohort_settings_handler
is working for HTTP GET.
Verify that
divide_discussion_topics
is working for HTTP GET.
"""
"""
# create inline & course-wide discussion to verify the different map.
# create inline & course-wide discussion to verify the different map.
self
.
create_
cohort
ed_discussions
()
self
.
create_
divid
ed_discussions
()
response
=
self
.
get_handler
(
self
.
course
,
handler
=
cohort_
discussion_topics
)
response
=
self
.
get_handler
(
self
.
course
,
handler
=
discussion_topics
)
start_date
=
response
[
'inline_discussions'
][
'subcategories'
][
'Chapter'
][
'start_date'
]
start_date
=
response
[
'inline_discussions'
][
'subcategories'
][
'Chapter'
][
'start_date'
]
expected_response
=
{
expected_response
=
{
"course_wide_discussions"
:
{
"course_wide_discussions"
:
{
...
...
openedx/core/djangoapps/course_groups/views.py
View file @
c23c0b99
...
@@ -64,20 +64,29 @@ def unlink_cohort_partition_group(cohort):
...
@@ -64,20 +64,29 @@ def unlink_cohort_partition_group(cohort):
# pylint: disable=invalid-name
# pylint: disable=invalid-name
def
_get_course_cohort_settings_representation
(
co
urse
,
is_cohorted
,
course_discussion_settings
):
def
_get_course_cohort_settings_representation
(
co
hort_id
,
is_cohorted
):
"""
"""
Returns a JSON representation of a course cohort settings.
Returns a JSON representation of a course cohort settings.
"""
"""
return
{
'id'
:
cohort_id
,
'is_cohorted'
:
is_cohorted
,
}
def
_get_course_discussion_settings_representation
(
course
,
course_discussion_settings
):
"""
Returns a JSON representation of a course discussion settings.
"""
divided_course_wide_discussions
,
divided_inline_discussions
=
get_divided_discussions
(
divided_course_wide_discussions
,
divided_inline_discussions
=
get_divided_discussions
(
course
,
course_discussion_settings
course
,
course_discussion_settings
)
)
return
{
return
{
'id'
:
course_discussion_settings
.
id
,
'id'
:
course_discussion_settings
.
id
,
'is_cohorted'
:
is_cohorted
,
'divided_inline_discussions'
:
divided_inline_discussions
,
'cohorted_inline_discussions'
:
divided_inline_discussions
,
'divided_course_wide_discussions'
:
divided_course_wide_discussions
,
'cohorted_course_wide_discussions'
:
divided_course_wide_discussions
,
'always_divide_inline_discussions'
:
course_discussion_settings
.
always_divide_inline_discussions
,
'always_cohort_inline_discussions'
:
course_discussion_settings
.
always_divide_inline_discussions
,
}
}
...
@@ -121,18 +130,17 @@ def get_divided_discussions(course, discussion_settings):
...
@@ -121,18 +130,17 @@ def get_divided_discussions(course, discussion_settings):
@ensure_csrf_cookie
@ensure_csrf_cookie
@expect_json
@expect_json
@login_required
@login_required
def
course_
cohort
_settings_handler
(
request
,
course_key_string
):
def
course_
discussions
_settings_handler
(
request
,
course_key_string
):
"""
"""
The restful handler for
cohort
setting requests. Requires JSON.
The restful handler for
divided discussion
setting requests. Requires JSON.
This will raise 404 if user is not staff.
This will raise 404 if user is not staff.
GET
GET
Returns the JSON representation of
cohort
settings for the course.
Returns the JSON representation of
divided discussion
settings for the course.
PATCH
PATCH
Updates the
cohort
settings for the course. Returns the JSON representation of updated settings.
Updates the
divided discussion
settings for the course. Returns the JSON representation of updated settings.
"""
"""
course_key
=
CourseKey
.
from_string
(
course_key_string
)
course_key
=
CourseKey
.
from_string
(
course_key_string
)
course
=
get_course_with_access
(
request
.
user
,
'staff'
,
course_key
)
course
=
get_course_with_access
(
request
.
user
,
'staff'
,
course_key
)
is_cohorted
=
cohorts
.
is_course_cohorted
(
course_key
)
discussion_settings
=
get_course_discussion_settings
(
course_key
)
discussion_settings
=
get_course_discussion_settings
(
course_key
)
if
request
.
method
==
'PATCH'
:
if
request
.
method
==
'PATCH'
:
...
@@ -142,40 +150,72 @@ def course_cohort_settings_handler(request, course_key_string):
...
@@ -142,40 +150,72 @@ def course_cohort_settings_handler(request, course_key_string):
settings_to_change
=
{}
settings_to_change
=
{}
if
'is_cohorted'
in
request
.
json
:
if
'divided_course_wide_discussions'
in
request
.
json
or
'divided_inline_discussions'
in
request
.
json
:
settings_to_change
[
'is_cohorted'
]
=
request
.
json
.
get
(
'is_cohorted'
)
if
'cohorted_course_wide_discussions'
in
request
.
json
or
'cohorted_inline_discussions'
in
request
.
json
:
divided_course_wide_discussions
=
request
.
json
.
get
(
divided_course_wide_discussions
=
request
.
json
.
get
(
'
cohort
ed_course_wide_discussions'
,
divided_course_wide_discussions
'
divid
ed_course_wide_discussions'
,
divided_course_wide_discussions
)
)
divided_inline_discussions
=
request
.
json
.
get
(
divided_inline_discussions
=
request
.
json
.
get
(
'
cohort
ed_inline_discussions'
,
divided_inline_discussions
'
divid
ed_inline_discussions'
,
divided_inline_discussions
)
)
settings_to_change
[
'divided_discussions'
]
=
divided_course_wide_discussions
+
divided_inline_discussions
settings_to_change
[
'divided_discussions'
]
=
divided_course_wide_discussions
+
divided_inline_discussions
if
'always_
cohort
_inline_discussions'
in
request
.
json
:
if
'always_
divide
_inline_discussions'
in
request
.
json
:
settings_to_change
[
'always_divide_inline_discussions'
]
=
request
.
json
.
get
(
settings_to_change
[
'always_divide_inline_discussions'
]
=
request
.
json
.
get
(
'always_
cohort
_inline_discussions'
'always_
divide
_inline_discussions'
)
)
if
not
settings_to_change
:
if
not
settings_to_change
:
return
JsonResponse
({
"error"
:
unicode
(
"Bad Request"
)},
400
)
return
JsonResponse
({
"error"
:
unicode
(
"Bad Request"
)},
400
)
try
:
try
:
if
'is_cohorted'
in
settings_to_change
:
is_cohorted
=
settings_to_change
[
'is_cohorted'
]
cohorts
.
set_course_cohorted
(
course_key
,
is_cohorted
)
del
settings_to_change
[
'is_cohorted'
]
settings_to_change
[
'division_scheme'
]
=
CourseDiscussionSettings
.
COHORT
if
is_cohorted
\
else
CourseDiscussionSettings
.
NONE
if
settings_to_change
:
if
settings_to_change
:
discussion_settings
=
set_course_discussion_settings
(
course_key
,
**
settings_to_change
)
discussion_settings
=
set_course_discussion_settings
(
course_key
,
**
settings_to_change
)
except
ValueError
as
err
:
# Note: error message not translated because it is not exposed to the user (UI prevents this state).
return
JsonResponse
({
"error"
:
unicode
(
err
)},
400
)
return
JsonResponse
(
_get_course_discussion_settings_representation
(
course
,
discussion_settings
))
@require_http_methods
((
"GET"
,
"PATCH"
))
@ensure_csrf_cookie
@expect_json
@login_required
def
course_cohort_settings_handler
(
request
,
course_key_string
):
"""
The restful handler for cohort setting requests. Requires JSON.
This will raise 404 if user is not staff.
GET
Returns the JSON representation of cohort settings for the course.
PATCH
Updates the cohort settings for the course. Returns the JSON representation of updated settings.
"""
course_key
=
CourseKey
.
from_string
(
course_key_string
)
# Although this course data is not used this method will return 404 is user is not staff
course
=
get_course_with_access
(
request
.
user
,
'staff'
,
course_key
)
if
request
.
method
==
'PATCH'
:
if
'is_cohorted'
not
in
request
.
json
:
return
JsonResponse
({
"error"
:
unicode
(
"Bad Request"
)},
400
)
is_cohorted
=
request
.
json
.
get
(
'is_cohorted'
)
try
:
cohorts
.
set_course_cohorted
(
course_key
,
is_cohorted
)
scheme
=
CourseDiscussionSettings
.
COHORT
if
is_cohorted
else
CourseDiscussionSettings
.
NONE
scheme_settings
=
{
'division_scheme'
:
scheme
}
set_course_discussion_settings
(
course_key
,
**
scheme_settings
)
except
ValueError
as
err
:
except
ValueError
as
err
:
# Note: error message not translated because it is not exposed to the user (UI prevents this state).
# Note: error message not translated because it is not exposed to the user (UI prevents this state).
return
JsonResponse
({
"error"
:
unicode
(
err
)},
400
)
return
JsonResponse
({
"error"
:
unicode
(
err
)},
400
)
return
JsonResponse
(
_get_course_cohort_settings_representation
(
course
,
is_cohorted
,
discussion_settings
))
return
JsonResponse
(
_get_course_cohort_settings_representation
(
cohorts
.
get_course_cohort_id
(
course_key
),
cohorts
.
is_course_cohorted
(
course_key
)
))
@require_http_methods
((
"GET"
,
"PUT"
,
"POST"
,
"PATCH"
))
@require_http_methods
((
"GET"
,
"PUT"
,
"POST"
,
"PATCH"
))
...
@@ -428,9 +468,9 @@ def debug_cohort_mgmt(request, course_key_string):
...
@@ -428,9 +468,9 @@ def debug_cohort_mgmt(request, course_key_string):
@expect_json
@expect_json
@login_required
@login_required
def
cohort_
discussion_topics
(
request
,
course_key_string
):
def
discussion_topics
(
request
,
course_key_string
):
"""
"""
The handler for
cohort
discussion categories requests.
The handler for
divided
discussion categories requests.
This will raise 404 if user is not staff.
This will raise 404 if user is not staff.
Returns the JSON representation of discussion topics w.r.t categories for the course.
Returns the JSON representation of discussion topics w.r.t categories for the course.
...
...
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