Merge pull request #752 from edx/peter-fogg/unlimited-graceperiod
Allow grace periods of > 24 hours.
This commit is contained in:
@@ -99,3 +99,21 @@ Feature: Course Grading
|
||||
And I have populated the course
|
||||
And I am viewing the grading settings
|
||||
Then I cannot edit the "Fail" grade range
|
||||
|
||||
Scenario: User can set a grace period greater than one day
|
||||
Given I have opened a new course in Studio
|
||||
And I have populated the course
|
||||
And I am viewing the grading settings
|
||||
When I change the grace period to "48:00"
|
||||
And I press the "Save" notification button
|
||||
And I reload the page
|
||||
Then I see the grace period is "48:00"
|
||||
|
||||
Scenario: Grace periods of more than 59 minutes are wrapped to the correct time
|
||||
Given I have opened a new course in Studio
|
||||
And I have populated the course
|
||||
And I am viewing the grading settings
|
||||
When I change the grace period to "01:99"
|
||||
And I press the "Save" notification button
|
||||
And I reload the page
|
||||
Then I see the grace period is "02:39"
|
||||
|
||||
@@ -143,6 +143,21 @@ def cannot_edit_fail(_step):
|
||||
except InvalidElementStateException:
|
||||
pass # We should get this exception on failing to edit the element
|
||||
|
||||
|
||||
@step(u'I change the grace period to "(.*)"$')
|
||||
def i_change_grace_period(_step, grace_period):
|
||||
grace_period_css = '#course-grading-graceperiod'
|
||||
ele = world.css_find(grace_period_css).first
|
||||
ele.value = grace_period
|
||||
|
||||
|
||||
@step(u'I see the grace period is "(.*)"$')
|
||||
def the_grace_period_is(_step, grace_period):
|
||||
grace_period_css = '#course-grading-graceperiod'
|
||||
ele = world.css_find(grace_period_css).first
|
||||
assert ele.value == grace_period
|
||||
|
||||
|
||||
def get_type_index(name):
|
||||
name_id = '#course-grading-assignment-name'
|
||||
all_types = world.css_find(name_id)
|
||||
|
||||
@@ -246,7 +246,8 @@ PIPELINE_JS = {
|
||||
'js/models/metadata_model.js', 'js/views/metadata_editor_view.js',
|
||||
'js/models/uploads.js', 'js/views/uploads.js',
|
||||
'js/models/textbook.js', 'js/views/textbook.js',
|
||||
'js/views/assets.js', 'js/utility.js'],
|
||||
'js/views/assets.js', 'js/utility.js',
|
||||
'js/models/settings/course_grading_policy.js'],
|
||||
'output_filename': 'js/cms-application.js',
|
||||
'test_order': 0
|
||||
},
|
||||
|
||||
24
cms/static/coffee/spec/models/settings_grading_spec.coffee
Normal file
24
cms/static/coffee/spec/models/settings_grading_spec.coffee
Normal file
@@ -0,0 +1,24 @@
|
||||
describe "CMS.Models.Settings.CourseGradingPolicy", ->
|
||||
beforeEach ->
|
||||
@model = new CMS.Models.Settings.CourseGradingPolicy()
|
||||
|
||||
describe "parse", ->
|
||||
it "sets a null grace period to 00:00", ->
|
||||
attrs = @model.parse(grace_period: null)
|
||||
expect(attrs.grace_period).toEqual(
|
||||
hours: 0,
|
||||
minutes: 0
|
||||
)
|
||||
|
||||
describe "parseGracePeriod", ->
|
||||
it "parses a time in HH:MM format", ->
|
||||
time = @model.parseGracePeriod("07:19")
|
||||
expect(time).toEqual(
|
||||
hours: 7,
|
||||
minutes: 19
|
||||
)
|
||||
|
||||
it "returns null on an incorrectly formatted string", ->
|
||||
expect(@model.parseGracePeriod("asdf")).toBe(null)
|
||||
expect(@model.parseGracePeriod("7:19")).toBe(null)
|
||||
expect(@model.parseGracePeriod("1000:00")).toBe(null)
|
||||
@@ -24,6 +24,14 @@ CMS.Models.Settings.CourseGradingPolicy = Backbone.Model.extend({
|
||||
}
|
||||
attributes.graders = graderCollection;
|
||||
}
|
||||
// If grace period is unset or equal to 00:00 on the server,
|
||||
// it's received as null
|
||||
if (attributes['grace_period'] === null) {
|
||||
attributes.grace_period = {
|
||||
hours: 0,
|
||||
minutes: 0
|
||||
}
|
||||
}
|
||||
return attributes;
|
||||
},
|
||||
url : function() {
|
||||
@@ -44,8 +52,25 @@ CMS.Models.Settings.CourseGradingPolicy = Backbone.Model.extend({
|
||||
|
||||
return newDate;
|
||||
},
|
||||
dateToGracePeriod : function(date) {
|
||||
return {hours : date.getHours(), minutes : date.getMinutes(), seconds : date.getSeconds() };
|
||||
parseGracePeriod : function(grace_period) {
|
||||
// Enforce hours:minutes format
|
||||
if(!/^\d{2,3}:\d{2}$/.test(grace_period)) {
|
||||
return null;
|
||||
}
|
||||
var pieces = grace_period.split(/:/);
|
||||
return {
|
||||
hours: parseInt(pieces[0], 10),
|
||||
minutes: parseInt(pieces[1], 10)
|
||||
}
|
||||
},
|
||||
validate : function(attrs) {
|
||||
if(_.has(attrs, 'grace_period')) {
|
||||
if(attrs['grace_period'] === null) {
|
||||
return {
|
||||
'grace_period': gettext('Grace period must be specified in HH:MM format.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
|
||||
this.setupCutoffs();
|
||||
|
||||
// Instrument grace period
|
||||
this.$el.find('#course-grading-graceperiod').timepicker();
|
||||
|
||||
// instantiates an editor template for each update in the collection
|
||||
// Because this calls render, put it after everything which render may depend upon to prevent race condition.
|
||||
window.templateLoader.loadRemoteTemplate("course_grade_policy",
|
||||
@@ -51,6 +48,10 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
// prevent bootstrap race condition by event dispatch
|
||||
if (!this.template) return;
|
||||
|
||||
this.clearValidationErrors();
|
||||
|
||||
this.renderGracePeriod();
|
||||
|
||||
// Create and render the grading type subs
|
||||
var self = this;
|
||||
var gradelist = this.$el.find('.course-grading-assignment-list');
|
||||
@@ -87,13 +88,6 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
// render the grade cutoffs
|
||||
this.renderCutoffBar();
|
||||
|
||||
var graceEle = this.$el.find('#course-grading-graceperiod');
|
||||
graceEle.timepicker({'timeFormat' : 'H:i'}); // init doesn't take setTime
|
||||
if (this.model.has('grace_period')) graceEle.timepicker('setTime', this.model.gracePeriodToDate());
|
||||
// remove any existing listeners to keep them from piling on b/c render gets called frequently
|
||||
graceEle.off('change', this.setGracePeriod);
|
||||
graceEle.on('change', this, this.setGracePeriod);
|
||||
|
||||
return this;
|
||||
},
|
||||
addAssignmentType : function(e) {
|
||||
@@ -103,17 +97,26 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
fieldToSelectorMap : {
|
||||
'grace_period' : 'course-grading-graceperiod'
|
||||
},
|
||||
renderGracePeriod: function() {
|
||||
var format = function(time) {
|
||||
return time >= 10 ? time.toString() : '0' + time;
|
||||
};
|
||||
var grace_period = this.model.get('grace_period');
|
||||
this.$el.find('#course-grading-graceperiod').val(
|
||||
format(grace_period.hours) + ':' + format(grace_period.minutes)
|
||||
);
|
||||
},
|
||||
setGracePeriod : function(event) {
|
||||
var self = event.data;
|
||||
self.clearValidationErrors();
|
||||
var newVal = self.model.dateToGracePeriod($(event.currentTarget).timepicker('getTime'));
|
||||
self.model.set('grace_period', newVal, {validate: true});
|
||||
this.clearValidationErrors();
|
||||
var newVal = this.model.parseGracePeriod($(event.currentTarget).val());
|
||||
this.model.set('grace_period', newVal, {validate: true});
|
||||
},
|
||||
updateModel : function(event) {
|
||||
if (!this.selectorToField[event.currentTarget.id]) return;
|
||||
|
||||
switch (this.selectorToField[event.currentTarget.id]) {
|
||||
case 'grace_period': // handled above
|
||||
case 'grace_period':
|
||||
this.setGracePeriod(event);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -97,7 +97,7 @@ from contentstore import utils
|
||||
<ol class="list-input">
|
||||
<li class="field text" id="field-course-grading-graceperiod">
|
||||
<label for="course-grading-graceperiod">${_("Grace Period on Deadline:")}</label>
|
||||
<input type="text" class="short time" id="course-grading-graceperiod" value="0:00" placeholder="HH:MM" autocomplete="off" />
|
||||
<input type="text" class="short time" id="course-grading-graceperiod" value="00:00" placeholder="HH:MM" autocomplete="off" />
|
||||
<span class="tip tip-inline">${_("Leeway on due dates")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
Reference in New Issue
Block a user