MDL-86006 mod_assign: Display markers on grading table

This commit is contained in:
Conn Warwicker
2026-03-05 14:52:27 +11:00
committed by David Woloszyn
parent a47646f775
commit eaf4a6d425
2 changed files with 218 additions and 43 deletions
+215 -43
View File
@@ -456,14 +456,21 @@ class assign_grading_table extends table_sql implements renderable {
$columns[] = 'team';
$headers[] = get_string('submissionteam', 'assign');
}
// Allocated marker.
// Multiple markers.
if ($this->assignment->get_instance()->markingworkflow &&
$this->assignment->get_instance()->markingallocation &&
has_capability('mod/assign:manageallocations', $this->assignment->get_context())) {
// Add a column for the allocated marker.
$columns[] = 'allocatedmarker';
$headers[] = get_string('marker', 'assign');
property_exists(
$assignment->get_instance(),
'markercount',
)
) {
for ($i = 1; $i <= $assignment->get_instance()->markercount; $i++) {
$columns[] = "marker$i";
$headers[] = get_string('markernumber', 'assign', $i);
}
}
// Grade.
$columns[] = 'grade';
$headers[] = get_string('gradenoun');
@@ -668,15 +675,6 @@ class assign_grading_table extends table_sql implements renderable {
$o .= html_writer::div($allworkflowstates[$workflowstate]);
} else {
$o .= html_writer::select($workflowstates, $name, $workflowstate, ['' => $notmarked]);
// Check if this user is a marker that can't manage allocations and doesn't have the marker column added.
if ($this->assignment->get_instance()->markingworkflow &&
$this->assignment->get_instance()->markingallocation &&
!has_capability('mod/assign:manageallocations', $this->assignment->get_context())) {
$name = 'quickgrade_' . $row->id . '_allocatedmarker';
$o .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => $name,
'value' => $row->allocatedmarker]);
}
}
} else {
$o .= $this->output->container(get_string('markingworkflowstate' . $workflowstate, 'assign'), $workflowstate);
@@ -699,56 +697,102 @@ class assign_grading_table extends table_sql implements renderable {
return get_string('markingworkflowstate' . $state, 'assign');
}
/**
* Get the user object for the marker of a given student and marker number.
*
* @param int $studentid ID of the student.
* @param int $number Marker index number.
* @return stdClass|false User object or false if not found.
*/
protected function get_marker_number(int $studentid, int $number): stdClass|false {
global $DB;
$multimarkers = $DB->get_fieldset('assign_allocated_marker', 'marker', [
'student' => $studentid, 'assignment' => $this->assignment->get_instance()->id,
]);
if (!empty($multimarkers) && count($multimarkers) >= $number) {
// Then get the name of the one at the column position requested, e.g. marker1, marker2, etc...
$index = $number - 1;
return \core_user::get_user($multimarkers[$index]);
}
return false;
}
/**
* list current marker
*
* @param stdClass $row - The row of data
* @return id the user->id of the marker.
* @param int $markerpos - Marker position number - related to the markercount
* @return string The name of the allocated marker
*/
public function col_allocatedmarker(stdClass $row) {
public function col_allocatedmarker(stdClass $row, int $markerpos = 1) {
static $markers = null;
static $markerlist = array();
if ($markers === null) {
list($sort, $params) = users_order_by_sql('u');
// Only enrolled users could be assigned as potential markers.
$markers = get_enrolled_users($this->assignment->get_context(), 'mod/assign:grade', 0, 'u.*', $sort);
$markerlist[0] = get_string('choosemarker', 'assign');
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->assignment->get_context());
foreach ($markers as $marker) {
$markerlist[$marker->id] = fullname($marker, $viewfullnames);
// Get the allocated markers that have been assigned to this student, if we are using multi-marking.
$allocatedmarker = $this->get_marker_number($row->userid, $markerpos);
if ($this->is_downloading()) {
if ($allocatedmarker) {
return fullname(
$allocatedmarker,
has_capability('moodle/site:viewfullnames', $this->assignment->get_context())
);
}
}
if (empty($markerlist)) {
// TODO: add some form of notification here that no markers are available.
return '';
}
if ($this->is_downloading()) {
if (isset($markers[$row->allocatedmarker])) {
return fullname($markers[$row->allocatedmarker],
has_capability('moodle/site:viewfullnames', $this->assignment->get_context()));
} else {
if (
$this->quickgrading &&
has_capability('mod/assign:manageallocations', $this->assignment->get_context()) &&
(
empty($row->workflowstate) ||
$row->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_INMARKING ||
$row->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED
)
) {
// Get the potential users who could be assigned as an allocated marker.
if ($markers === null) {
[$sort, $params] = users_order_by_sql('u');
// Only enrolled users could be assigned as potential markers.
$markers = get_enrolled_users($this->assignment->get_context(), 'mod/assign:grade', 0, 'u.*', $sort);
$markerlist[0] = get_string('choosemarker', 'assign');
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->assignment->get_context());
foreach ($markers as $marker) {
$markerlist[$marker->id] = fullname($marker, $viewfullnames);
}
}
if (empty($markerlist)) {
return '';
}
if (count($markers) > 1) {
$label = get_string('allocatedmarker', 'assign') . ' ' . $markerpos;
} else {
$label = get_string('allocatedmarker', 'assign');
}
$name = 'quickgrade_' . $row->id . '_allocatedmarker_' . $markerpos;
return html_writer::label(
$label,
'menu' . $name
) . html_writer::select($markerlist, $name, ($allocatedmarker) ? $allocatedmarker->id : '', false);
}
if ($this->quickgrading && has_capability('mod/assign:manageallocations', $this->assignment->get_context()) &&
(empty($row->workflowstate) ||
$row->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_INMARKING ||
$row->workflowstate == ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED)) {
$name = 'quickgrade_' . $row->id . '_allocatedmarker';
return html_writer::select($markerlist, $name, $row->allocatedmarker, false);
} else if (!empty($row->allocatedmarker)) {
if ($allocatedmarker) {
$output = '';
if ($this->quickgrading) { // Add hidden field for quickgrading page.
$name = 'quickgrade_' . $row->id . '_allocatedmarker';
$attributes = ['type' => 'hidden', 'name' => $name, 'value' => $row->allocatedmarker];
$name = 'quickgrade_' . $row->id . '_allocatedmarker_' . $markerpos;
$attributes = ['type' => 'hidden', 'name' => $name, 'value' => $allocatedmarker->id];
$output .= html_writer::empty_tag('input', $attributes);
}
$output .= $markerlist[$row->allocatedmarker];
$output .= html_writer::tag('strong', fullname($allocatedmarker));
return $output;
}
return '';
}
/**
* For download only - list all the valid options for this custom scale.
*
@@ -1048,6 +1092,128 @@ class assign_grading_table extends table_sql implements renderable {
return $displaygrade;
}
/**
* Format the marker column's data for display.
*
* @param stdClass $row Row object.
* @param int $col Marker number column.
* @return string
*/
public function col_marker(stdClass $row, int $col): string {
global $USER, $DB;
$allocatedmarker = "";
$index = $col - 1;
if (
$this->assignment->get_instance()->markingworkflow &&
$this->assignment->get_instance()->markingallocation
) {
$allocatedmarker = $this->col_allocatedmarker($row, $col);
}
$gradingdisabled = $this->assignment->grading_disabled($row->id, true, $this->gradinginfo);
$displaymark = "";
if (!$this->is_downloading() && $this->hasgrade) {
if (
$this->assignment->get_instance()->markingworkflow &&
$this->assignment->get_instance()->markingallocation
) {
// Allocated markers are enabled: get the mark corresponding to
// the marker for this column.
$markers = array_values($DB->get_records('assign_allocated_marker', [
'student' => $row->userid,
'assignment' => $this->assignment->get_instance()->id,
], 'id'));
if (count($markers) > $index) {
$mark = $DB->get_record('assign_mark', ['gradeid' => $row->gradeid, 'marker' => $markers[$index]->marker]);
// Mark is only editable if we are quick grading, grading is not disabled, and if we are either
// the marker for this column, or we have manageallocations permissions.
$editable = (
($this->quickgrading) &&
(!$gradingdisabled) &&
($USER->id == $markers[$index]->marker)
);
$displaymark = $this->display_grade(
$mark->mark ?? null,
$editable,
$row->userid,
$row->timemarked,
0,
$markers[$index]->marker,
);
// Display the workflow state for this mark.
if ($markers[$index]->marker > 0) {
$displaymark .= html_writer::div(
get_string('markingworkflowstate' . ($mark->workflowstate ?? 'notmarked'), 'assign'),
'badge bg-info d-block'
);
}
}
}
$urlparams = [
'id' => $this->assignment->get_course_module()->id,
'rownum' => 0,
'action' => 'marker',
];
if ($this->assignment->is_blind_marking()) {
if (empty($row->recordid)) {
$row->recordid = $this->assignment->get_uniqueid_for_user($row->userid);
}
$urlparams['blindid'] = $row->recordid;
} else {
$urlparams['userid'] = $row->userid;
}
$url = new moodle_url('/mod/assign/view.php', $urlparams);
// The container with the grade information.
$gradecontainer = $this->output->container($displaymark, 'w-100');
// Should this user get the 'Mark' action menu item?
$isallocatedmarker = false;
if (
$this->assignment->get_instance()->markingworkflow &&
$this->assignment->get_instance()->markingallocation
) {
// If allocated marking is enabled is this user the marker for this column?
if (
$markers = $DB->get_fieldset('assign_allocated_marker', 'marker', [
'student' => $row->userid,
'assignment' => $this->assignment->get_instance()->id,
])
) {
$isallocatedmarker = (array_key_exists($index, $markers) && $markers[$index] == $USER->id);
}
}
if ($isallocatedmarker) {
$menu = new action_menu();
$menu->set_owner_selector('.gradingtable-actionmenu');
$menu->set_boundary('window');
$menu->set_kebab_trigger(get_string('markactions', 'assign'));
$menu->set_additional_classes('ps-2 ms-auto');
// Prioritise the menu ahead of all other actions.
$menu->prioritise = true;
// Add the 'Mark' action item to the contextual menu.
$menu->add(new action_menu_link_secondary($url, null, get_string('markverb', 'assign')));
// The contextual menu container.
$contextualmenucontainer = $this->output->container($this->output->render($menu), 'd-flex');
return $allocatedmarker .
$this->output->container($gradecontainer . $contextualmenucontainer, ['class' => 'd-flex']);
}
}
// The table data is being downloaded, or the user cannot grade; therefore, only the formatted grade for display
// is returned.
if ($this->is_downloading()) {
return $displaymark;
} else {
return $allocatedmarker . $this->output->container($gradecontainer, ['class' => 'd-flex']);
}
}
/**
* Format a column of data for display.
*
@@ -1644,6 +1810,12 @@ class assign_grading_table extends table_sql implements renderable {
* @return mixed string or NULL
*/
public function other_cols($colname, $row) {
// If the column name is formatted as "marker" and a number, e.g. "marker1", this is a Marker column
// and so we need to call col_marker() instead with the marker number provided in the name. In this case "1".
if (str_starts_with($colname, 'marker') && ($col = substr($colname, 6)) && ctype_digit($col)) {
return $this->col_marker($row, $col);
}
// For extra user fields the result is already in $row.
if (empty($this->plugincache[$colname])) {
return parent::other_cols($colname, $row);
+3
View File
@@ -384,11 +384,13 @@ $string['locksubmissionforstudent'] = 'Prevent any more submissions for student:
$string['locksubmissions'] = 'Lock submissions';
$string['manageassignfeedbackplugins'] = 'Manage assignment feedback plugins';
$string['manageassignsubmissionplugins'] = 'Manage assignment submission plugins';
$string['markactions'] = 'Mark actions';
$string['marker'] = 'Marker';
$string['markercount'] = 'Markers';
$string['markercount_help'] = 'Number of markers for the assignment.';
$string['markerfilter'] = 'Marker filter';
$string['markerfilternomarker'] = 'No marker';
$string['markernumber'] = 'Marker {$a}';
$string['markgradeaverage'] = 'Average mark';
$string['markgradeaverage_help'] = 'Grade will be calculated as the average of all marks given.';
$string['markgrademanual'] = 'Manual';
@@ -417,6 +419,7 @@ $string['markingworkflowstatenotmarked'] = 'Not marked';
$string['markingworkflowstatereadyforreview'] = 'Marking completed';
$string['markingworkflowstatereadyforrelease'] = 'Ready for release';
$string['markingworkflowstatereleased'] = 'Released';
$string['markverb'] = 'Mark';
$string['multimarkmethod'] = 'Calculate grade';
$string['multimarkmethod_help'] = 'How the grade will be calculated when there are multiple markers';
$string['multimarkrounding'] = 'Rounding';