Skip to content

Commit

Permalink
MDL-52522 grades: Add rescale grades option for grade categories.
Browse files Browse the repository at this point in the history
When a grade category is overridden, it starts to behave like a regular grade item.
Therefore we need similar behaviour to what was implemented in MDL-48634.
  • Loading branch information
cameorn1730 committed Mar 8, 2016
1 parent 7adc7ef commit fa8e27b
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 8 deletions.
5 changes: 5 additions & 0 deletions grade/edit/tree/category.php
Expand Up @@ -237,6 +237,11 @@

$grade_item->outcomeid = null;

if (!empty($data->grade_item_rescalegrades) && $data->grade_item_rescalegrades == 'yes') {
$grade_item->rescale_grades_keep_percentage($grade_item_copy->grademin, $grade_item_copy->grademax, $grade_item->grademin,
$grade_item->grademax, 'gradebook');
}

// update hiding flag
if ($hiddenuntil) {
$grade_item->set_hidden($hiddenuntil, false);
Expand Down
65 changes: 64 additions & 1 deletion grade/edit/tree/category_form.php
Expand Up @@ -32,7 +32,7 @@ class edit_category_form extends moodleform {
private $aggregation_options = array();

function definition() {
global $CFG, $COURSE, $DB;
global $CFG, $COURSE, $DB, $OUTPUT;
$mform =& $this->_form;

$category = $this->_customdata['current'];
Expand Down Expand Up @@ -104,6 +104,25 @@ function definition() {
$mform->addHelpButton('grade_item_idnumber', 'idnumbermod');
$mform->setType('grade_item_idnumber', PARAM_RAW);

if (!empty($category->id)) {
$gradecategory = grade_category::fetch(array('id' => $category->id));
$gradeitem = $gradecategory->load_grade_item();

// If grades exist set a message so the user knows why they can not alter the grade type or scale.
// We could never change the grade type for external items, so only need to show this for manual grade items.
if ($gradeitem->has_overridden_grades()) {
// Set a message so the user knows why the can not alter the grade type or scale.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$gradesexistmsg = get_string('modgradecategorycantchangegradetyporscalemsg', 'grades');
} else {
$gradesexistmsg = get_string('modgradecategorycantchangegradetypemsg', 'grades');
}
$notification = new \core\output\notification($gradesexistmsg, \core\output\notification::NOTIFY_INFO);
$notification->set_show_closebutton(false);
$mform->addElement('static', 'gradesexistmsg', '', $OUTPUT->render($notification));
}
}

$options = array(GRADE_TYPE_NONE=>get_string('typenone', 'grades'),
GRADE_TYPE_VALUE=>get_string('typevalue', 'grades'),
GRADE_TYPE_SCALE=>get_string('typescale', 'grades'),
Expand Down Expand Up @@ -140,6 +159,14 @@ function definition() {
$mform->disabledIf('grade_item_scaleid', 'grade_item_gradetype', 'noteq', GRADE_TYPE_SCALE);
$mform->disabledIf('grade_item_scaleid', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);

$choices = array();
$choices[''] = get_string('choose');
$choices['no'] = get_string('no');
$choices['yes'] = get_string('yes');
$mform->addElement('select', 'grade_item_rescalegrades', get_string('modgradecategoryrescalegrades', 'grades'), $choices);
$mform->addHelpButton('grade_item_rescalegrades', 'modgradecategoryrescalegrades', 'grades');
$mform->disabledIf('grade_item_rescalegrades', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);

$mform->addElement('text', 'grade_item_grademax', get_string('grademax', 'grades'));
$mform->setType('grade_item_grademax', PARAM_RAW);
$mform->addHelpButton('grade_item_grademax', 'grademax', 'grades');
Expand Down Expand Up @@ -398,6 +425,8 @@ function definition_after_data() {
}
}
}

$mform->removeElement('grade_item_rescalegrades');
}


Expand All @@ -423,6 +452,25 @@ function definition_after_data() {
$mform->removeElement('grade_item_display');
$mform->removeElement('grade_item_decimals');
$mform->hardFreeze('grade_item_scaleid');
// Only show the option to rescale grades on a category if its corresponding grade_item has overridden grade_grades.
} else if ($grade_item->has_overridden_grades()) {
// Can't change the grade type or the scale if there are grades.
$mform->hardFreeze('grade_item_gradetype, grade_item_scaleid');

// If we are using scles then remove the unnecessary rescale and grade fields.
if ($grade_item->gradetype == GRADE_TYPE_SCALE) {
$mform->removeElement('grade_item_rescalegrades');
$mform->removeElement('grade_item_grademax');
if ($mform->elementExists('grade_item_grademin')) {
$mform->removeElement('grade_item_grademin');
}
} else { // Not using scale, so remove it.
$mform->removeElement('grade_item_scaleid');
$mform->disabledIf('grade_item_grademax', 'grade_item_rescalegrades', 'eq', '');
$mform->disabledIf('grade_item_grademin', 'grade_item_rescalegrades', 'eq', '');
}
} else { // Remove the rescale element if there are no grades.
$mform->removeElement('grade_item_rescalegrades');
}

//remove the aggregation coef element if not needed
Expand Down Expand Up @@ -484,6 +532,11 @@ function definition_after_data() {
/// perform extra validation before submission
function validation($data, $files) {
global $COURSE;
$gradeitem = false;
if ($data['id']) {
$gradecategory = grade_category::fetch(array('id' => $data['id']));
$gradeitem = $gradecategory->load_grade_item();
}

$errors = parent::validation($data, $files);

Expand All @@ -501,6 +554,16 @@ function validation($data, $files) {
}
}

if ($data['id'] && $gradeitem->has_overridden_grades()) {
if ($gradeitem->gradetype == GRADE_TYPE_VALUE) {
if (grade_floats_different($data['grade_item_grademin'], $gradeitem->grademin) ||
grade_floats_different($data['grade_item_grademax'], $gradeitem->grademax)) {
if (empty($data['grade_item_rescalegrades'])) {
$errors['grade_item_rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades');
}
}
}
}
return $errors;
}
}
Expand Down
108 changes: 108 additions & 0 deletions grade/tests/behat/grade_category_validation.feature
@@ -0,0 +1,108 @@
@core_grades
Feature: Editing a grade item
In order to ensure validation is provided to the teacher
As a teacher
I need to know why I can not add/edit values on the grade category form

Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "admin"
And I navigate to "Scales" node in "Site administration > Grades"
And I press "Add a new scale"
And I set the following fields to these values:
| Name | ABCDEF |
| Scale | F,E,D,C,B,A |
And I press "Save changes"
And I press "Add a new scale"
And I set the following fields to these values:
| Name | Letter scale |
| Scale | Disappointing, Good, Very good, Excellent |
And I press "Save changes"
And I set the following administration settings values:
| grade_aggregations_visible | Mean of grades,Weighted mean of grades,Simple weighted mean of grades,Mean of grades (with extra credits),Median of grades,Lowest grade,Highest grade,Mode of grades,Natural |
And I log out
And I log in as "teacher1"
And I am on site homepage
And I follow "Course 1"
And I navigate to "Gradebook setup" node in "Course administration"
And I press "Add category"
And I set the following fields to these values:
| Category name | Cat 1 |
| Aggregation | Highest grade |
And I press "Save changes"
And I press "Add grade item"
And I set the following fields to these values:
| Item name | Item 1 |
| Grade category | Cat 1 |
And I press "Save changes"
And I press "Add grade item"
And I set the following fields to these values:
| Item name | Item 2 |
| Grade category | Cat 1 |
And I press "Save changes"

Scenario: Being able to change the grade type, scale and maximum grade for a grade category when there are no overridden grades
Given I click on "Edit" "link" in the "Cat 1" "table_row"
When I click on "Edit settings" "link" in the "Cat 1" "table_row"
Then I should not see "This category has associated grade items which have been overridden. Therefore some grades have already been awarded"
And I set the field "Grade type" to "Scale"
And I press "Save changes"
And I should see "Scale must be selected"
And I set the field "Scale" to "ABCDEF"
And I press "Save changes"
And I should not see "You cannot change the type, as grades already exist for this item"
And I click on "Edit" "link" in the "Cat 1" "table_row"
And I click on "Edit settings" "link" in the "Cat 1" "table_row"
And I should not see "This category has associated grade items which have been overridden. Therefore some grades have already been awarded"
And I set the field "Scale" to "Letter scale"
And I press "Save changes"
And I should not see "You cannot change the scale, as grades already exist for this item"

Scenario: Attempting to change a category item's grade type when overridden grades already exist
Given I navigate to "Grader report" node in "Grade administration"
And I turn editing mode on
And I give the grade "20.00" to the user "Student 1" for the grade item "Cat 1 total"
And I press "Save changes"
And I navigate to "Gradebook setup" node in "Grade administration > Setup"
And I click on "Edit" "link" in the "Cat 1" "table_row"
When I click on "Edit settings" "link" in the "Cat 1" "table_row"
Then I should see "This category has associated grade items which have been overridden. Therefore some grades have already been awarded, so the grade type cannot be changed. If you wish to change the maximum grade, you must first choose whether or not to rescale existing grades."
And "//div[contains(concat(' ', normalize-space(@class), ' '), 'fstatic') and contains(text(), 'Value')]" "xpath_element" should exist

Scenario: Attempting to change a category item's scale when overridden grades already exist
Given I click on "Edit" "link" in the "Cat 1" "table_row"
And I click on "Edit settings" "link" in the "Cat 1" "table_row"
And I set the field "Grade type" to "Scale"
And I set the field "Scale" to "ABCDEF"
And I press "Save changes"
And I navigate to "Grader report" node in "Grade administration"
And I turn editing mode on
And I give the grade "C" to the user "Student 1" for the grade item "Cat 1 total"
And I press "Save changes"
And I navigate to "Gradebook setup" node in "Grade administration > Setup"
And I click on "Edit" "link" in the "Cat 1" "table_row"
When I click on "Edit settings" "link" in the "Cat 1" "table_row"
Then I should see "This category has associated grade items which have been overridden. Therefore some grades have already been awarded, so the grade type and scale cannot be changed."
And "//div[contains(concat(' ', normalize-space(@class), ' '), 'fstatic') and contains(text(), 'ABCDEF')]" "xpath_element" should exist

Scenario: Attempting to change a category item's maximum grade when no rescaling option has been chosen
Given I navigate to "Grader report" node in "Grade administration"
And I turn editing mode on
And I give the grade "20.00" to the user "Student 1" for the grade item "Cat 1 total"
And I press "Save changes"
And I navigate to "Gradebook setup" node in "Grade administration > Setup"
And I click on "Edit" "link" in the "Cat 1" "table_row"
And I click on "Edit settings" "link" in the "Cat 1" "table_row"
And I set the field "Maximum grade" to "50"
When I press "Save changes"
Then I should see "You must choose whether to rescale existing grades or not."
8 changes: 8 additions & 0 deletions lang/en/grades.php
Expand Up @@ -477,6 +477,8 @@
$string['modgradecantchangegradetype'] = 'You cannot change the type, as grades already exist for this item.';
$string['modgradecantchangegradetypemsg'] = 'Some grades have already been awarded, so the grade type cannot be changed. If you wish to change the maximum grade, you must first choose whether or not to rescale existing grades.';
$string['modgradecantchangegradetyporscalemsg'] = 'Some grades have already been awarded, so the grade type and scale cannot be changed.';
$string['modgradecategorycantchangegradetypemsg'] = 'This category has associated grade items which have been overridden. Therefore some grades have already been awarded, so the grade type cannot be changed. If you wish to change the maximum grade, you must first choose whether or not to rescale existing grades.';
$string['modgradecategorycantchangegradetyporscalemsg'] = 'This category has associated grade items which have been overridden. Therefore some grades have already been awarded, so the grade type and scale cannot be changed.';
$string['modgradecantchangescale'] = 'You cannot change the scale, as grades already exist for this item.';
$string['modgradecantchangeratingmaxgrade'] = 'You cannot change the maximum grade when grades already exist for an activity with ratings.';
$string['modgradedonotmodify'] = 'Do not modify existing grades';
Expand All @@ -489,6 +491,12 @@
If this is set to \'Yes\', any existing grades will be rescaled so that the percentage grade remains the same.
For example, if this option is set to \'Yes\', changing the maximum grade on an item from 10 to 20 would cause a grade of 6/10 (60%) to be rescaled to 12/20 (60%). With this option set to \'No\', the grade would change from 6/10 (60%) to 6/20 (30%), requiring manual adjustment of the grade items to ensure correct scores.';
$string['modgradecategoryrescalegrades'] = 'Rescale overridden grades';
$string['modgradecategoryrescalegrades_help'] = 'When changing the maximum grades on a gradebook item you need to specify whether or not this will cause existing percentage grades to change as well.
If this is set to \'Yes\', any existing overridden grades will be rescaled so that the percentage grade remains the same.
For example, if this option is set to \'Yes\', changing the maximum grade on an item from 10 to 20 would cause a grade of 6/10 (60%) to be rescaled to 12/20 (60%). With this option set to \'No\', the grade will remain unchanged, requiring manual adjustment of the grade items to ensure correct scores.';
$string['modgradetype'] = 'Type';
$string['modgradetypenone'] = 'None';
$string['modgradetypepoint'] = 'Point';
Expand Down
30 changes: 23 additions & 7 deletions lib/grade/grade_item.php
Expand Up @@ -368,6 +368,20 @@ public function has_grades() {
return $count > 0;
}

/**
* Check to see if there are existing overridden grades for this grade_item.
*
* @return boolean - true if there are overridden grades for this grade_item.
*/
public function has_overridden_grades() {
global $DB;

$count = $DB->count_records_select('grade_grades',
'itemid = :gradeitemid AND finalgrade IS NOT NULL AND overridden > 0',
array('gradeitemid' => $this->id));
return $count > 0;
}

/**
* Finds and returns all grade_item instances based on params.
*
Expand Down Expand Up @@ -872,13 +886,15 @@ public function rescale_grades_keep_percentage($oldgrademin, $oldgrademax, $newg
// Set this object in the item so it doesn't re-fetch it.
$grade->grade_item = $this;

// Updating the raw grade automatically updates the min/max.
if ($this->is_raw_used()) {
$rawgrade = (($grade->rawgrade - $oldgrademin) * $scale) + $newgrademin;
$this->update_raw_grade(false, $rawgrade, $source, false, FORMAT_MOODLE, null, null, null, $grade);
} else {
$finalgrade = (($grade->finalgrade - $oldgrademin) * $scale) + $newgrademin;
$this->update_final_grade($grade->userid, $finalgrade, $source);
if (!$this->is_category_item() || ($this->is_category_item() && $grade->is_overridden())) {
// Updating the raw grade automatically updates the min/max.
if ($this->is_raw_used()) {
$rawgrade = (($grade->rawgrade - $oldgrademin) * $scale) + $newgrademin;
$this->update_raw_grade(false, $rawgrade, $source, false, FORMAT_MOODLE, null, null, null, $grade);
} else {
$finalgrade = (($grade->finalgrade - $oldgrademin) * $scale) + $newgrademin;
$this->update_final_grade($grade->userid, $finalgrade, $source);
}
}
}
$rs->close();
Expand Down

0 comments on commit fa8e27b

Please sign in to comment.