mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
MDL-38105 gradingform_rubric: option to lock 0 level for rubrics
this also allows using negative scores
This commit is contained in:
parent
35d5053ba2
commit
dfc261be66
@ -56,8 +56,8 @@ if ($mform->is_cancelled()) {
|
||||
|
||||
// If we do not go back to management url and the minscore warning needs to be displayed, display it during redirection.
|
||||
$warning = null;
|
||||
if (!empty($data->returnurl)) {
|
||||
if (($scores = $controller->get_min_max_score()) && $scores['minscore'] <> 0) {
|
||||
if (!empty($data->returnurl) && $data->returnurl !== $manager->get_management_url()->out(false)) {
|
||||
if (empty($data->rubric['options']['lockzeropoints']) && ($scores = $controller->get_min_max_score()) && $scores['minscore'] <> 0) {
|
||||
$warning = get_string('zerolevelsabsent', 'gradingform_rubric').'<br>'.
|
||||
html_writer::link($manager->get_management_url(), get_string('back'));
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ $string['err_nocriteria'] = 'Rubric must contain at least one criterion';
|
||||
$string['err_nodefinition'] = 'Level definition can not be empty';
|
||||
$string['err_nodescription'] = 'Criterion description can not be empty';
|
||||
$string['err_novariations'] = 'Criterion levels cannot all be worth the same number of points';
|
||||
$string['err_scoreformat'] = 'Number of points for each level must be a valid non-negative number';
|
||||
$string['err_scoreformat'] = 'Number of points for each level must be a valid number';
|
||||
$string['err_totalscore'] = 'Maximum number of points possible when graded by the rubric must be more than zero';
|
||||
$string['gradingof'] = '{$a} grading';
|
||||
$string['level'] = 'Level {$a->definition}, {$a->score} points.';
|
||||
@ -53,6 +53,11 @@ $string['leveldelete'] = 'Delete level {$a}';
|
||||
$string['leveldefinition'] = 'Level {$a} definition';
|
||||
$string['levelempty'] = 'Click to edit level';
|
||||
$string['levelsgroup'] = 'Levels group';
|
||||
$string['lockzeropoints'] = 'When converting rubric score to points/scale assume that minimum number of points is 0';
|
||||
$string['lockzeropoints_help'] = 'Locking the minimum number of points will allow to create rubrics without 0-levels. This may also mean that 0% grade on this rubric is not possible to achieve.<br />
|
||||
The maximum score in the rubric is always converted to the maximum grade.<br />
|
||||
When this setting is unchecked, the minimum possible score for this rubric will be converted to the minimum grade available in the module (which is zero unless the scale is used).<br />
|
||||
<a href="https://docs.moodle.org/en/Rubrics#Grade_calculation">Explanation of rubrics grade calculation</a>';
|
||||
$string['name'] = 'Name';
|
||||
$string['needregrademessage'] = 'The rubric definition was changed after this student had been graded. The student can not see this rubric until you check the rubric and update the grade.';
|
||||
$string['pluginname'] = 'Rubric';
|
||||
@ -66,8 +71,9 @@ $string['rubric'] = 'Rubric';
|
||||
$string['rubricmapping'] = 'Score to grade mapping rules';
|
||||
$string['rubricmappingexplained'] = 'The minimum possible score for this rubric is <b>{$a->minscore} points</b> and it will be converted to the minimum grade available in this module (which is zero unless the scale is used).
|
||||
The maximum score <b>{$a->maxscore} points</b> will be converted to the maximum grade.<br />
|
||||
Intermediate scores will be converted respectively and rounded to the nearest available grade.<br />
|
||||
If a scale is used instead of a grade, the score will be converted to the scale elements as if they were consecutive integers.';
|
||||
Intermediate scores will be converted respectively.<br />
|
||||
If a scale is used for grading, the score will be rounded and converted to the scale elements as if they were consecutive integers.<br><br>
|
||||
You can change the method of grade calculation in the Rubic options section below.';
|
||||
$string['rubricnotcompleted'] = 'Please choose something for each criterion';
|
||||
$string['rubricoptions'] = 'Rubric options';
|
||||
$string['rubricstatus'] = 'Current rubric status';
|
||||
|
@ -143,8 +143,16 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
// reload the definition from the database
|
||||
$currentdefinition = $this->get_definition(true);
|
||||
|
||||
// update rubric data
|
||||
$haschanges = array();
|
||||
|
||||
// Check if 'lockzeropoints' option has changed.
|
||||
$newlockzeropoints = $newdefinition->rubric['options']['lockzeropoints'];
|
||||
$currentoptions = $this->get_options();
|
||||
if ((bool)$newlockzeropoints != (bool)$currentoptions['lockzeropoints']) {
|
||||
$haschanges[3] = true;
|
||||
}
|
||||
|
||||
// update rubric data
|
||||
if (empty($newdefinition->rubric['criteria'])) {
|
||||
$newcriteria = array();
|
||||
} else {
|
||||
@ -203,10 +211,7 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
}
|
||||
foreach ($levelsdata as $levelid => $level) {
|
||||
if (isset($level['score'])) {
|
||||
$level['score'] = (float)$level['score'];
|
||||
if ($level['score']<0) {
|
||||
$level['score'] = 0;
|
||||
}
|
||||
$level['score'] = unformat_float($level['score']);
|
||||
}
|
||||
if (preg_match('/^NEWID\d+$/', $levelid)) {
|
||||
// insert level into DB
|
||||
@ -354,6 +359,7 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
public static function get_default_options() {
|
||||
$options = array(
|
||||
'sortlevelsasc' => 1,
|
||||
'lockzeropoints' => 1,
|
||||
'alwaysshowdefinition' => 1,
|
||||
'showdescriptionteacher' => 1,
|
||||
'showdescriptionstudent' => 1,
|
||||
@ -368,6 +374,9 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
/**
|
||||
* Gets the options of this rubric definition, fills the missing options with default values
|
||||
*
|
||||
* The only exception is 'lockzeropoints' - if other options are present in the json string but this
|
||||
* one is absent, this means that the rubric was created before Moodle 3.2 and the 0 value should be used.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_options() {
|
||||
@ -377,6 +386,11 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
foreach ($thisoptions as $option => $value) {
|
||||
$options[$option] = $value;
|
||||
}
|
||||
if (!array_key_exists('lockzeropoints', $thisoptions)) {
|
||||
// Rubrics created before Moodle 3.2 don't have 'lockzeropoints' option. In this case they should not
|
||||
// assume default value 1 but use "legacy" value 0.
|
||||
$options['lockzeropoints'] = 0;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
@ -522,7 +536,10 @@ class gradingform_rubric_controller extends gradingform_controller {
|
||||
$rubric .= $output->box($this->get_formatted_description(), 'gradingform_rubric-description');
|
||||
}
|
||||
if (has_capability('moodle/grade:managegradingforms', $page->context)) {
|
||||
$rubric .= $output->display_rubric_mapping_explained($this->get_min_max_score());
|
||||
if (!$options['lockzeropoints']) {
|
||||
// Warn about using grade calculation method where minimum number of points is flexible.
|
||||
$rubric .= $output->display_rubric_mapping_explained($this->get_min_max_score());
|
||||
}
|
||||
$rubric .= $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW, 'rubric');
|
||||
} else {
|
||||
$rubric .= $output->display_rubric($criteria, $options, self::DISPLAY_PREVIEW_GRADED, 'rubric');
|
||||
@ -889,11 +906,19 @@ class gradingform_rubric_instance extends gradingform_instance {
|
||||
foreach ($grade['criteria'] as $id => $record) {
|
||||
$curscore += $this->get_controller()->get_definition()->rubric_criteria[$id]['levels'][$record['levelid']]['score'];
|
||||
}
|
||||
$gradeoffset = ($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*($maxgrade-$mingrade);
|
||||
if ($this->get_controller()->get_allow_grade_decimals()) {
|
||||
return $gradeoffset + $mingrade;
|
||||
|
||||
$allowdecimals = $this->get_controller()->get_allow_grade_decimals();
|
||||
$options = $this->get_controller()->get_options();
|
||||
|
||||
if ($options['lockzeropoints']) {
|
||||
// Grade calculation method when 0-level is locked.
|
||||
$grade = max($mingrade, $curscore / $scores['maxscore'] * $maxgrade);
|
||||
return $allowdecimals ? $grade : round($grade, 0);
|
||||
} else {
|
||||
// Alternative grade calculation method.
|
||||
$gradeoffset = ($curscore - $scores['minscore']) / ($scores['maxscore'] - $scores['minscore']) * ($maxgrade - $mingrade);
|
||||
return ($allowdecimals ? $gradeoffset : round($gradeoffset, 0)) + $mingrade;
|
||||
}
|
||||
return round($gradeoffset, 0) + $mingrade;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -470,6 +470,9 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
|
||||
$html .= html_writer::tag('label', get_string($option, 'gradingform_rubric'), array('for' => $attrs['id']));
|
||||
break;
|
||||
}
|
||||
if (get_string_manager()->string_exists($option.'_help', 'gradingform_rubric')) {
|
||||
$html .= $this->help_icon($option, 'gradingform_rubric');
|
||||
}
|
||||
$html .= html_writer::end_tag('div'); // .option
|
||||
}
|
||||
$html .= html_writer::end_tag('div'); // .options
|
||||
@ -636,10 +639,7 @@ class gradingform_rubric_renderer extends plugin_renderer_base {
|
||||
if ($scores['minscore'] <> 0) {
|
||||
$html .= $this->output->notification(get_string('zerolevelsabsent', 'gradingform_rubric'), 'error');
|
||||
}
|
||||
$html .= $this->box(
|
||||
html_writer::tag('h4', get_string('rubricmapping', 'gradingform_rubric')).
|
||||
html_writer::tag('div', get_string('rubricmappingexplained', 'gradingform_rubric', (object)$scores))
|
||||
, 'generalbox rubricmappingexplained');
|
||||
$html .= $this->output->notification(get_string('rubricmappingexplained', 'gradingform_rubric', (object)$scores), 'info');
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
@ -232,29 +232,30 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
|
||||
'score' => 0,
|
||||
);
|
||||
foreach ($criterion['levels'] as $lastlevel) {
|
||||
if (isset($lastlevel['score']) && $level['score'] < $lastlevel['score'] + 1) {
|
||||
$level['score'] = $lastlevel['score'] + 1;
|
||||
if (isset($lastlevel['score'])) {
|
||||
$level['score'] = max($level['score'], ceil(unformat_float($lastlevel['score'])) + 1);
|
||||
}
|
||||
}
|
||||
$this->nonjsbuttonpressed = true;
|
||||
}
|
||||
if (!array_key_exists('delete', $level)) {
|
||||
$score = unformat_float($level['score'], true);
|
||||
if ($withvalidation) {
|
||||
if (!strlen(trim($level['definition']))) {
|
||||
$errors['err_nodefinition'] = 1;
|
||||
$level['error_definition'] = true;
|
||||
}
|
||||
if (!preg_match('#^[\+]?\d*$#', trim($level['score'])) && !preg_match('#^[\+]?\d*[\.,]\d+$#', trim($level['score']))) {
|
||||
if ($score === null || $score === false) {
|
||||
$errors['err_scoreformat'] = 1;
|
||||
$level['error_score'] = true;
|
||||
}
|
||||
}
|
||||
$levels[$levelid] = $level;
|
||||
if ($minscore === null || (float)$level['score'] < $minscore) {
|
||||
$minscore = (float)$level['score'];
|
||||
if ($minscore === null || $score < $minscore) {
|
||||
$minscore = $score;
|
||||
}
|
||||
if ($maxscore === null || (float)$level['score'] > $maxscore) {
|
||||
$maxscore = (float)$level['score'];
|
||||
if ($maxscore === null || $score > $maxscore) {
|
||||
$maxscore = $score;
|
||||
}
|
||||
} else {
|
||||
$this->nonjsbuttonpressed = true;
|
||||
@ -313,8 +314,10 @@ class MoodleQuickForm_rubriceditor extends HTML_QuickForm_input {
|
||||
|
||||
// create validation error string (if needed)
|
||||
if ($withvalidation) {
|
||||
if ($overallminscore == $overallmaxscore) {
|
||||
$errors['err_novariations'] = 1;
|
||||
if (!$return['options']['lockzeropoints']) {
|
||||
if ($overallminscore == $overallmaxscore) {
|
||||
$errors['err_novariations'] = 1;
|
||||
}
|
||||
}
|
||||
if (count($errors)) {
|
||||
$rv = array();
|
||||
|
@ -58,7 +58,7 @@ Feature: Rubrics can be created and edited
|
||||
And I complete the advanced grading form with these values:
|
||||
| Feedback comments | In general... work harder... |
|
||||
# Checking that the user grade is correct.
|
||||
And I should see "58.33" in the "Student 1" "table_row"
|
||||
And I should see "65" in the "Student 1" "table_row"
|
||||
# Updating the user grade.
|
||||
And I go to "Student 1" "Test assignment 1 name" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
@ -68,13 +68,13 @@ Feature: Rubrics can be created and edited
|
||||
#And the level with "50" points was previously selected for the rubric criterion "Criterion 1"
|
||||
#And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I save the advanced grading form
|
||||
And I should see "22.62" in the "Student 1" "table_row"
|
||||
And I should see "35" in the "Student 1" "table_row"
|
||||
And I log out
|
||||
# Viewing it as a student.
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And I should see "Rubric test description" in the ".feedback" "css_element"
|
||||
And I should see "In general... work harder..."
|
||||
And the level with "10" points is selected for the rubric criterion "Criterion 2"
|
||||
@ -97,7 +97,7 @@ Feature: Rubrics can be created and edited
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Editing a rubric with significant changes.
|
||||
@ -105,7 +105,7 @@ Feature: Rubrics can be created and edited
|
||||
And I follow "Course 1"
|
||||
And I go to "Test assignment 1 name" advanced grading definition page
|
||||
And I click on "Move down" "button" in the "Criterion 2" "table_row"
|
||||
And I replace "1" rubric level with "11" in "Criterion 1" criterion
|
||||
And I replace "1" rubric level with "60" in "Criterion 1" criterion
|
||||
And I press "Save"
|
||||
And I should see "You are about to save significant changes to a rubric that has already been used for grading. The gradebook value will be unchanged, but the rubric will be hidden from students until their item is regraded."
|
||||
And I press "Continue"
|
||||
@ -114,7 +114,7 @@ Feature: Rubrics can be created and edited
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "22.62" in the ".feedback" "css_element"
|
||||
And I should see "35" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
And I log out
|
||||
# Regrade student.
|
||||
@ -129,7 +129,7 @@ Feature: Rubrics can be created and edited
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 1 name"
|
||||
And I should see "12.16" in the ".feedback" "css_element"
|
||||
And I should see "31.82" in the ".feedback" "css_element"
|
||||
And the level with "20" points is not selected for the rubric criterion "Criterion 1"
|
||||
# Hide all rubric info for students
|
||||
And I log out
|
||||
|
@ -0,0 +1,56 @@
|
||||
@gradingform @gradingform_rubric @javascript
|
||||
Feature: Converting rubric score to grades
|
||||
In order to use and refine rubrics to grade students
|
||||
As a teacher
|
||||
I need to be able to use different grade settings
|
||||
|
||||
Scenario Outline:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following "scales" exist:
|
||||
| name | scale |
|
||||
| Test scale 1 | Disappointing, Good, Very good, Excellent |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
|
||||
| assign | Test assignment 1 | Test | C1 | assign1 | <grade> | rubric |
|
||||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I go to "Test assignment 1" advanced grading definition page
|
||||
And I set the following fields to these values:
|
||||
| Name | Assignment 1 rubric |
|
||||
| Description | Rubric test description |
|
||||
| When converting rubric score to points/scale assume that minimum number of points is 0 | <lockzeropoints> |
|
||||
And I define the following rubric:
|
||||
| Criterion 1 | Level 11 | 20 | Level 12 | 25 | Level 13 | 40 | Level 14 | 50 |
|
||||
| Criterion 2 | Level 21 | 20 | Level 22 | 25 | Level 23 | 30 | | |
|
||||
| Criterion 3 | Level 31 | 10 | Level 32 | 20 | | | | |
|
||||
And I press "Save rubric and make it ready"
|
||||
# Grading a student.
|
||||
And I go to "Student 1" "Test assignment 1" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 25 | |
|
||||
| Criterion 2 | 20 | |
|
||||
| Criterion 3 | 10 | |
|
||||
And I save the advanced grading form
|
||||
# Checking that the user grade is correct.
|
||||
And I should see "<studentgrade>" in the "student1@example.com" "table_row"
|
||||
And I log out
|
||||
|
||||
Examples:
|
||||
| grade | lockzeropoints | studentgrade |
|
||||
| 100 | 1 | 55.00 |
|
||||
| 70 | 1 | 38.50 |
|
||||
| Test scale 1 | 1 | Good |
|
||||
| 100 | | 10.00 |
|
||||
| 70 | | 7.00 |
|
||||
| Test scale 1 | | Disappointing |
|
@ -0,0 +1,65 @@
|
||||
@gradingform @gradingform_rubric @javascript
|
||||
Feature: Rubrics can have levels with negative scores
|
||||
In order to use and refine rubrics to grade students
|
||||
As a teacher
|
||||
I need to be able to penalise for very wrong submissions
|
||||
|
||||
Scenario: Using negative levels in rubrics
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
| student3 | Student | 3 | student3@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
| student3 | C1 | student |
|
||||
And the following "scales" exist:
|
||||
| name | scale |
|
||||
| Test scale 1 | Disappointing, Good, Very good, Excellent |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber | grade | advancedgradingmethod_submissions |
|
||||
| assign | Test assignment 1 | Test | C1 | assign1 | 100 | rubric |
|
||||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I go to "Test assignment 1" advanced grading definition page
|
||||
And I set the following fields to these values:
|
||||
| Name | Assignment 1 rubric |
|
||||
| Description | Rubric test description |
|
||||
And I define the following rubric:
|
||||
| Criterion 1 | Did not try | -11 | Level 12 | 25 | Level 13 | 40 | Level 14 | 50 |
|
||||
| Criterion 2 | Very bad | -20 | Level 22 | 25 | Level 23 | 30 | | |
|
||||
| Criterion 3 | Level 31 | 10 | Level 32 | 20 | | | | |
|
||||
And I press "Save rubric and make it ready"
|
||||
# Grading a student.
|
||||
And I go to "Student 1" "Test assignment 1" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 25 | |
|
||||
| Criterion 2 | 30 | |
|
||||
| Criterion 3 | 10 | |
|
||||
And I save the advanced grading form
|
||||
And I go to "Student 2" "Test assignment 1" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | 25 | |
|
||||
| Criterion 2 | -20 | |
|
||||
| Criterion 3 | 10 | |
|
||||
And I save the advanced grading form
|
||||
And I go to "Student 3" "Test assignment 1" activity advanced grading page
|
||||
And I grade by filling the rubric with:
|
||||
| Criterion 1 | -11 | |
|
||||
| Criterion 2 | -20 | |
|
||||
| Criterion 3 | 10 | |
|
||||
And I save the advanced grading form
|
||||
# Checking that the user grade is correct.
|
||||
And I should see "65.00" in the "student1@example.com" "table_row"
|
||||
And I should see "15.00" in the "student2@example.com" "table_row"
|
||||
And I should see "0.00" in the "student3@example.com" "table_row"
|
||||
And I should not see "-" in the "student3@example.com" "table_row"
|
||||
And I log out
|
Loading…
x
Reference in New Issue
Block a user