Commit f0f06450 by Christina Roberts

Merge pull request #12278 from edx/christina/cohort-messages

Add messaging when verified track content MVP is enabled.
parents b1313671 0f8b2452
......@@ -477,6 +477,9 @@ def _section_cohort_management(course, access):
'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)}),
'discussion_topics_url': reverse('cohort_discussion_topics', kwargs={'course_key_string': unicode(course_key)}),
'verified_track_cohorting_url': reverse(
'verified_track_cohorting', kwargs={'course_key_string': unicode(course_key)}
),
}
return section_data
......
"""
Tests for verified track content views.
"""
import json
from nose.plugins.attrib import attr
from unittest import skipUnless
from django.http import Http404
from django.test.client import RequestFactory
from django.conf import settings
from student.tests.factories import UserFactory, AdminFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from verified_track_content.models import VerifiedTrackCohortedCourse
from verified_track_content.views import cohorting_settings
@attr('shard_2')
@skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Tests only valid in LMS')
class CohortingSettingsTestCase(SharedModuleStoreTestCase):
"""
Tests the `cohort_discussion_topics` view.
"""
@classmethod
def setUpClass(cls):
super(CohortingSettingsTestCase, cls).setUpClass()
cls.course = CourseFactory.create()
def test_non_staff(self):
"""
Verify that we cannot access cohorting_settings if we're a non-staff user.
"""
request = RequestFactory().get("dummy_url")
request.user = UserFactory()
with self.assertRaises(Http404):
cohorting_settings(request, unicode(self.course.id))
def test_cohorting_settings_enabled(self):
"""
Verify that cohorting_settings is working for HTTP GET when verified track cohorting is enabled.
"""
config = VerifiedTrackCohortedCourse.objects.create(
course_key=unicode(self.course.id), enabled=True, verified_cohort_name="Verified Learners"
)
config.save()
expected_response = {
"enabled": True,
"verified_cohort_name": "Verified Learners"
}
self._verify_cohort_settings_response(expected_response)
def test_cohorting_settings_disabled(self):
"""
Verify that cohorting_settings is working for HTTP GET when verified track cohorting is disabled.
"""
expected_response = {
"enabled": False
}
self._verify_cohort_settings_response(expected_response)
def _verify_cohort_settings_response(self, expected_response):
request = RequestFactory().get("dummy_url")
request.user = AdminFactory()
response = cohorting_settings(request, unicode(self.course.id))
self.assertEqual(200, response.status_code)
self.assertEqual(expected_response, json.loads(response.content))
"""
View methods for verified track content.
"""
from util.json_request import expect_json, JsonResponse
from django.contrib.auth.decorators import login_required
from opaque_keys.edx.keys import CourseKey
from courseware.courses import get_course_with_access
from verified_track_content.models import VerifiedTrackCohortedCourse
@expect_json
@login_required
def cohorting_settings(request, course_key_string):
"""
The handler for verified track cohorting requests.
This will raise 404 if user is not staff.
Returns a JSON representation of whether or not the course has verified track cohorting enabled.
The "verified_cohort_name" field will only be present if "enabled" is True.
Example:
>>> example = {
>>> "enabled": True,
>>> "verified_cohort_name" : "Micromasters"
>>> }
"""
course_key = CourseKey.from_string(course_key_string)
get_course_with_access(request.user, 'staff', course_key)
settings = {}
verified_track_cohort_enabled = VerifiedTrackCohortedCourse.is_verified_track_cohort_enabled(course_key)
settings['enabled'] = verified_track_cohort_enabled
if verified_track_cohort_enabled:
settings['verified_cohort_name'] = VerifiedTrackCohortedCourse.verified_cohort_name_for_course(course_key)
return JsonResponse(settings)
......@@ -8,7 +8,7 @@
name: '',
user_count: 0,
/**
* Indicates how students are added to the cohort. Will be "none" (signifying manual assignment) or
* Indicates how students are added to the cohort. Will be "manual" (signifying manual assignment) or
* "random" (indicating students are randomly assigned).
*/
assignment_type: '',
......
;(function (define) {
'use strict';
define(['backbone'], function(Backbone) {
var VerifiedTrackSettingsModel = Backbone.Model.extend({
defaults: {
enabled: false,
verified_cohort_name: ''
}
});
return VerifiedTrackSettingsModel;
});
}).call(this, define || RequireJS.define);
;(function (define) {
'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/models/cohort',
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/models/cohort',
'js/groups/models/verified_track_settings',
'js/groups/views/cohort_editor', 'js/groups/views/cohort_form',
'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',
'edx-ui-toolkit/js/utils/html-utils',
'js/views/file_uploader', 'js/models/notification', 'js/views/notification', 'string_utils'],
function($, _, Backbone, gettext, CohortModel, CohortEditorView, CohortFormView,
CourseCohortSettingsNotificationView, InlineDiscussionsView, CourseWideDiscussionsView, HtmlUtils) {
function($, _, Backbone, gettext, CohortModel, VerifiedTrackSettingsModel, CohortEditorView, CohortFormView,
CourseCohortSettingsNotificationView, InlineDiscussionsView, CourseWideDiscussionsView,
VerifiedTrackSettingsNotificationView, HtmlUtils) {
var hiddenClass = 'is-hidden',
disabledClass = 'is-disabled';
disabledClass = 'is-disabled',
enableCohortsSelector = '.cohorts-state';
var CohortsView = Backbone.View.extend({
......@@ -49,6 +53,19 @@
cohortsEnabled: this.cohortSettings.get('is_cohorted')
}));
this.onSync();
// Don't create this view until the first render is called, as at that point the
// various other models whose state is required to properly view the notification
// will have completed their fetch operations.
if (!this.verifiedTrackSettingsNotificationView) {
var verifiedTrackSettingsModel = new VerifiedTrackSettingsModel();
verifiedTrackSettingsModel.url = this.context.verifiedTrackCohortingUrl;
verifiedTrackSettingsModel.fetch({
success: _.bind(this.renderVerifiedTrackSettingsNotificationView, this)
});
this.verifiedTrackSettingsNotificationView = new VerifiedTrackSettingsNotificationView({
model: verifiedTrackSettingsModel
});
}
return this;
},
......@@ -67,6 +84,14 @@
cohortStateMessageNotificationView.render();
},
renderVerifiedTrackSettingsNotificationView: function() {
if (this.verifiedTrackSettingsNotificationView) {
this.verifiedTrackSettingsNotificationView.validateSettings(
this.getCohortsEnabled(), this.model.models, this.$(enableCohortsSelector)
);
}
},
onSync: function(model, response, options) {
var selectedCohort = this.lastSelectedCohortId && this.model.get(this.lastSelectedCohortId),
hasCohorts = this.model.length > 0,
......@@ -100,6 +125,7 @@
actionIconClass: 'fa-plus'
});
}
this.renderVerifiedTrackSettingsNotificationView();
},
getSelectedCohort: function() {
......@@ -139,7 +165,7 @@
},
getCohortsEnabled: function() {
return this.$('.cohorts-state').prop('checked');
return this.$(enableCohortsSelector).prop('checked');
},
showCohortEditor: function(cohort) {
......
......@@ -31,6 +31,7 @@
context: {
discussionTopicsSettingsModel: discussionTopicsSettings,
uploadCohortsCsvUrl: cohortManagementElement.data('upload_cohorts_csv_url'),
verifiedTrackCohortingUrl: cohortManagementElement.data('verified_track_cohorting_url'),
studioGroupConfigurationsUrl: studioGroupConfigurationsUrl
}
});
......
;(function (define) {
'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'edx-ui-toolkit/js/utils/string-utils',
'js/models/notification', 'js/views/notification'],
function ($, _, Backbone, gettext, StringUtils) {
/*global NotificationModel, NotificationView */
var VerifiedTrackSettingsNotificationView = Backbone.View.extend({
render: function () {
// All rendering is done in validateSettings, which must be called with some additional information.
return this;
},
validateSettings: function (isCohorted, cohortCollection, enableCohortsCheckbox) {
if (this.model.get('enabled')) {
var verifiedCohortName = this.model.get('verified_cohort_name');
if (isCohorted) {
var verifiedCohortExists = false;
$.each(cohortCollection, function (_, cohort) {
if (cohort.get('assignment_type') === 'manual' &&
cohort.get('name') === verifiedCohortName) {
verifiedCohortExists = true;
cohort.disableEditingName = true;
}
else {
cohort.disableEditingName = false;
}
}
);
if (verifiedCohortExists) {
this.showNotification({
type: 'confirmation',
title: StringUtils.interpolate(
gettext("This course uses automatic cohorting for verified track learners. You cannot disable cohorts, and you cannot rename the manual cohort named '{verifiedCohortName}'. To change the configuration for verified track cohorts, contact your edX partner manager."), // jshint ignore:line
{verifiedCohortName: verifiedCohortName}
)
});
}
else {
this.showNotification({
type: 'error',
title: StringUtils.interpolate(
gettext("This course has automatic cohorting enabled for verified track learners, but the required cohort does not exist. You must create a manually-assigned cohort named '{verifiedCohortName}' for the feature to work."), // jshint ignore:line
{verifiedCohortName: verifiedCohortName}
)
});
}
enableCohortsCheckbox.prop('disabled', true);
}
else {
this.showNotification({
type: 'error',
title: gettext('This course has automatic cohorting enabled for verified track learners, but cohorts are disabled. You must enable cohorts for the feature to work.') // jshint ignore:line
});
enableCohortsCheckbox.prop('disabled', false);
}
}
},
showNotification: function (options) {
if (this.notification) {
this.notification.remove();
}
this.notification = new NotificationView({
model: new NotificationModel(options)
});
// It's ugly to reach outside to the cohort-management div, but we want this notification
// message to always be visible (as opposed to using the transient notification area defined
// by cohorts.js).
$('.cohort-management').before(this.notification.$el);
this.notification.render();
}
});
return VerifiedTrackSettingsNotificationView;
});
}).call(this, define || RequireJS.define);
......@@ -201,7 +201,7 @@ $shadow-d2: rgba($black, 0.6) !default;
// system feedback-based colors
$error-color: rgb(253, 87, 87) !default;
$warning-color: rgb(181,42,103) !default;
$confirm-color: rgb(0, 136, 1) !default;
$confirm-color: rgb(0, 132, 1) !default;
$active-color: $blue !default;
$highlight-color: rgb(255,255,0) !default;
$alert-color: rgb(212, 64, 64) !default;
......
......@@ -84,11 +84,11 @@
// TYPE: confirm
.msg-error {
border-top: 2px solid $error-color;
background: tint($error-color,95%);
border-top: 2px solid $red1;
background: tint($red1, 90%);
.copy {
color: $error-color;
color: $red1;
}
}
......@@ -247,11 +247,11 @@
// type - error
.message-error {
border-top: 2px solid $error-color;
background: tint($error-color,95%);
border-top: 2px solid $red1;
background: tint($red1, 90%);
.message-title {
color: $error-color;
color: $red1;
}
.message-copy {
......@@ -262,7 +262,7 @@
// type - confirmation
.message-confirmation {
border-top: 2px solid $confirm-color;
background: tint($confirm-color,95%);
background: tint($confirm-color, 95%);
.message-title {
color: $confirm-color;
......@@ -671,6 +671,12 @@
// needed to reset poor input styling
input[type="text"] {
height: auto;
&.readonly {
background-color: transparent;
padding: 0;
border: none;
box-shadow: none;
}
}
.input {
......
......@@ -25,8 +25,13 @@
<%- gettext('Cohort Name') %> *
<span class="sr"><%- gettext('(Required Field)')%></span>
</label>
<input name="cohort-name" value="<%- cohort_name_value %>" class="input cohort-name" id="cohort-name"
placeholder="<%- placeholder_value %>" required="required" type="text">
<input name="cohort-name" value="<%- cohort_name_value %>" class="input cohort-name
<% if (cohort.disableEditingName) { %>
readonly" readonly
<% } else { %>
"
<% } %>
id="cohort-name" placeholder="<%- placeholder_value %>" required="required" type="text">
</div>
<hr class="divider divider-lv1">
<% if (isDefaultCohort) { %>
......
<%page expression_filter="h"/>
<%page expression_filter="h" args="section_data"/>
<%page args="section_data"/>
<%namespace name='static' file='../../static_content.html'/>
<%!
from django.utils.translation import ugettext as _
......@@ -15,6 +14,7 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_
data-upload_cohorts_csv_url="${section_data['upload_cohorts_csv_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']}"
>
</div>
......
......@@ -611,7 +611,13 @@ urlpatterns += (
'openedx.core.djangoapps.course_groups.views.cohort_discussion_topics',
name='cohort_discussion_topics',
),
url(
r'^courses/{}/verified_track_content/settings'.format(
settings.COURSE_KEY_PATTERN,
),
'verified_track_content.views.cohorting_settings',
name='verified_track_cohorting',
),
url(
r'^courses/{}/notes$'.format(
settings.COURSE_ID_PATTERN,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment