Skip to content

Commit

Permalink
MDL-60474 assign: Consistent user filters
Browse files Browse the repository at this point in the history
Use the current filters and sorting on the user grading table in the single
page grading app when it is possible.

This replaces the popover used to configure the filters to one that closely matches the
one from the grading table. It supports standard filters, workflow filters and allocated marker filters.

It will also support group filtering and suspended user filtering but we don't show the controls for those in
the single grading page.
  • Loading branch information
Damyon Wiese committed Dec 19, 2018
1 parent 33a388e commit 064f150
Show file tree
Hide file tree
Showing 16 changed files with 526 additions and 159 deletions.
2 changes: 1 addition & 1 deletion mod/assign/amd/build/grading_navigation.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mod/assign/amd/build/grading_navigation_user_info.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mod/assign/amd/build/participant_selector.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

203 changes: 135 additions & 68 deletions mod/assign/amd/src/grading_navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
this._filters = [];
this._users = [];
this._filteredUsers = [];
this._lastXofYUpdate = 0;
this._firstLoadUsers = true;

// Get the current user list from a webservice.
this._loadAllUsers();

// We do not allow navigation while ajax requests are pending.
// Attach listeners to the select and arrow buttons.

this._region.find('[data-action="previous-user"]').on('click', this._handlePreviousUser.bind(this));
Expand All @@ -56,7 +59,7 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
var toggleLink = this._region.find('[data-region="user-filters"]');
var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));

configPanel.on('change', '[type="checkbox"]', this._filterChanged.bind(this));
configPanel.on('change', 'select', this._filterChanged.bind(this));

var userid = $('[data-region="grading-navigation-panel"]').data('first-userid');
if (userid) {
Expand All @@ -68,8 +71,6 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
}
).fail(notification.exception);

// We do not allow navigation while ajax requests are pending.

$(document).bind("start-loading-user", function() {
this._isLoading = true;
}.bind(this));
Expand All @@ -93,23 +94,44 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
/** @type {JQuery} JQuery node for the page region containing the user navigation. */
GradingNavigation.prototype._region = null;

/** @type {String} Last active filters */
GradingNavigation.prototype._lastFilters = '';

/**
* Load the list of all users for this assignment.
*
* @private
* @method _loadAllUsers
* @return {Boolean} True if the user list was fetched.
*/
GradingNavigation.prototype._loadAllUsers = function() {
var select = this._region.find('[data-action=change-user]');
var assignmentid = select.attr('data-assignmentid');
var groupid = select.attr('data-groupid');

var filterPanel = this._region.find('[data-region="configure-filters"]');
var filter = filterPanel.find('select[name="filter"]').val();
var workflowFilter = filterPanel.find('select[name="workflowfilter"]');
if (workflowFilter) {
filter += ',' + workflowFilter.val();
}
var markerFilter = filterPanel.find('select[name="markerfilter"]');
if (markerFilter) {
filter += ',' + markerFilter.val();
}

if (this._lastFilters == filter) {
return false;
}
this._lastFilters = filter;

ajax.call([{
methodname: 'mod_assign_list_participants',
args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true},
args: {assignid: assignmentid, groupid: groupid, filter: '', onlyids: true, tablesort: true},
done: this._usersLoaded.bind(this),
fail: notification.exception
}]);
return true;
};

/**
Expand All @@ -120,13 +142,14 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
* @param {Array} users
*/
GradingNavigation.prototype._usersLoaded = function(users) {
this._firstLoadUsers = false;
this._filteredUsers = this._users = users;
if (this._users.length) {
// Position the configure filters panel under the link that expands it.
var toggleLink = this._region.find('[data-region="user-filters"]');
var configPanel = $(document.getElementById(toggleLink.attr('aria-controls')));

configPanel.find('[type="checkbox"]').trigger('change');
configPanel.find('select[name="filter"]').trigger('change');
} else {
this._selectNoUser();
}
Expand All @@ -153,35 +176,69 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
}
};

