diff --git a/grade/import/csv/classes/load_data.php b/grade/import/csv/classes/load_data.php index 7d6aeb4fc9838..bc7ad65d46219 100644 --- a/grade/import/csv/classes/load_data.php +++ b/grade/import/csv/classes/load_data.php @@ -149,24 +149,31 @@ protected function raise_limits() { /** * Inserts a record into the grade_import_values table. This also adds common record information. * - * @param object $record The grade record being inserted into the database. + * @param stdClass $record The grade record being inserted into the database. * @param int $studentid The student ID. - * @return bool|int true or insert id on success. Null if the grade value is too high. + * @param grade_item $gradeitem Grade item. + * @return mixed true or insert id on success. Null if the grade value is too high or too low or grade item not exist. */ - protected function insert_grade_record($record, $studentid) { + protected function insert_grade_record(stdClass $record, int $studentid, grade_item $gradeitem): mixed { global $DB, $USER, $CFG; $record->importcode = $this->importcode; $record->userid = $studentid; $record->importer = $USER->id; - // By default the maximum grade is 100. - // If the grade limit has been increased then use the gradepointmax setting. - // Unlimitedgrades allows for scores over 100%. // If the record final grade is set then check that the grade value isn't too high. // Final grade will not be set if we are inserting feedback. - if (!isset($record->finalgrade) || $record->finalgrade <= $CFG->gradepointmax || $CFG->unlimitedgrades) { + $gradepointmaximum = $gradeitem->grademax; + $gradepointminimum = $gradeitem->grademin; + + $finalgradeinrange = + isset($record->finalgrade) && $record->finalgrade <= $gradepointmaximum && $record->finalgrade >= $gradepointminimum; + if (!isset($record->finalgrade) || $finalgradeinrange || $CFG->unlimitedgrades) { return $DB->insert_record('grade_import_values', $record); } else { - $this->cleanup_import(get_string('gradevaluetoobig', 'grades', $CFG->gradepointmax)); + if ($record->finalgrade > $gradepointmaximum) { + $this->cleanup_import(get_string('gradevaluetoobig', 'grades', format_float($gradepointmaximum))); + } else { + $this->cleanup_import(get_string('gradevaluetoosmall', 'grades', format_float($gradepointminimum))); + } return null; } } @@ -572,7 +579,12 @@ public function prepare_import_grade_data($header, $formdata, $csvimport, $cours } } } - $insertid = self::insert_grade_record($newgrade, $this->studentid); + if (isset($newgrade->itemid)) { + $gradeitem = new grade_item(['id' => $newgrade->itemid]); + } else if (isset($newgrade->newgradeitem)) { + $gradeitem = new grade_item(['id' => $newgrade->newgradeitem]); + } + $insertid = isset($gradeitem) ? self::insert_grade_record($newgrade, $this->studentid, $gradeitem) : null; // Check to see if the insert was successful. if (empty($insertid)) { return null; @@ -594,7 +606,7 @@ public function prepare_import_grade_data($header, $formdata, $csvimport, $cours } else { // The grade item for this is not updated. $newfeedback->importonlyfeedback = true; - $insertid = self::insert_grade_record($newfeedback, $this->studentid); + $insertid = self::insert_grade_record($newfeedback, $this->studentid, new grade_item(['id' => $newfeedback->itemid])); // Check to see if the insert was successful. if (empty($insertid)) { return null; diff --git a/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php b/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php index ad80e519ed662..814102e19f61c 100644 --- a/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php +++ b/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php @@ -27,10 +27,11 @@ class phpunit_gradeimport_csv_load_data extends gradeimport_csv_load_data { * * @param object $record * @param int $studentid + * @param grade_item $gradeitem */ - public function test_insert_grade_record($record, $studentid) { + public function test_insert_grade_record($record, $studentid, grade_item $gradeitem) { $this->importcode = 00001; - $this->insert_grade_record($record, $studentid); + $this->insert_grade_record($record, $studentid, $gradeitem); } /** diff --git a/grade/import/csv/tests/load_data_test.php b/grade/import/csv/tests/load_data_test.php index 391b8b02e7d2b..4b20f4fb543f1 100644 --- a/grade/import/csv/tests/load_data_test.php +++ b/grade/import/csv/tests/load_data_test.php @@ -187,7 +187,8 @@ public function test_insert_grade_record() { $record->feedback = 'Some test feedback'; $testobject = new \phpunit_gradeimport_csv_load_data(); - $testobject->test_insert_grade_record($record, $user->id); + + $testobject->test_insert_grade_record($record, $user->id, new \grade_item()); $gradeimportvalues = $DB->get_records('grade_import_values'); // Get the insert id. diff --git a/grade/tests/behat/grade_import.feature b/grade/tests/behat/grade_import.feature new file mode 100644 index 0000000000000..a5ea6f6feb3a7 --- /dev/null +++ b/grade/tests/behat/grade_import.feature @@ -0,0 +1,68 @@ +@core @core_grades @javascript @_file_upload +Feature: An admin can import grades into gradebook using a CSV file + In order to import grades using a CSV file + As a teacher + I need to be able to upload a CSV file and see uploaded grades in gradebook + + Background: + Given the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | topics | + And 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 "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + And the following "grade item" exists: + | course | C1 | + | itemname | Manual item 1 | + | grademin | 10 | + | grademax | 500 | + And the following "grade grades" exist: + | gradeitem | user | grade | + | Manual item 1 | student1 | 50.00 | + | Manual item 1 | student2 | 50.00 | + | Manual item 1 | student3 | 50.00 | + + @javascript + Scenario: Max grade of grade item is respected when importing grades + Given I am on the "Course 1" "Course" page logged in as "teacher1" + And I navigate to "More > Import" in the course gradebook + And I upload "grade/tests/fixtures/grade_import_grademax.csv" file to "File" filemanager + And I click on "Upload grades" "button" + And I set the field "Map from" to "Email address" + And I set the field "Map to" to "Email address" + And I set the field "Manual item 1" to "Manual item 1" + And I click on "Upload grades" "button" + And I should see "One of the grade values is larger than the allowed grade maximum of 500.0" + And I should see "Import failed. No data was imported." + And I click on "Continue" "button" + And I upload "grade/tests/fixtures/grade_import_grademin.csv" file to "File" filemanager + And I click on "Upload grades" "button" + And I set the field "Map from" to "Email address" + And I set the field "Map to" to "Email address" + And I set the field "Manual item 1" to "Manual item 1" + And I click on "Upload grades" "button" + And I should see "One of the grade values is smaller than the allowed grade minimum of 10.0" + And I should see "Import failed. No data was imported." + And I click on "Continue" "button" + When I upload "grade/tests/fixtures/grade_import.csv" file to "File" filemanager + And I click on "Upload grades" "button" + And I set the field "Map from" to "Email address" + And I set the field "Map to" to "Email address" + And I set the field "Manual item 1" to "Manual item 1" + And I click on "Upload grades" "button" + And I should see "Grade import success" + And I click on "Continue" "button" + Then the following should exist in the "user-grades" table: + | -1- | -1- | -3- | -4- | + | Student 1 | student1@example.com | 400.00 | 400.00 | + | Student 2 | student2@example.com | 50.00 | 50.00 | + | Student 3 | student3@example.com | 50.00 | 50.00 | diff --git a/grade/tests/fixtures/grade_import.csv b/grade/tests/fixtures/grade_import.csv new file mode 100644 index 0000000000000..aff7e1d513aaa --- /dev/null +++ b/grade/tests/fixtures/grade_import.csv @@ -0,0 +1,2 @@ +First name,Last name,Email address,Manual item 1 +Student,1,student1@example.com,400 diff --git a/grade/tests/fixtures/grade_import_grademax.csv b/grade/tests/fixtures/grade_import_grademax.csv new file mode 100644 index 0000000000000..60817e30c82c8 --- /dev/null +++ b/grade/tests/fixtures/grade_import_grademax.csv @@ -0,0 +1,2 @@ +First name,Last name,Email address,Manual item 1 +Student,1,student1@example.com,800 diff --git a/grade/tests/fixtures/grade_import_grademin.csv b/grade/tests/fixtures/grade_import_grademin.csv new file mode 100644 index 0000000000000..47417103c9b6c --- /dev/null +++ b/grade/tests/fixtures/grade_import_grademin.csv @@ -0,0 +1,2 @@ +First name,Last name,Email address,Manual item 1 +Student,1,student1@example.com,5 diff --git a/grade/upgrade.txt b/grade/upgrade.txt index 7e4d0d076e2dc..c6c37b9c0392f 100644 --- a/grade/upgrade.txt +++ b/grade/upgrade.txt @@ -1,6 +1,10 @@ This file describes API changes in /grade/* ; Information provided here is intended especially for developers. +=== 4.2.4 === +* The function gradeimport_csv_load_data::insert_grade_record() now has extra parameter $gradeitem to carry over the grade item related info + for validation. + === 4.2.3 === * The grade `itemname` property contained in the return structure of the following external methods is now PARAM_CLEANHTML: - `core_grades_get_gradeitems` diff --git a/lang/en/grades.php b/lang/en/grades.php index 153153d2123e4..c6cb399162040 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -363,6 +363,7 @@ Only value and scale grade types may be aggregated. The grade type for an activity-based grade item is set on the activity settings page.'; $string['gradevaluetoobig'] = 'One of the grade values is larger than the allowed grade maximum of {$a}'; +$string['gradevaluetoosmall'] = 'One of the grade values is smaller than the allowed grade minimum of {$a}'; $string['gradeview'] = 'View grade'; $string['gradewasmodifiedduringediting'] = 'The grade entered for {$a->itemname} for {$a->username} was ignored because it was more recently updated by someone else.'; $string['gradeweighthelp'] = 'Grade weight help';