Skip to content

Commit

Permalink
Merge pull request #899 from edx/mohtamba/adjust_allowance_modal
Browse files Browse the repository at this point in the history
Mohtamba/adjust allowance modal
  • Loading branch information
mohtamba committed Jul 16, 2021
2 parents 053d38c + e5cf13f commit 3cb93dd
Show file tree
Hide file tree
Showing 15 changed files with 980 additions and 241 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Change Log
Unreleased
~~~~~~~~~~

[3.19.0] - 2021-07-16
~~~~~~~~~~~~~~~~~~~~~
* Updated allowance modal to allow bulk allowances to be added.
* Added waffle flag to enable/disable bulk allowances feature.

[3.18.0] - 2021-07-15
~~~~~~~~~~~~~~~~~~~~~
* Remove old proctored exam attempt url.
Expand Down
2 changes: 1 addition & 1 deletion edx_proctoring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"""

# Be sure to update the version number in edx_proctoring/package.json
__version__ = '3.18.0'
__version__ = '3.19.0'

default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name
6 changes: 4 additions & 2 deletions edx_proctoring/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ def add_bulk_allowances(exam_ids, user_ids, allowance_type, value):
"""
Adds (or updates) an allowance for multiple users and exams
"""

# Input pasrsing
exam_ids = set(exam_ids)
user_ids = set(user_ids)

Expand Down Expand Up @@ -569,7 +571,7 @@ def add_bulk_allowances(exam_ids, user_ids, allowance_type, value):
if multiplier < 0:
raise AllowanceValueNotAllowedException(err_msg)

if allowance_type == constants.ADDITIONAL_TIME:
if allowance_type in ProctoredExamStudentAllowance.ADDITIONAL_TIME_GRANTED:
err_msg = (
u'allowance_value "{value}" should be a non-negative integer value'
).format(value=value)
Expand Down Expand Up @@ -631,7 +633,7 @@ def add_bulk_allowances(exam_ids, user_ids, allowance_type, value):
for user_id in user_ids:
try:
add_allowance_for_user(exam_id, user_id,
ProctoredExamStudentAllowance.ADDITIONAL_TIME_GRANTED,
allowance_type,
value)
successes += 1
data.append({
Expand Down
2 changes: 0 additions & 2 deletions edx_proctoring/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,3 @@
CONTENT_VIEWABLE_PAST_DUE_DATE = getattr(settings, 'PROCTORED_EXAM_VIEWABLE_PAST_DUE', False)

TIME_MULTIPLIER = 'time_multiplier'

ADDITIONAL_TIME = 'additional_time'
2 changes: 2 additions & 0 deletions edx_proctoring/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def plugin_settings(settings):
[
'proctoring/js/models/proctored_exam_allowance_model.js',
'proctoring/js/models/proctored_exam_attempt_model.js',
'proctoring/js/models/proctored_exam_bulk_allowance_model.js',
'proctoring/js/models/proctored_exam_model.js',
'proctoring/js/models/learner_onboarding_model.js',
'proctoring/js/collections/proctored_exam_allowance_collection.js',
Expand All @@ -24,6 +25,7 @@ def plugin_settings(settings):
'proctoring/js/collections/proctored_exam_collection.js',
'proctoring/js/views/Backbone.ModalDialog.js',
'proctoring/js/views/proctored_exam_add_allowance_view.js',
'proctoring/js/views/proctored_exam_add_bulk_allowance_view.js',
'proctoring/js/views/proctored_exam_allowance_view.js',
'proctoring/js/views/proctored_exam_attempt_view.js',
'proctoring/js/views/proctored_exam_onboarding_view.js',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
edx = edx || {};

(function(Backbone) {
'use strict';

edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};

edx.instructor_dashboard.proctoring.ProctoredExamBulkAllowanceModel = Backbone.Model.extend({
url: '/api/edx_proctoring/v1/proctored_exam/bulk_allowance'

});
this.edx.instructor_dashboard.proctoring.ProctoredExamBulkAllowanceModel =
edx.instructor_dashboard.proctoring.ProctoredExamBulkAllowanceModel;
}).call(this, Backbone);
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
edx = edx || {};

(function(Backbone, $, _, gettext) {
'use strict';

edx.instructor_dashboard = edx.instructor_dashboard || {};
edx.instructor_dashboard.proctoring = edx.instructor_dashboard.proctoring || {};

edx.instructor_dashboard.proctoring.AddBulkAllowanceView = Backbone.ModalView.extend({
name: 'AddBulkAllowanceView',
template: null,
template_url: '/static/proctoring/templates/add-new-bulk-allowance.underscore',
initialize: function(options) {
this.all_exams = options.proctored_exams;
this.proctored_exams = [];
this.timed_exams = [];
this.proctored_exam_allowance_view = options.proctored_exam_allowance_view;
this.course_id = options.course_id;
this.allowance_types = options.allowance_types;
this.model = new edx.instructor_dashboard.proctoring.ProctoredExamBulkAllowanceModel();
_.bindAll(this, 'render');
this.loadTemplateData();
// Backbone.Validation.bind( this, {valid:this.hideError, invalid:this.showError});
},
events: {
'submit form': 'addAllowance',
'change #proctored_exam': 'selectExam',
'change #timed_exam': 'selectExam',
'change #allowance_type': 'selectAllowance',
'change #exam_type': 'selectExamType'
},
loadTemplateData: function() {
var self = this;
$.ajax({url: self.template_url, dataType: 'html'})
.done(function(templateData) {
self.sortExamsByExamType();
self.template = _.template(templateData);
self.render();
self.showModal();
self.updateCss();
});
},
updateCss: function() {
var $el = $(this.el);
$el.find('.modal-header').css({
color: '#1580b0',
'font-size': '20px',
'font-weight': '600',
'line-height': 'normal',
padding: '10px 15px',
'border-bottom': '1px solid #ccc'
});
$el.find('form').css({
padding: '15px'
});
$el.find('form table.compact td').css({
'vertical-align': 'middle',
padding: '4px 8px'
});
$el.find('form label').css({
display: 'block',
'font-size': '14px',
margin: 0,
cursor: 'default'
});
$el.find('form #minutes_label').css({
display: 'inline-block'
});
$el.find('form input[type="text"]').css({
height: '26px',
padding: '1px 8px 2px',
'font-size': '14px',
width: '100%'
});
$el.find('form input[type="submit"]').css({
'margin-top': '10px',
float: 'right'
});
$el.find('.error-message').css({
color: '#ff0000',
'line-height': 'normal',
'font-size': '14px'
});
$el.find('.error-response').css({
color: '#ff0000',
'line-height': 'normal',
'font-size': '14px',
padding: '0px 10px 5px 7px'
});
$el.find('form select').css({
padding: '2px 0px 2px 2px',
'font-size': '16px',
width: '100%'
});
$el.find('#selected_exams').css({
background: '#fff',
display: 'flex',
'flex-wrap': 'wrap',
'align-content': 'flex-start',
'overflow-x': 'scroll'
});
$el.find('.tag').css({
'font-size': '14px',
height: '15px',
'margin-right': '5px',
padding: '5px 6px',
border: '1px solid #ccc',
'border-radius': '3px',
background: '#eee',
display: 'flex',
'align-items': 'center',
color: '#333',
'box-shadow': '0 0 4px rgba(0, 0, 0, 0.2), inset 0 1px 1px #fff',
cursor: 'default'
});
$el.find('.close').css({
'font-size': '16px',
margin: '5px'
});
$el.find('.exam_dropdown').css({
height: '60px'
});
},
getCurrentFormValues: function() {
return {
proctored_exam: this.selectedExams,
allowance_type: $('select#allowance_type').val(),
allowance_value: $('#allowance_value').val(),
user_info: $('#user_info').val()
};
},
hideError: function(view, attr) {
var $element = view.$form[attr];

$element.removeClass('error');
$element.parent().find('.error-message').empty();
},
showError: function(view, attr, errorMessage) {
var $element = view.$form[attr];
var $errorMessage;

$element.addClass('error');
$errorMessage = $element.parent().find('.error-message');
if ($errorMessage.length === 0) {
$errorMessage = $("<div class='error-message'></div>");
$element.parent().append($errorMessage);
}

$errorMessage.empty().append(errorMessage);
this.updateCss();
},
addAllowance: function(event) {
var $errorResponse, values, formHasErrors, exams;
var self = this;
event.preventDefault();
$errorResponse = $('.error-response');
$errorResponse.html();
values = this.getCurrentFormValues();
formHasErrors = false;
exams = '';

$('.close').each(function() {
exams += $(this).attr('data-item') + ',';
});

$.each(values, function(key, value) {
if (value === '') {
formHasErrors = true;
self.showError(self, key, gettext('Required field'));
} else {
self.hideError(self, key);
}
});

if (exams === '') {
formHasErrors = true;
self.showError(self, 'proctored_exam', gettext('Required field'));
} else {
self.hideError(self, 'proctored_exam');
}

if (!formHasErrors) {
self.model.fetch({
headers: {
'X-CSRFToken': self.proctored_exam_allowance_view.getCSRFToken()
},
type: 'PUT',
data: {
exam_ids: exams,
user_ids: values.user_info,
allowance_type: values.allowance_type,
value: values.allowance_value
},
success: function() {
// fetch the allowances again.
$errorResponse.html();
self.proctored_exam_allowance_view.collection.url =
self.proctored_exam_allowance_view.initial_url + self.course_id + '/allowance';
self.proctored_exam_allowance_view.hydrate();
self.hideModal();
},
error: function(unused, response) {
var data = $.parseJSON(response.responseText);
$errorResponse.html(gettext(data.detail));
}
});
}
},
selectExamAtIndex: function(examID, examName) {
var createdTag = this.createTag(examName, examID);
$('.exam_dropdown:visible').val('default');
$('.exam_dropdown:visible option[value=' + examID + ']').remove();
$('#selected_exams').append(createdTag);
this.updateCss();
},
selectExam: function() {
this.selectExamAtIndex($('.exam_dropdown:visible').val(), $('.exam_dropdown:visible :selected').text());
},
selectAllowance: function() {
this.updateAllowanceLabels($('#allowance_type').val());
},
selectExamType: function() {
$('.close').each(function() {
$(this).trigger('click');
});
if ($('#proctored_exam').is(':visible')) {
$('#proctored_exam').hide();
$('#timed_exam').show();
$('#allowance_type option[value="review_policy_exception"]').remove();
} else {
$('#proctored_exam').show();
$('#timed_exam').hide();
$('#allowance_type').append(new Option(gettext('Review Policy Exception'), 'review_policy_exception'));
}
this.updateAllowanceLabels($('#allowance_type').val());
},
updateAllowanceLabels: function(selectedAllowanceType) {
if (selectedAllowanceType === 'additional_time_granted') {
$('#allowance_value_label').text(gettext('Input Additional Minutes as a Positive Number'));
} else if (selectedAllowanceType === 'time_multiplier') {
$('#allowance_value_label').text(gettext('Input Multiplier as a Number Greater Than 1'));
} else {
$('#allowance_value_label').text(gettext('Add Policy Exception'));
}
},
sortExamsByExamType: function() {
var self = this;
self.all_exams.forEach(function(exam) {
if (exam.is_proctored) {
self.proctored_exams.push(exam);
} else {
self.timed_exams.push(exam);
}
});
},
createTag: function(examName, examID) {
var div = document.createElement('div');
var span = document.createElement('span');
var closeIcon = document.createElement('span');
div.setAttribute('class', 'tag');
span.innerHTML = examName;
closeIcon.innerHTML = 'x';
closeIcon.setAttribute('class', 'close');
closeIcon.setAttribute('data-item', examID);
closeIcon.setAttribute('data-name', examName);
closeIcon.onclick = this.deleteTag;
div.appendChild(span);
div.appendChild(closeIcon);
return div;
},
deleteTag: function() {
var examID = $(this).data('item');
var examName = $(this).data('name');
$(this).closest('div').remove();
$('.exam_dropdown:visible').append(new Option(examName, examID));
},

render: function() {
$(this.el).html(this.template({
proctored_exams: this.proctored_exams,
timed_exams: this.timed_exams,
allowance_types: this.allowance_types
}));

this.$form = {
proctored_exam: this.$('select#proctored_exam'),
timed_exam: this.$('select#timed_exam'),
allowance_type: this.$('select#allowance_type'),
allowance_value: this.$('#allowance_value'),
user_info: this.$('#user_info')
};
return this;
}
});
}).call(this, Backbone, $, _, gettext);
Loading

0 comments on commit 3cb93dd

Please sign in to comment.