/**
* Close the configure filters panel if a click is detected outside of it.
*
* @private
* @method _updateFilterPreference
* @param {Number} userId The current user id.
* @param {Array} filterList The list of current filter values.
* @param {Array} preferenceNames The names of the preferences to update
* @return {Promise} Resolved when all the preferences are updated.
*/
GradingNavigation.prototype._updateFilterPreferences = function(userId, filterList, preferenceNames) {
var preferences = [],
i = 0;

if (filterList.length == 0 || this._firstLoadUsers) {
// Nothing to update.
var deferred = $.Deferred();
deferred.resolve();
return deferred;
}
// General filter.
// Set the user preferences to the current filters.
for (i = 0; i < filterList.length; i++) {
var newValue = filterList[i];
if (newValue == 'none') {
newValue = '';
}

preferences.push({
userid: userId,
name: preferenceNames[i],
value: newValue
});
}

return ajax.call([{
methodname: 'core_user_set_user_preferences',
args: {
preferences: preferences
}
}])[0];
};
/**
* Turn a filter on or off.
*
* @private
* @method _filterChanged
* @param {Event} event
*/
GradingNavigation.prototype._filterChanged = function(event) {
var name = $(event.target).attr('name');
var key = name.split('_').pop();
var enabled = $(event.target).prop('checked');

if (enabled) {
if (this._filters.indexOf(key) == -1) {
this._filters[this._filters.length] = key;
}
} else {
var index = this._filters.indexOf(key);
if (index != -1) {
this._filters.splice(index, 1);
}
}
GradingNavigation.prototype._filterChanged = function() {
// There are 3 types of filter right now.
var filterPanel = this._region.find('[data-region="configure-filters"]');
var filters = filterPanel.find('select');

this._filters = [];
filters.each(function(idx, ele) {
this._filters.push($(ele).val());
}.bind(this));

// Update the active filter string.
var filterlist = [];
this._region.find('[data-region="configure-filters"]').find('[type="checkbox"]').each(function(idx, ele) {
if ($(ele).prop('checked')) {
filterlist[filterlist.length] = $(ele).closest('label').text();
}
filterPanel.find('option:checked').each(function(idx, ele) {
filterlist[filterlist.length] = $(ele).text();
});
if (filterlist.length) {
this._region.find('[data-region="user-filters"] span').text(filterlist.join(', '));
Expand All @@ -191,50 +248,30 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
}.bind(this)).fail(notification.exception);
}

// Filter the options in the select box that do not match the current filters.

var select = this._region.find('[data-action=change-user]');
var userid = select.attr('data-selected');
var foundIndex = 0;

this._filteredUsers = [];

$.each(this._users, function(index, user) {
var show = true;
$.each(this._filters, function(filterindex, filter) {
if (filter == "submitted") {
if (user.submitted == "0") {
show = false;
var currentUserID = select.data('currentuserid');
var preferenceNames = ['assign_filter', 'assign_workflowfilter', 'assign_markerfilter'];
this._updateFilterPreferences(currentUserID, this._filters, preferenceNames).done(function() {
// Reload the list of users to apply the new filters.
if (!this._loadAllUsers()) {
var userid = parseInt(select.attr('data-selected'));
var foundIndex = 0;
// Search the returned users for the current selection.
$.each(this._filteredUsers, function(index, user) {
if (userid == user.id) {
foundIndex = index;
}
} else if (filter == "notsubmitted") {
if (user.submitted == "1") {
show = false;
}
} else if (filter == "requiregrading") {
if (user.requiregrading == "0") {
show = false;
}
} else if (filter == "grantedextension") {
if (user.grantedextension == "0") {
show = false;
}
}
});
});

if (show) {
this._filteredUsers[this._filteredUsers.length] = user;
if (userid == user.id) {
foundIndex = (this._filteredUsers.length - 1);
if (this._filteredUsers.length) {
this._selectUserById(this._filteredUsers[foundIndex].id);
} else {
this._selectNoUser();
}
}
}.bind(this));

if (this._filteredUsers.length) {
this._selectUserById(this._filteredUsers[foundIndex].id);
} else {
this._selectNoUser();
}
this._triggerNextUserEvent();
}
}.bind(this)).fail(notification.exception);
this._refreshCount();
};

/**
Expand Down Expand Up @@ -396,6 +433,28 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
}
};

/**
* Set count string. This method only sets the value for the last time it was ever called to deal
* with promises that return in a non-predictable order.
*
* @private
* @method _setCountString
* @param {Number} x
* @param {Number} y
*/
GradingNavigation.prototype._setCountString = function(x, y) {
var updateNumber = 0;
this._lastXofYUpdate++;
updateNumber = this._lastXofYUpdate;

var param = {x: x, y: y};
str.get_string('xofy', 'mod_assign', param).done(function(s) {
if (updateNumber == this._lastXofYUpdate) {
this._region.find('[data-region="user-count-summary"]').text(s);
}
}.bind(this)).fail(notification.exception);
};

/**
* Rebuild the x of y string.
*
Expand Down Expand Up @@ -423,11 +482,19 @@ define(['jquery', 'core/notification', 'core/str', 'core/form-autocomplete',
if (count) {
currentIndex += 1;
}
var param = {x: currentIndex, y: count};

str.get_string('xofy', 'mod_assign', param).done(function(s) {
this._region.find('[data-region="user-count-summary"]').text(s);
}.bind(this)).fail(notification.exception);
this._setCountString(currentIndex, count);
// Update window URL
if (currentIndex > 0) {
var url = new URL(window.location);
if (parseInt(url.searchParams.get('blindid')) > 0) {
var newid = this._filteredUsers[currentIndex - 1].recordid;
url.searchParams.set('blindid', newid);
} else {
url.searchParams.set('userid', userid);
}
// We do this so a browser refresh will return to the same user.
window.history.replaceState({}, "", url);
}
}
};

Expand Down
Loading

0 comments on commit 064f150

Please sign in to comment.