diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py
index 8a7b4dcf79..9f4b1d3527 100644
--- a/common/test/acceptance/pages/lms/instructor_dashboard.py
+++ b/common/test/acceptance/pages/lms/instructor_dashboard.py
@@ -28,6 +28,15 @@ class InstructorDashboardPage(CoursePage):
membership_section.wait_for_page()
return membership_section
+ def select_cohort_management(self):
+ """
+ Selects the cohort management tab and returns the CohortManagementSection
+ """
+ self.q(css='a[data-section=cohort_management]').first.click()
+ cohort_management_section = CohortManagementSection(self.browser)
+ cohort_management_section.wait_for_page()
+ return cohort_management_section
+
def select_data_download(self):
"""
Selects the data download tab and returns a DataDownloadPage.
@@ -84,16 +93,10 @@ class MembershipPage(PageObject):
"""
return MembershipPageAutoEnrollSection(self.browser)
- def select_cohort_management_section(self):
- """
- Returns the MembershipPageCohortManagementSection page object.
- """
- return MembershipPageCohortManagementSection(self.browser)
-
-class MembershipPageCohortManagementSection(PageObject):
+class CohortManagementSection(PageObject):
"""
- The cohort management subsection of the Membership section of the Instructor dashboard.
+ The Cohort Management section of the Instructor dashboard.
"""
url = None
csv_browse_button_selector_css = '.csv-upload #file-upload-form-file'
@@ -104,13 +107,13 @@ class MembershipPageCohortManagementSection(PageObject):
assignment_type_buttons_css = '.cohort-management-assignment-type-settings input'
def is_browser_on_page(self):
- return self.q(css='.cohort-management.membership-section').present
+ return self.q(css='.cohort-management').present
def _bounded_selector(self, selector):
"""
Return `selector`, but limited to the cohort management context.
"""
- return '.cohort-management.membership-section {}'.format(selector)
+ return '.cohort-management {}'.format(selector)
def _get_cohort_options(self):
"""
@@ -158,10 +161,10 @@ class MembershipPageCohortManagementSection(PageObject):
Return assignment settings disabled message in case of default cohort.
"""
query = self.q(css=self._bounded_selector('.copy-error'))
- if query.present:
+ if query.visible:
return query.text[0]
- else:
- return ''
+
+ return ''
@property
def cohort_name_in_header(self):
@@ -232,7 +235,11 @@ class MembershipPageCohortManagementSection(PageObject):
Adds a new manual cohort with the specified name.
If a content group should also be associated, the name of the content group should be specified.
"""
- create_buttons = self.q(css=self._bounded_selector(".action-create"))
+ add_cohort_selector = self._bounded_selector(".action-create")
+
+ # We need to wait because sometime add cohort button is not in a state to be clickable.
+ self.wait_for_element_presence(add_cohort_selector, 'Add Cohort button is present.')
+ create_buttons = self.q(css=add_cohort_selector)
# There are 2 create buttons on the page. The second one is only present when no cohort yet exists
# (in which case the first is not visible). Click on the last present create button.
create_buttons.results[len(create_buttons.results) - 1].click()
@@ -444,6 +451,28 @@ class MembershipPageCohortManagementSection(PageObject):
file_input.send_keys(path)
self.q(css=self._bounded_selector(self.csv_upload_button_selector_css)).first.click()
+ @property
+ def is_cohorted(self):
+ """
+ Returns the state of `Enable Cohorts` checkbox state.
+ """
+ return self.q(css=self._bounded_selector('.cohorts-state')).selected
+
+ @is_cohorted.setter
+ def is_cohorted(self, state):
+ """
+ Check/Uncheck the `Enable Cohorts` checkbox state.
+ """
+ if state != self.is_cohorted:
+ self.q(css=self._bounded_selector('.cohorts-state')).first.click()
+
+ def cohort_management_controls_visible(self):
+ """
+ Return the visibility status of cohort management controls(cohort selector section etc).
+ """
+ return (self.q(css=self._bounded_selector('.cohort-management-nav')).visible and
+ self.q(css=self._bounded_selector('.wrapper-cohort-supplemental')).visible)
+
class MembershipPageAutoEnrollSection(PageObject):
"""
diff --git a/common/test/acceptance/tests/discussion/test_cohort_management.py b/common/test/acceptance/tests/discussion/test_cohort_management.py
index 31e4db463c..7ec69497b9 100644
--- a/common/test/acceptance/tests/discussion/test_cohort_management.py
+++ b/common/test/acceptance/tests/discussion/test_cohort_management.py
@@ -63,8 +63,7 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
# go to the membership page on the instructor dashboard
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
self.instructor_dashboard_page.visit()
- membership_page = self.instructor_dashboard_page.select_membership()
- self.cohort_management_page = membership_page.select_cohort_management_section()
+ self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
def verify_cohort_description(self, cohort_name, expected_description):
"""
@@ -441,9 +440,31 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
self.assertTrue(self.cohort_management_page.is_assignment_settings_disabled)
- message = "There must be one cohort to which students can be randomly assigned."
+ message = "There must be one cohort to which students can automatically be assigned."
self.assertEqual(message, self.cohort_management_page.assignment_settings_message)
+ def test_cohort_enable_disable(self):
+ """
+ Scenario: Cohort Enable/Disable checkbox related functionality is working as intended.
+
+ Given I have a cohorted course with a user.
+ And I can see the `Enable Cohorts` checkbox is checked.
+ And cohort management controls are visible.
+ When I uncheck the `Enable Cohorts` checkbox.
+ Then I cohort management controls are not visible.
+ And When I reload the page.
+ Then I can see the `Enable Cohorts` checkbox is unchecked.
+ And cohort management controls are not visible.
+ """
+ self.assertTrue(self.cohort_management_page.is_cohorted)
+ self.assertTrue(self.cohort_management_page.cohort_management_controls_visible())
+ self.cohort_management_page.is_cohorted = False
+ self.assertFalse(self.cohort_management_page.cohort_management_controls_visible())
+ self.browser.refresh()
+ self.cohort_management_page.wait_for_page()
+ self.assertFalse(self.cohort_management_page.is_cohorted)
+ self.assertFalse(self.cohort_management_page.cohort_management_controls_visible())
+
def test_link_to_data_download(self):
"""
Scenario: a link is present from the cohort configuration in
@@ -656,8 +677,7 @@ class CohortContentGroupAssociationTest(UniqueCourseTest, CohortTestMixin):
# go to the membership page on the instructor dashboard
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
self.instructor_dashboard_page.visit()
- membership_page = self.instructor_dashboard_page.select_membership()
- self.cohort_management_page = membership_page.select_cohort_management_section()
+ self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
def test_no_content_group_linked(self):
"""
diff --git a/common/test/acceptance/tests/test_cohorted_courseware.py b/common/test/acceptance/tests/test_cohorted_courseware.py
index 00f42cd14e..cd1df402b1 100644
--- a/common/test/acceptance/tests/test_cohorted_courseware.py
+++ b/common/test/acceptance/tests/test_cohorted_courseware.py
@@ -154,8 +154,7 @@ class EndToEndCohortedCoursewareTest(ContainerBase):
"""
instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
instructor_dashboard_page.visit()
- membership_page = instructor_dashboard_page.select_membership()
- cohort_management_page = membership_page.select_cohort_management_section()
+ cohort_management_page = instructor_dashboard_page.select_cohort_management()
def add_cohort_with_student(cohort_name, content_group, student):
cohort_management_page.add_cohort(cohort_name, content_group=content_group)
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index 512283db94..ed6b7aefea 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -65,9 +65,7 @@ def instructor_dashboard_2(request, course_id):
'finance_admin': CourseFinanceAdminRole(course_key).has_user(request.user),
'sales_admin': CourseSalesAdminRole(course_key).has_user(request.user),
'staff': has_access(request.user, 'staff', course),
- 'forum_admin': has_forum_access(
- request.user, course_key, FORUM_ROLE_ADMINISTRATOR
- ),
+ 'forum_admin': has_forum_access(request.user, course_key, FORUM_ROLE_ADMINISTRATOR),
}
if not access['staff']:
@@ -79,6 +77,7 @@ def instructor_dashboard_2(request, course_id):
_section_student_admin(course, access),
_section_data_download(course, access),
_section_analytics(course, access),
+ _section_cohort_management(course, access),
]
#check if there is corresponding entry in the CourseMode Table related to the Instructor Dashboard course
@@ -330,7 +329,22 @@ def _section_membership(course, access):
'modify_access_url': reverse('modify_access', kwargs={'course_id': unicode(course_key)}),
'list_forum_members_url': reverse('list_forum_members', kwargs={'course_id': unicode(course_key)}),
'update_forum_role_membership_url': reverse('update_forum_role_membership', kwargs={'course_id': unicode(course_key)}),
- 'cohorts_ajax_url': reverse('cohorts', kwargs={'course_key_string': unicode(course_key)}),
+ }
+ return section_data
+
+
+def _section_cohort_management(course, access):
+ """ Provide data for the corresponding cohort management section """
+ course_key = course.id
+ section_data = {
+ 'section_key': 'cohort_management',
+ 'section_display_name': _('Cohort Management'),
+ 'access': access,
+ 'course_cohort_settings_url': reverse(
+ 'course_cohort_settings',
+ kwargs={'course_key_string': unicode(course_key)}
+ ),
+ 'cohorts_url': reverse('cohorts', kwargs={'course_key_string': unicode(course_key)}),
'advanced_settings_url': get_studio_url(course, 'settings/advanced'),
'upload_cohorts_csv_url': reverse('add_users_to_cohorts', kwargs={'course_id': unicode(course_key)}),
}
diff --git a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
index fa7c0d353f..3334bd8fb8 100644
--- a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
@@ -176,6 +176,9 @@ setup_instructor_dashboard_sections = (idash_content) ->
,
constructor: window.InstructorDashboard.sections.Metrics
$element: idash_content.find ".#{CSS_IDASH_SECTION}#metrics"
+ ,
+ constructor: window.InstructorDashboard.sections.CohortManagement
+ $element: idash_content.find ".#{CSS_IDASH_SECTION}#cohort_management"
]
sections_to_initialize.map ({constructor, $element}) ->
diff --git a/lms/static/js/factories/cohorts_factory.js b/lms/static/js/factories/cohorts_factory.js
new file mode 100644
index 0000000000..3d04e1fd91
--- /dev/null
+++ b/lms/static/js/factories/cohorts_factory.js
@@ -0,0 +1,35 @@
+;(function (define, undefined) {
+ 'use strict';
+ define(['jquery', 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/course_cohort_settings'],
+ function($) {
+
+ return function(contentGroups, studioGroupConfigurationsUrl) {
+
+ var cohorts = new edx.groups.CohortCollection(),
+ courseCohortSettings = new edx.groups.CourseCohortSettingsModel();
+
+ var cohortManagementElement = $('.cohort-management');
+
+ cohorts.url = cohortManagementElement.data('cohorts_url');
+ courseCohortSettings.url = cohortManagementElement.data('course_cohort_settings_url');
+
+ var cohortsView = new edx.groups.CohortsView({
+ el: cohortManagementElement,
+ model: cohorts,
+ contentGroups: contentGroups,
+ cohortSettings: courseCohortSettings,
+ context: {
+ uploadCohortsCsvUrl: cohortManagementElement.data('upload_cohorts_csv_url'),
+ studioAdvancedSettingsUrl: cohortManagementElement.data('advanced-settings-url'),
+ studioGroupConfigurationsUrl: studioGroupConfigurationsUrl
+ }
+ });
+ cohorts.fetch().done(function() {
+ courseCohortSettings.fetch().done(function() {
+ cohortsView.render();
+ })
+ });
+ };
+ });
+}).call(this, define || RequireJS.define);
+
diff --git a/lms/static/js/groups/models/course_cohort_settings.js b/lms/static/js/groups/models/course_cohort_settings.js
new file mode 100644
index 0000000000..a87eeceec7
--- /dev/null
+++ b/lms/static/js/groups/models/course_cohort_settings.js
@@ -0,0 +1,16 @@
+var edx = edx || {};
+
+(function(Backbone) {
+ 'use strict';
+
+ edx.groups = edx.groups || {};
+
+ edx.groups.CourseCohortSettingsModel = Backbone.Model.extend({
+ idAttribute: 'id',
+ defaults: {
+ is_cohorted: false,
+ cohorted_discussions: [],
+ always_cohort_inline_discussions: true
+ }
+ });
+}).call(this, Backbone);
diff --git a/lms/static/js/groups/views/cohorts.js b/lms/static/js/groups/views/cohorts.js
index 4815eaa567..ba175097a9 100644
--- a/lms/static/js/groups/views/cohorts.js
+++ b/lms/static/js/groups/views/cohorts.js
@@ -1,7 +1,7 @@
var edx = edx || {};
(function($, _, Backbone, gettext, interpolate_text, CohortModel, CohortEditorView, CohortFormView,
- NotificationModel, NotificationView, FileUploaderView) {
+ CourseCohortSettingsNotificationView, NotificationModel, NotificationView, FileUploaderView) {
'use strict';
var hiddenClass = 'is-hidden',
@@ -12,6 +12,7 @@ var edx = edx || {};
edx.groups.CohortsView = Backbone.View.extend({
events : {
'change .cohort-select': 'onCohortSelected',
+ 'change .cohorts-state': 'onCohortsEnabledChanged',
'click .action-create': 'showAddCohortForm',
'click .cohort-management-add-form .action-save': 'saveAddCohortForm',
'click .cohort-management-add-form .action-cancel': 'cancelAddCohortForm',
@@ -26,19 +27,21 @@ var edx = edx || {};
this.selectorTemplate = _.template($('#cohort-selector-tpl').text());
this.context = options.context;
this.contentGroups = options.contentGroups;
+ this.cohortSettings = options.cohortSettings;
model.on('sync', this.onSync, this);
- // Update cohort counts when the user clicks back on the membership tab
+ // Update cohort counts when the user clicks back on the cohort management tab
// (for example, after uploading a csv file of cohort assignments and then
// checking results on data download tab).
- $(this.getSectionCss('membership')).click(function () {
+ $(this.getSectionCss('cohort_management')).click(function () {
model.fetch();
});
},
render: function() {
this.$el.html(this.template({
- cohorts: this.model.models
+ cohorts: this.model.models,
+ cohortsEnabled: this.cohortSettings.get('is_cohorted')
}));
this.onSync();
return this;
@@ -51,6 +54,13 @@ var edx = edx || {};
}));
},
+ renderCourseCohortSettingsNotificationView: function() {
+ var cohortStateMessageNotificationView = new CourseCohortSettingsNotificationView({
+ el: $('.cohort-state-message'),
+ cohortEnabled: this.getCohortsEnabled()});
+ cohortStateMessageNotificationView.render();
+ },
+
onSync: function(model, response, options) {
var selectedCohort = this.lastSelectedCohortId && this.model.get(this.lastSelectedCohortId),
hasCohorts = this.model.length > 0,
@@ -98,6 +108,28 @@ var edx = edx || {};
this.showCohortEditor(selectedCohort);
},
+ onCohortsEnabledChanged: function(event) {
+ event.preventDefault();
+ this.saveCohortSettings();
+ },
+
+ saveCohortSettings: function() {
+ var self = this,
+ cohortSettings,
+ fieldData = {is_cohorted: this.getCohortsEnabled()};
+ cohortSettings = this.cohortSettings;
+ cohortSettings.save(
+ fieldData, {wait: true}
+ ).done(function() {
+ self.render();
+ self.renderCourseCohortSettingsNotificationView();
+ });
+ },
+
+ getCohortsEnabled: function() {
+ return this.$('.cohorts-state').prop('checked');
+ },
+
showCohortEditor: function(cohort) {
this.removeNotification();
if (this.editor) {
@@ -242,4 +274,5 @@ var edx = edx || {};
}
});
}).call(this, $, _, Backbone, gettext, interpolate_text, edx.groups.CohortModel, edx.groups.CohortEditorView,
- edx.groups.CohortFormView, NotificationModel, NotificationView, FileUploaderView);
+ edx.groups.CohortFormView, edx.groups.CourseCohortSettingsNotificationView, NotificationModel, NotificationView,
+ FileUploaderView);
diff --git a/lms/static/js/groups/views/course_cohort_settings_notification.js b/lms/static/js/groups/views/course_cohort_settings_notification.js
new file mode 100644
index 0000000000..1708b1e532
--- /dev/null
+++ b/lms/static/js/groups/views/course_cohort_settings_notification.js
@@ -0,0 +1,35 @@
+var edx = edx || {};
+
+(function($, _, Backbone, gettext) {
+ 'use strict';
+
+ edx.groups = edx.groups || {};
+
+ edx.groups.CourseCohortSettingsNotificationView = Backbone.View.extend({
+ initialize: function(options) {
+ this.template = _.template($('#cohort-state-tpl').text());
+ this.cohortEnabled = options.cohortEnabled;
+ },
+
+ render: function() {
+ this.$el.html(this.template({}));
+ this.showCohortStateMessage();
+ return this;
+ },
+
+ showCohortStateMessage: function () {
+ var actionToggleMessage = this.$('.action-toggle-message');
+
+ // The following lines are necessary to re-trigger the CSS animation on span.action-toggle-message
+ actionToggleMessage.removeClass('is-fleeting');
+ actionToggleMessage.offset().width = actionToggleMessage.offset().width;
+ actionToggleMessage.addClass('is-fleeting');
+
+ if (this.cohortEnabled) {
+ actionToggleMessage.text(gettext('Cohorts Enabled'));
+ } else {
+ actionToggleMessage.text(gettext('Cohorts Disabled'));
+ }
+ }
+ });
+}).call(this, $, _, Backbone, gettext);
diff --git a/lms/static/js/instructor_dashboard/cohort_management.js b/lms/static/js/instructor_dashboard/cohort_management.js
new file mode 100644
index 0000000000..1d820ba2cc
--- /dev/null
+++ b/lms/static/js/instructor_dashboard/cohort_management.js
@@ -0,0 +1,29 @@
+(function() {
+ var CohortManagement;
+
+ CohortManagement = (function() {
+
+ function CohortManagement($section) {
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ }
+
+ CohortManagement.prototype.onClickTitle = function() {};
+
+ return CohortManagement;
+
+ })();
+
+ _.defaults(window, {
+ InstructorDashboard: {}
+ });
+
+ _.defaults(window.InstructorDashboard, {
+ sections: {}
+ });
+
+ _.defaults(window.InstructorDashboard.sections, {
+ CohortManagement: CohortManagement
+ });
+
+}).call(this);
diff --git a/lms/static/js/spec/groups/views/cohorts_spec.js b/lms/static/js/spec/groups/views/cohorts_spec.js
index 4aacf9caf0..3123bef275 100644
--- a/lms/static/js/spec/groups/views/cohorts_spec.js
+++ b/lms/static/js/spec/groups/views/cohorts_spec.js
@@ -1,15 +1,18 @@
define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpers/template_helpers',
- 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group'],
- function (Backbone, $, AjaxHelpers, TemplateHelpers, CohortsView, CohortCollection, ContentGroupModel) {
+ 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group',
+ 'js/groups/models/course_cohort_settings', 'js/groups/views/course_cohort_settings_notification'],
+ function (Backbone, $, AjaxHelpers, TemplateHelpers, CohortsView, CohortCollection, ContentGroupModel,
+ CourseCohortSettingsModel, CourseCohortSettingsNotificationView) {
'use strict';
describe("Cohorts View", function () {
var catLoversInitialCount = 123, dogLoversInitialCount = 456, unknownUserMessage,
- createMockCohort, createMockCohorts, createMockContentGroups, createCohortsView, cohortsView,
- requests, respondToRefresh, verifyMessage, verifyNoMessage, verifyDetailedMessage, verifyHeader,
- expectCohortAddRequest, getAddModal, selectContentGroup, clearContentGroup, saveFormAndExpectErrors,
- MOCK_COHORTED_USER_PARTITION_ID, MOCK_UPLOAD_COHORTS_CSV_URL, MOCK_STUDIO_ADVANCED_SETTINGS_URL,
- MOCK_STUDIO_GROUP_CONFIGURATIONS_URL, MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT;
+ createMockCohort, createMockCohorts, createMockContentGroups, createCohortSettings, createCohortsView,
+ cohortsView, requests, respondToRefresh, verifyMessage, verifyNoMessage, verifyDetailedMessage,
+ verifyHeader, expectCohortAddRequest, getAddModal, selectContentGroup, clearContentGroup,
+ saveFormAndExpectErrors, createMockCohortSettings, MOCK_COHORTED_USER_PARTITION_ID,
+ MOCK_UPLOAD_COHORTS_CSV_URL, MOCK_STUDIO_ADVANCED_SETTINGS_URL, MOCK_STUDIO_GROUP_CONFIGURATIONS_URL,
+ MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT;
MOCK_MANUAL_ASSIGNMENT = 'manual';
MOCK_RANDOM_ASSIGNMENT = 'random';
@@ -49,17 +52,35 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
];
};
+ createMockCohortSettings = function (isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions) {
+ return {
+ id: 0,
+ is_cohorted: isCohorted || false,
+ cohorted_discussions: cohortedDiscussions || [],
+ always_cohort_inline_discussions: alwaysCohortInlineDiscussions || true
+ };
+ };
+
+ createCohortSettings = function (isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions) {
+ return new CourseCohortSettingsModel(
+ createMockCohortSettings(isCohorted, cohortedDiscussions, alwaysCohortInlineDiscussions)
+ );
+ };
+
createCohortsView = function (test, options) {
- var cohortsJson, cohorts, contentGroups;
+ var cohortsJson, cohorts, contentGroups, cohortSettings;
options = options || {};
cohortsJson = options.cohorts ? {cohorts: options.cohorts} : createMockCohorts();
cohorts = new CohortCollection(cohortsJson, {parse: true});
contentGroups = options.contentGroups || createMockContentGroups();
+ cohortSettings = options.cohortSettings || createCohortSettings(true);
+ cohortSettings.url = '/mock_service/cohorts/settings';
cohorts.url = '/mock_service/cohorts';
requests = AjaxHelpers.requests(test);
cohortsView = new CohortsView({
model: cohorts,
contentGroups: contentGroups,
+ cohortSettings: cohortSettings,
context: {
uploadCohortsCsvUrl: MOCK_UPLOAD_COHORTS_CSV_URL,
studioAdvancedSettingsUrl: MOCK_STUDIO_ADVANCED_SETTINGS_URL,
@@ -177,13 +198,14 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
};
beforeEach(function () {
- setFixtures('
');
+ setFixtures('');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohorts');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-form');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-selector');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-editor');
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/cohort-state');
TemplateHelpers.installTemplate('templates/file-upload');
});
@@ -202,7 +224,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
it("syncs data when membership tab is clicked", function() {
createCohortsView(this, {selectCohort: 1});
verifyHeader(1, 'Cat Lovers', catLoversInitialCount);
- $(cohortsView.getSectionCss("membership")).click();
+ $(cohortsView.getSectionCss("cohort_management")).click();
AjaxHelpers.expectRequest(requests, 'GET', '/mock_service/cohorts');
respondToRefresh(1001, 2);
verifyHeader(1, 'Cat Lovers', 1001);
@@ -255,6 +277,54 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
});
});
+ describe("Course Cohort Settings", function () {
+ it('enable/disable working correctly', function () {
+ createCohortsView(this, {cohortSettings: createCohortSettings(false)});
+
+ expect(cohortsView.$('.cohorts-state').prop('checked')).toBeFalsy();
+
+ cohortsView.$('.cohorts-state').prop('checked', true).change();
+ AjaxHelpers.expectJsonRequest(
+ requests, 'PUT', '/mock_service/cohorts/settings',
+ createMockCohortSettings(true, [], true)
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ createMockCohortSettings(true)
+ );
+ expect(cohortsView.$('.cohorts-state').prop('checked')).toBeTruthy();
+
+ cohortsView.$('.cohorts-state').prop('checked', false).change();
+ AjaxHelpers.expectJsonRequest(
+ requests, 'PUT', '/mock_service/cohorts/settings',
+ createMockCohortSettings(false, [], true)
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ createMockCohortSettings(false)
+ );
+ expect(cohortsView.$('.cohorts-state').prop('checked')).toBeFalsy();
+ });
+
+
+ it('Course Cohort Settings Notification View renders correctly', function () {
+ var createCourseCohortSettingsNotificationView = function (is_cohorted) {
+ var notificationView = new CourseCohortSettingsNotificationView({
+ el: $('.cohort-state-message'),
+ cohortEnabled: is_cohorted});
+ notificationView.render();
+ return notificationView;
+ };
+
+ var notificationView = createCourseCohortSettingsNotificationView(true);
+ expect(notificationView.$('.action-toggle-message').text().trim()).toBe('Cohorts Enabled');
+
+ notificationView = createCourseCohortSettingsNotificationView(false);
+ expect(notificationView.$('.action-toggle-message').text().trim()).toBe('Cohorts Disabled');
+ });
+
+ });
+
describe("Cohort Group Header", function () {
it("renders header correctly", function () {
var cohortName = 'Transformers',
@@ -867,7 +937,7 @@ define(['backbone', 'jquery', 'js/common_helpers/ajax_helpers', 'js/common_helpe
// We have a single random cohort so we should not be allowed to change it assignment type
expect(cohortsView.$('.cohort-management-assignment-type-settings')).toHaveClass('is-disabled');
- expect(cohortsView.$('.copy-error').text()).toContain("There must be one cohort to which students can be randomly assigned.");
+ expect(cohortsView.$('.copy-error').text()).toContain("There must be one cohort to which students can automatically be assigned.");
});
it("cancel settings works", function() {
diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js
index caf1f418a8..0c2b9fcbca 100644
--- a/lms/static/js/spec/main.js
+++ b/lms/static/js/spec/main.js
@@ -67,6 +67,8 @@
'js/views/notification': 'js/views/notification',
'js/groups/models/cohort': 'js/groups/models/cohort',
'js/groups/models/content_group': 'js/groups/models/content_group',
+ 'js/groups/models/course_cohort_settings': 'js/groups/models/course_cohort_settings',
+ 'js/groups/views/course_cohort_settings_notification': 'js/groups/views/course_cohort_settings_notification',
'js/groups/collections/cohort': 'js/groups/collections/cohort',
'js/groups/views/cohort_editor': 'js/groups/views/cohort_editor',
'js/groups/views/cohort_form': 'js/groups/views/cohort_form',
@@ -294,6 +296,14 @@
exports: 'edx.groups.ContentGroupModel',
deps: ['backbone']
},
+ 'js/groups/models/course_cohort_settings': {
+ exports: 'edx.groups.CourseCohortSettingsModel',
+ deps: ['backbone']
+ },
+ 'js/groups/views/course_cohort_settings_notification': {
+ exports: 'edx.groups.CourseCohortSettingsNotificationView',
+ deps: ['backbone']
+ },
'js/groups/collections/cohort': {
exports: 'edx.groups.CohortCollection',
deps: ['backbone', 'js/groups/models/cohort']
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index 98cf31d895..5ea18fe818 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -435,6 +435,248 @@
}
}
+ .batch-enrollment, .batch-beta-testers {
+ textarea {
+ margin-top: 0.2em;
+ height: auto;
+ width: 90%;
+ }
+
+ input {
+ margin-right: ($baseline/4);
+ }
+
+ .request-res-section {
+ margin-top: 1.5em;
+
+ h3 {
+ color: #646464;
+ }
+
+ ul {
+ margin: 0;
+ margin-top: 0.5em;
+ padding: 0;
+ list-style-type: none;
+ line-height: 1.5em;
+ }
+ }
+ }
+ // Auto Enroll Csv Section
+ .auto_enroll_csv {
+ .results {
+
+ }
+ .enrollment_signup_button {
+ @include margin-right($baseline/4);
+ }
+ // Custom File upload
+ .customBrowseBtn {
+ margin: ($baseline/2) 0;
+ display: inline-block;
+ .file-browse {
+ position:relative;
+ overflow:hidden;
+ display: inline;
+ @include margin-left(-5px);
+ span.browse{
+ @include button(simple, $blue);
+ @include margin-right($baseline);
+ padding: 6px ($baseline/2);
+ font-size: 12px;
+ border-radius: 0 3px 3px 0;
+ }
+ input.file_field {
+ position:absolute;
+ @include right(0);
+ top:0;
+ margin:0;
+ padding:0;
+ cursor:pointer;
+ opacity:0;
+ filter:alpha(opacity=0);
+ }
+ }
+ & > span, & input[disabled]{
+ vertical-align: middle;
+ }
+ input[disabled] {
+ @include border-radius(4px 0 0 4px);
+ @include padding(6px 6px 5px);
+ border: 1px solid $lightGrey1;
+ cursor: not-allowed;
+ }
+ }
+ }
+
+ .enroll-option {
+ margin: ($baseline/2) 0;
+ position: relative;
+
+ label {
+ border-bottom: 1px dotted $base-font-color;
+ }
+
+ .hint {
+ @extend %t-copy-sub2;
+ display: none;
+ position: absolute;
+ top: 15px;
+ @include left($baseline*10);
+ padding: ($baseline/2);
+ width: 50%;
+ background-color: $light-gray;
+ box-shadow: 2px 2px 3px $shadow;
+
+ .hint-caret {
+ display: block;
+ position: absolute;
+ top: 0;
+ @include left(-15px);
+ @include border-right(8px solid $light-gray);
+ @include border-left(8px solid transparent);
+ border-top: 8px solid $light-gray;
+ border-bottom: 8px solid transparent;
+ }
+ }
+ }
+
+ label[for="auto-enroll"]:hover + .auto-enroll-hint {
+ display: block;
+ }
+
+ label[for="auto-enroll-beta"]:hover + .auto-enroll-beta-hint {
+ width: 30%;
+ display: block;
+ }
+
+ label[for="email-students"]:hover + .email-students-hint {
+ display: block;
+ }
+
+ label[for="email-students-beta"]:hover + .email-students-beta-hint {
+ width: 30%;
+ display: block;
+ }
+
+ .enroll-actions {
+ margin-top: $baseline;
+ }
+
+ .member-lists-management {
+
+ .wrapper-member-select {
+ padding: ($baseline/2);
+ background-color: $light-gray;
+ }
+
+ .member-lists-selector {
+ display: block;
+ margin: ($baseline/4) 0;
+ padding: ($baseline/4);
+ }
+
+ .auth-list-container {
+ display: none;
+ margin-bottom: ($baseline*1.5);
+
+ &.active {
+ display: block;
+ }
+
+ .member-list-widget {
+
+ .header {
+ @include box-sizing(border-box);
+ @include border-top-radius(3);
+ position: relative;
+ padding: ($baseline/2);
+ background-color: #efefef;
+ border: 1px solid $light-gray;
+ display: none; // hiding to prefer dropdown as header
+ }
+
+ .title {
+ @include font-size(16);
+ }
+
+ .label,
+ .form-label {
+ @extend %t-copy-sub1;
+ color: $lighter-base-font-color;
+ }
+
+ .info {
+ @include box-sizing(border-box);
+ padding: ($baseline/2);
+ border: 1px solid $light-gray;
+ color: $lighter-base-font-color;
+ line-height: 1.3em;
+ font-size: .85em;
+ }
+
+ .member-list {
+ @include box-sizing(border-box);
+
+ table {
+ width: 100%;
+ }
+
+ thead {
+ background-color: $light-gray;
+ }
+
+ tr {
+ border-bottom: 1px solid $light-gray;
+ }
+
+ td {
+ @extend %t-copy-sub1;
+ vertical-align: middle;
+ padding: ($baseline/2) ($baseline/4);
+ @include border-left(1px solid $light-gray);
+ @include border-right(1px solid $light-gray);
+ word-wrap: break-word;
+ }
+ }
+
+ .bottom-bar {
+ @include box-sizing(border-box);
+ @include border-bottom-radius(3);
+ position: relative;
+ padding: ($baseline/2);
+ margin-top: -1px;
+ border: 1px solid $light-gray;
+ background-color: #efefef;
+ box-shadow: inset #bbb 0px 1px 1px 0px;
+ }
+
+ // .add-field
+
+ input[type="button"].add {
+ @include idashbutton($blue);
+ position: absolute;
+ @include right($baseline);
+ }
+ }
+
+ .revoke {
+ color: $lighter-base-font-color;
+ cursor: pointer;
+
+ &:hover, &:focus {
+ color: $alert-color;
+ }
+ }
+ }
+ }
+}
+
+
+// view - cohort management
+// --------------------
+.instructor-dashboard-wrapper-2 section.idash-section#cohort_management {
+
// cohort management
%cohort-management-form {
@@ -733,245 +975,6 @@
}
}
-
-
- .batch-enrollment, .batch-beta-testers {
- textarea {
- margin-top: 0.2em;
- height: auto;
- width: 90%;
- }
-
- input {
- margin-right: ($baseline/4);
- }
-
- .request-res-section {
- margin-top: 1.5em;
-
- h3 {
- color: #646464;
- }
-
- ul {
- margin: 0;
- margin-top: 0.5em;
- padding: 0;
- list-style-type: none;
- line-height: 1.5em;
- }
- }
- }
- // Auto Enroll Csv Section
- .auto_enroll_csv {
- .results {
-
- }
- .enrollment_signup_button {
- @include margin-right($baseline/4);
- }
- // Custom File upload
- .customBrowseBtn {
- margin: ($baseline/2) 0;
- display: inline-block;
- .file-browse {
- position:relative;
- overflow:hidden;
- display: inline;
- @include margin-left(-5px);
- span.browse{
- @include button(simple, $blue);
- @include margin-right($baseline);
- padding: 6px ($baseline/2);
- font-size: 12px;
- border-radius: 0 3px 3px 0;
- }
- input.file_field {
- position:absolute;
- @include right(0);
- top:0;
- margin:0;
- padding:0;
- cursor:pointer;
- opacity:0;
- filter:alpha(opacity=0);
- }
- }
- & > span, & input[disabled]{
- vertical-align: middle;
- }
- input[disabled] {
- @include border-radius(4px 0 0 4px);
- @include padding(6px 6px 5px);
- border: 1px solid $lightGrey1;
- cursor: not-allowed;
- }
- }
- }
-
- .enroll-option {
- margin: ($baseline/2) 0;
- position: relative;
-
- label {
- border-bottom: 1px dotted $base-font-color;
- }
-
- .hint {
- @extend %t-copy-sub2;
- display: none;
- position: absolute;
- top: 15px;
- @include left($baseline*10);
- padding: ($baseline/2);
- width: 50%;
- background-color: $light-gray;
- box-shadow: 2px 2px 3px $shadow;
-
- .hint-caret {
- display: block;
- position: absolute;
- top: 0;
- @include left(-15px);
- @include border-right(8px solid $light-gray);
- @include border-left(8px solid transparent);
- border-top: 8px solid $light-gray;
- border-bottom: 8px solid transparent;
- }
- }
- }
-
- label[for="auto-enroll"]:hover + .auto-enroll-hint {
- display: block;
- }
-
- label[for="auto-enroll-beta"]:hover + .auto-enroll-beta-hint {
- width: 30%;
- display: block;
- }
-
-
- label[for="email-students"]:hover + .email-students-hint {
- display: block;
- }
-
- label[for="email-students-beta"]:hover + .email-students-beta-hint {
- width: 30%;
- display: block;
- }
-
- .enroll-actions {
- margin-top: $baseline;
- }
-
- .member-lists-management {
-
- .wrapper-member-select {
- padding: ($baseline/2);
- background-color: $light-gray;
- }
-
- .member-lists-selector {
- display: block;
- margin: ($baseline/4) 0;
- padding: ($baseline/4);
- }
-
- .auth-list-container {
- display: none;
- margin-bottom: ($baseline*1.5);
-
- &.active {
- display: block;
- }
-
- .member-list-widget {
-
- .header {
- @include box-sizing(border-box);
- @include border-top-radius(3);
- position: relative;
- padding: ($baseline/2);
- background-color: #efefef;
- border: 1px solid $light-gray;
- display: none; // hiding to prefer dropdown as header
- }
-
- .title {
- @include font-size(16);
- }
-
- .label,
- .form-label {
- @extend %t-copy-sub1;
- color: $lighter-base-font-color;
- }
-
- .info {
- @include box-sizing(border-box);
- padding: ($baseline/2);
- border: 1px solid $light-gray;
- color: $lighter-base-font-color;
- line-height: 1.3em;
- font-size: .85em;
- }
-
- .member-list {
- @include box-sizing(border-box);
-
- table {
- width: 100%;
- }
-
- thead {
- background-color: $light-gray;
- }
-
- tr {
- border-bottom: 1px solid $light-gray;
- }
-
- td {
- @extend %t-copy-sub1;
- vertical-align: middle;
- padding: ($baseline/2) ($baseline/4);
- @include border-left(1px solid $light-gray);
- @include border-right(1px solid $light-gray);
- word-wrap: break-word;
- }
- }
-
- .bottom-bar {
- @include box-sizing(border-box);
- @include border-bottom-radius(3);
- position: relative;
- padding: ($baseline/2);
- margin-top: -1px;
- border: 1px solid $light-gray;
- background-color: #efefef;
- box-shadow: inset #bbb 0px 1px 1px 0px;
- }
-
- // .add-field
-
- input[type="button"].add {
- @include idashbutton($blue);
- position: absolute;
- @include right($baseline);
- }
- }
-
- .revoke {
- color: $lighter-base-font-color;
- cursor: pointer;
-
- &:hover, &:focus {
- color: $alert-color;
- }
- }
- }
- }
-
.has-other-input-text { // Given to groups which have an 'other' input that appears when needed
display: inline-block;
@@ -1118,6 +1121,7 @@
}
}
+
// view - student admin
// --------------------
.instructor-dashboard-wrapper-2 section.idash-section#student_admin > {
diff --git a/lms/templates/instructor/instructor_dashboard_2/cohort-form.underscore b/lms/templates/instructor/instructor_dashboard_2/cohort-form.underscore
index 20049ecde5..8d7df60bf0 100644
--- a/lms/templates/instructor/instructor_dashboard_2/cohort-form.underscore
+++ b/lms/templates/instructor/instructor_dashboard_2/cohort-form.underscore
@@ -38,7 +38,7 @@
<%- gettext('Students in this cohort are:') %>