352365e700c659764c33b0686b93d3eb1b2a4abf
[moodle.git] / grade / import / csv / tests / load_data_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Unit tests for the class in load_data.php
19  *
20  * @package    gradeimport_csv
21  * @category   phpunit
22  * @copyright  2014 Adrian Greeve
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php');
30 require_once($CFG->libdir . '/csvlib.class.php');
31 require_once($CFG->libdir . '/grade/grade_item.php');
32 require_once($CFG->libdir . '/grade/tests/fixtures/lib.php');
34 /**
35  * Unit tests for lib.php
36  *
37  * @package    gradeimport_csv
38  * @copyright  2014 Adrian Greeve
39  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 class gradeimport_csv_load_data_testcase extends grade_base_testcase {
43     /** @var string $oktext Text to be imported. This data should have no issues being imported. */
44     protected $oktext = '"First name",Surname,"ID number",Institution,Department,"Email address","Assignment: Assignment for grape group", "Feedback: Assignment for grape group","Assignment: Second new grade item","Course total"
45 Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,"We welcome feedback",,56.00
46 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,,45.0,75.00';
48     /** @var string $badtext Text to be imported. This data has an extra column and should not succeed in being imported. */
49     protected $badtext = '"First name",Surname,"ID number",Institution,Department,"Email address","Assignment: Assignment for grape group","Course total"
50 Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,56.00,78.00
51 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,75.00';
53     /** @var string $csvtext CSV data to be imported with Last download from this course column. */
54     protected $csvtext = '"First name",Surname,"ID number",Institution,Department,"Email address","Assignment: Assignment for grape group", "Feedback: Assignment for grape group","Course total","Last downloaded from this course"
55 Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,"We welcome feedback",56.00,{exportdate}
56 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,,75.00,{exportdate}';
58     /** @var int $iid Import ID. */
59     protected $iid;
61     /** @var object $csvimport a csv_import_reader object that handles the csv import. */
62     protected $csvimport;
64     /** @var array $columns The first row of the csv file. These are the columns of the import file.*/
65     protected $columns;
67     public function tearDown() {
68         $this->csvimport = null;
69     }
71     /**
72      * Load up the above text through the csv import.
73      *
74      * @param string $content Text to be imported into the gradebook.
75      * @return array All text separated by commas now in an array.
76      */
77     protected function csv_load($content) {
78         // Import the csv strings.
79         $this->iid = csv_import_reader::get_new_iid('grade');
80         $this->csvimport = new csv_import_reader($this->iid, 'grade');
82         $this->csvimport->load_csv_content($content, 'utf8', 'comma');
83         $this->columns = $this->csvimport->get_columns();
85         $this->csvimport->init();
86         while ($line = $this->csvimport->next()) {
87             $testarray[] = $line;
88         }
90         return $testarray;
91     }
93     /**
94      * Test loading data and returning preview content.
95      */
96     public function test_load_csv_content() {
97         $encoding = 'utf8';
98         $separator = 'comma';
99         $previewrows = 5;
100         $csvpreview = new phpunit_gradeimport_csv_load_data();
101         $csvpreview->load_csv_content($this->oktext, $encoding, $separator, $previewrows);
103         $expecteddata = array(array(
104                 'Anne',
105                 'Able',
106                 '',
107                 'Moodle HQ',
108                 'Rock on!',
109                 'student7@example.com',
110                 56.00,
111                 'We welcome feedback',
112                 '',
113                 56.00
114             ),
115             array(
116                 'Bobby',
117                 'Bunce',
118                 '',
119                 'Moodle HQ',
120                 'Rock on!',
121                 'student5@example.com',
122                 75.00,
123                 '',
124                 45.0,
125                 75.00
126             )
127         );
129         $expectedheaders = array(
130             'First name',
131             'Surname',
132             'ID number',
133             'Institution',
134             'Department',
135             'Email address',
136             'Assignment: Assignment for grape group',
137             'Feedback: Assignment for grape group',
138             'Assignment: Second new grade item',
139             'Course total'
140         );
141         // Check that general data is returned as expected.
142         $this->assertEquals($csvpreview->get_previewdata(), $expecteddata);
143         // Check that headers are returned as expected.
144         $this->assertEquals($csvpreview->get_headers(), $expectedheaders);
146         // Check that errors are being recorded.
147         $csvpreview = new phpunit_gradeimport_csv_load_data();
148         $csvpreview->load_csv_content($this->badtext, $encoding, $separator, $previewrows);
149         // Columns shouldn't match.
150         $this->assertEquals($csvpreview->get_error(), get_string('csvweirdcolumns', 'error'));
151     }
153     /**
154      * Test fetching grade items for the course.
155      */
156     public function test_fetch_grade_items() {
158         $gradeitemsarray = grade_item::fetch_all(array('courseid' => $this->courseid));
159         $gradeitems = phpunit_gradeimport_csv_load_data::fetch_grade_items($this->courseid);
161         // Make sure that each grade item is located in the gradeitemsarray.
162         foreach ($gradeitems as $key => $gradeitem) {
163             $this->assertArrayHasKey($key, $gradeitemsarray);
164         }
166         // Get the key for a specific grade item.
167         $quizkey = null;
168         foreach ($gradeitemsarray as $key => $value) {
169             if ($value->itemname == "Quiz grade item") {
170                 $quizkey = $key;
171             }
172         }
174         // Expected modified item name.
175         $testitemname = get_string('modulename', $gradeitemsarray[$quizkey]->itemmodule) . ': ' .
176                 $gradeitemsarray[$quizkey]->itemname;
177         // Check that an item that is a module, is concatenated properly.
178         $this->assertEquals($testitemname, $gradeitems[$quizkey]);
179     }
181     /**
182      * Test the inserting of grade record data.
183      */
184     public function test_insert_grade_record() {
185         global $DB, $USER;
187         $user = $this->getDataGenerator()->create_user();
188         $this->setAdminUser();
190         $record = new stdClass();
191         $record->itemid = 4;
192         $record->newgradeitem = 25;
193         $record->finalgrade = 62.00;
194         $record->feedback = 'Some test feedback';
196         $testobject = new phpunit_gradeimport_csv_load_data();
197         $testobject->test_insert_grade_record($record, $user->id);
199         $gradeimportvalues = $DB->get_records('grade_import_values');
200         // Get the insert id.
201         $key = key($gradeimportvalues);
203         $testarray = array();
204         $testarray[$key] = new stdClass();
205         $testarray[$key]->id = $key;
206         $testarray[$key]->itemid = $record->itemid;
207         $testarray[$key]->newgradeitem = $record->newgradeitem;
208         $testarray[$key]->userid = $user->id;
209         $testarray[$key]->finalgrade = $record->finalgrade;
210         $testarray[$key]->feedback = $record->feedback;
211         $testarray[$key]->importcode = $testobject->get_importcode();
212         $testarray[$key]->importer = $USER->id;
213         $testarray[$key]->importonlyfeedback = 0;
215         // Check that the record was inserted into the database.
216         $this->assertEquals($gradeimportvalues, $testarray);
217     }
219     /**
220      * Test preparing a new grade item for import into the gradebook.
221      */
222     public function test_import_new_grade_item() {
223         global $DB;
225         $this->setAdminUser();
226         $this->csv_load($this->oktext);
227         $columns = $this->columns;
229         // The assignment is item 6.
230         $key = 6;
231         $testobject = new phpunit_gradeimport_csv_load_data();
233         // Key for this assessment.
234         $this->csvimport->init();
235         $testarray = array();
236         while ($line = $this->csvimport->next()) {
237             $testarray[] = $testobject->test_import_new_grade_item($columns, $key, $line[$key]);
238         }
240         // Query the database and check how many results were inserted.
241         $newgradeimportitems = $DB->get_records('grade_import_newitem');
242         $this->assertEquals(count($testarray), count($newgradeimportitems));
243     }
245     /**
246      * Check that the user matches a user in the system.
247      */
248     public function test_check_user_exists() {
250         // Need to add one of the users into the system.
251         $user = new stdClass();
252         $user->firstname = 'Anne';
253         $user->lastname = 'Able';
254         $user->email = 'student7@example.com';
255         $userdetail = $this->getDataGenerator()->create_user($user);
257         $testobject = new phpunit_gradeimport_csv_load_data();
259         $testarray = $this->csv_load($this->oktext);
261         $userfields = array('field' => 'email', 'label' => 'Email address');
262         // If the user exists then the user id is returned.
263         $userid = $testobject->test_check_user_exists($testarray[0][5] , $userfields);
264         // Check that the user id returned matches with the user that we created.
265         $this->assertEquals($userid, $userdetail->id);
267         // Check for failure.
268         // Try for an exception.
269         $userfields = array('field' => 'id', 'label' => 'userid');
270         $userid = $testobject->test_check_user_exists($testarray[0][0], $userfields);
271         // Check that the userid is null.
272         $this->assertNull($userid);
274         // Expected error message.
275         $mappingobject = new stdClass();
276         $mappingobject->field = $userfields['label'];
277         $mappingobject->value = $testarray[0][0];
278         $expectederrormessage = get_string('usermappingerror', 'grades', $mappingobject);
279         // Check that expected error message and actual message match.
280         $gradebookerrors = $testobject->get_gradebookerrors();
281         $this->assertEquals($expectederrormessage, $gradebookerrors[0]);
283         // The field mapping is correct, but the student does not exist.
284         $userid = $testobject->test_check_user_exists($testarray[1][5], $userfields);
285         // Check that the userid is null.
286         $this->assertNull($userid);
288         // Expected error message.
289         $mappingobject = new stdClass();
290         $mappingobject->field = $userfields['label'];
291         $mappingobject->value = $testarray[1][5];
292         $expectederrormessage = get_string('usermappingerror', 'grades', $mappingobject);
293         // Check that expected error message and actual message match.
294         $gradebookerrors = $testobject->get_gradebookerrors();
295         // This is the second error in the array of gradebook errors.
296         $this->assertEquals($expectederrormessage, $gradebookerrors[1]);
297     }
299     /**
300      * Test preparing feedback for inserting / updating into the gradebook.
301      */
302     public function test_create_feedback() {
304         $testarray = $this->csv_load($this->oktext);
305         $testobject = new phpunit_gradeimport_csv_load_data();
307         // Try to insert some feedback for an assessment.
308         $feedback = $testobject->test_create_feedback($this->courseid, 1, $testarray[0][7]);
310         // Expected result.
311         $expectedfeedback = array('itemid' => 1, 'feedback' => $testarray[0][7]);
312         $this->assertEquals((array)$feedback, $expectedfeedback);
313     }
315     /**
316      * Test preparing grade_items for upgrading into the gradebook.
317      */
318     public function test_update_grade_item() {
320         $testarray = $this->csv_load($this->oktext);
321         $testobject = new phpunit_gradeimport_csv_load_data();
323         // We're not using scales so no to this option.
324         $verbosescales = 0;
325         // Map and key are to retrieve the grade_item that we are updating.
326         $map = array(1);
327         $key = 0;
328         // We return the new grade array for saving.
329         $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, $testarray[0][6]);
331         $expectedresult = array();
332         $expectedresult[0] = new stdClass();
333         $expectedresult[0]->itemid = 1;
334         $expectedresult[0]->finalgrade = $testarray[0][6];
336         $this->assertEquals($newgrades, $expectedresult);
338         // Try sending a bad grade value (A letter instead of a float / int).
339         $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, 'A');
340         // The $newgrades variable should be null.
341         $this->assertNull($newgrades);
342         $expectederrormessage = get_string('badgrade', 'grades');
343         // Check that the error message is what we expect.
344         $gradebookerrors = $testobject->get_gradebookerrors();
345         $this->assertEquals($expectederrormessage, $gradebookerrors[0]);
346     }
348     /**
349      * Test importing data and mapping it with items in the course.
350      */
351     public function test_map_user_data_with_value() {
352         // Need to add one of the users into the system.
353         $user = new stdClass();
354         $user->firstname = 'Anne';
355         $user->lastname = 'Able';
356         $user->email = 'student7@example.com';
357         $userdetail = $this->getDataGenerator()->create_user($user);
359         $testarray = $this->csv_load($this->oktext);
360         $testobject = new phpunit_gradeimport_csv_load_data();
362         // We're not using scales so no to this option.
363         $verbosescales = 0;
364         // Map and key are to retrieve the grade_item that we are updating.
365         $map = array(1);
366         $key = 0;
368         // Test new user mapping. This should return the user id if there were no problems.
369         $userid = $testobject->test_map_user_data_with_value('useremail', $testarray[0][5], $this->columns, $map, $key,
370                 $this->courseid, $map[$key], $verbosescales);
371         $this->assertEquals($userid, $userdetail->id);
373         $newgrades = $testobject->test_map_user_data_with_value('new', $testarray[0][6], $this->columns, $map, $key,
374                 $this->courseid, $map[$key], $verbosescales);
375         // Check that the final grade is the same as the one inserted.
376         $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade);
378         $newgrades = $testobject->test_map_user_data_with_value('new', $testarray[0][8], $this->columns, $map, $key,
379                 $this->courseid, $map[$key], $verbosescales);
380         // Check that the final grade is the same as the one inserted.
381         // The testobject should now contain 2 new grade items.
382         $this->assertEquals(2, count($newgrades));
383         // Because this grade item is empty, the value for final grade should be null.
384         $this->assertNull($newgrades[1]->finalgrade);
386         $feedback = $testobject->test_map_user_data_with_value('feedback', $testarray[0][7], $this->columns, $map, $key,
387                 $this->courseid, $map[$key], $verbosescales);
388         // Expected result.
389         $resultarray = array();
390         $resultarray[0] = new stdClass();
391         $resultarray[0]->itemid = 1;
392         $resultarray[0]->feedback = $testarray[0][7];
393         $this->assertEquals($feedback, $resultarray);
395         // Default behaviour (update a grade item).
396         $newgrades = $testobject->test_map_user_data_with_value('default', $testarray[0][6], $this->columns, $map, $key,
397                 $this->courseid, $map[$key], $verbosescales);
398         $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade);
399     }
401     /**
402      * Test importing data into the gradebook.
403      */
404     public function test_prepare_import_grade_data() {
405         global $DB;
407         // Need to add one of the users into the system.
408         $user = new stdClass();
409         $user->firstname = 'Anne';
410         $user->lastname = 'Able';
411         $user->email = 'student7@example.com';
412         // Insert user 1.
413         $this->getDataGenerator()->create_user($user);
414         $user = new stdClass();
415         $user->firstname = 'Bobby';
416         $user->lastname = 'Bunce';
417         $user->email = 'student5@example.com';
418         // Insert user 2.
419         $this->getDataGenerator()->create_user($user);
421         $this->csv_load($this->oktext);
423         $importcode = 007;
424         $verbosescales = 0;
426         // Form data object.
427         $formdata = new stdClass();
428         $formdata->mapfrom = 5;
429         $formdata->mapto = 'useremail';
430         $formdata->mapping_0 = 0;
431         $formdata->mapping_1 = 0;
432         $formdata->mapping_2 = 0;
433         $formdata->mapping_3 = 0;
434         $formdata->mapping_4 = 0;
435         $formdata->mapping_5 = 0;
436         $formdata->mapping_6 = 'new';
437         $formdata->mapping_7 = 'feedback_2';
438         $formdata->mapping_8 = 0;
439         $formdata->mapping_9 = 0;
440         $formdata->map = 1;
441         $formdata->id = 2;
442         $formdata->iid = $this->iid;
443         $formdata->importcode = $importcode;
444         $formdata->forceimport = false;
446         // Blam go time.
447         $testobject = new phpunit_gradeimport_csv_load_data();
448         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '',
449                 $verbosescales);
450         // If everything inserted properly then this should be true.
451         $this->assertTrue($dataloaded);
452     }
454     /*
455      * Test importing csv data into the gradebook using "Last downloaded from this course" column and force import option.
456      */
457     public function test_force_import_option () {
459         // Need to add users into the system.
460         $user = new stdClass();
461         $user->firstname = 'Anne';
462         $user->lastname = 'Able';
463         $user->email = 'student7@example.com';
464         $user->id_number = 1;
465         $user1 = $this->getDataGenerator()->create_user($user);
466         $user = new stdClass();
467         $user->firstname = 'Bobby';
468         $user->lastname = 'Bunce';
469         $user->email = 'student5@example.com';
470         $user->id_number = 2;
471         $user2 = $this->getDataGenerator()->create_user($user);
473         // Create a new grade item.
474         $params = array(
475             'itemtype'  => 'manual',
476             'itemname'  => 'Grade item 1',
477             'gradetype' => GRADE_TYPE_VALUE,
478             'courseid'  => $this->courseid
479         );
480         $gradeitem = new grade_item($params, false);
481         $gradeitemid = $gradeitem->insert();
483         $importcode = 001;
484         $verbosescales = 0;
486         // Form data object.
487         $formdata = new stdClass();
488         $formdata->mapfrom = 5;
489         $formdata->mapto = 'useremail';
490         $formdata->mapping_0 = 0;
491         $formdata->mapping_1 = 0;
492         $formdata->mapping_2 = 0;
493         $formdata->mapping_3 = 0;
494         $formdata->mapping_4 = 0;
495         $formdata->mapping_5 = 0;
496         $formdata->mapping_6 = $gradeitemid;
497         $formdata->mapping_7 = 'feedback_2';
498         $formdata->mapping_8 = 0;
499         $formdata->mapping_9 = 0;
500         $formdata->map = 1;
501         $formdata->id = 2;
502         $formdata->iid = $this->iid;
503         $formdata->importcode = $importcode;
504         $formdata->forceimport = false;
506         // Add last download from this course column to csv content.
507         $exportdate = time();
508         $newcsvdata = str_replace('{exportdate}', $exportdate, $this->csvtext);
509         $this->csv_load($newcsvdata);
510         $testobject = new phpunit_gradeimport_csv_load_data();
511         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
512                 $this->courseid, '', '', $verbosescales);
513         $this->assertTrue($dataloaded);
515         // We must update the last modified date.
516         grade_import_commit($this->courseid, $importcode, false, false);
518         // Test using force import disabled and a date in the past.
519         $pastdate = strtotime('-1 day', time());
520         $newcsvdata = str_replace('{exportdate}', $pastdate, $this->csvtext);
521         $this->csv_load($newcsvdata);
522         $testobject = new phpunit_gradeimport_csv_load_data();
523         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
524                 $this->courseid, '', '', $verbosescales);
525         $this->assertFalse($dataloaded);
526         $errors = $testobject->get_gradebookerrors();
527         $this->assertEquals($errors[0], get_string('gradealreadyupdated', 'grades', fullname($user1)));
529         // Test using force import enabled and a date in the past.
530         $formdata->forceimport = true;
531         $testobject = new phpunit_gradeimport_csv_load_data();
532         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
533                 $this->courseid, '', '', $verbosescales);
534         $this->assertTrue($dataloaded);
536         // Test importing using an old exported file (2 years ago).
537         $formdata->forceimport = false;
538         $twoyearsago = strtotime('-2 year', time());
539         $newcsvdata = str_replace('{exportdate}', $twoyearsago, $this->csvtext);
540         $this->csv_load($newcsvdata);
541         $testobject = new phpunit_gradeimport_csv_load_data();
542         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
543                 $this->courseid, '', '', $verbosescales);
544         $this->assertFalse($dataloaded);
545         $errors = $testobject->get_gradebookerrors();
546         $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades'));
548         // Test importing using invalid exported date.
549         $baddate = '0123A56B89';
550         $newcsvdata = str_replace('{exportdate}', $baddate, $this->csvtext);
551         $this->csv_load($newcsvdata);
552         $formdata->mapping_6 = $gradeitemid;
553         $testobject = new phpunit_gradeimport_csv_load_data();
554         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
555                 $this->courseid, '', '', $verbosescales);
556         $this->assertFalse($dataloaded);
557         $errors = $testobject->get_gradebookerrors();
558         $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades'));
560         // Test importing using date in the future.
561         $oneyearahead = strtotime('+1 year', time());
562         $oldcsv = str_replace('{exportdate}', $oneyearahead, $this->csvtext);
563         $this->csv_load($oldcsv);
564         $formdata->mapping_6 = $gradeitemid;
565         $testobject = new phpunit_gradeimport_csv_load_data();
566         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
567             $this->courseid, '', '', $verbosescales);
568         $this->assertFalse($dataloaded);
569         $errors = $testobject->get_gradebookerrors();
570         $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades'));
571     }