Merge branch 'MDL-46599-master' of git://github.com/lameze/moodle
[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","Course total"
45 Anne,Able,,"Moodle HQ","Rock on!",student7@mail.com,56.00,"We welcome feedback",56.00
46 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@mail.com,75.00,,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@mail.com,56.00,56.00,78.00
51 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@mail.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@mail.com,56.00,"We welcome feedback",56.00,{exportdate}
56 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@mail.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@mail.com',
110                 56.00,
111                 'We welcome feedback',
112                 56.00
113             ),
114             array(
115                 'Bobby',
116                 'Bunce',
117                 '',
118                 'Moodle HQ',
119                 'Rock on!',
120                 'student5@mail.com',
121                 75.00,
122                 '',
123                 75.00
124             )
125         );
127         $expectedheaders = array(
128             'First name',
129             'Surname',
130             'ID number',
131             'Institution',
132             'Department',
133             'Email address',
134             'Assignment: Assignment for grape group',
135             'Feedback: Assignment for grape group',
136             'Course total'
137         );
138         // Check that general data is returned as expected.
139         $this->assertEquals($csvpreview->get_previewdata(), $expecteddata);
140         // Check that headers are returned as expected.
141         $this->assertEquals($csvpreview->get_headers(), $expectedheaders);
143         // Check that errors are being recorded.
144         $csvpreview = new phpunit_gradeimport_csv_load_data();
145         $csvpreview->load_csv_content($this->badtext, $encoding, $separator, $previewrows);
146         // Columns shouldn't match.
147         $this->assertEquals($csvpreview->get_error(), get_string('csvweirdcolumns', 'error'));
148     }
150     /**
151      * Test fetching grade items for the course.
152      */
153     public function test_fetch_grade_items() {
155         $gradeitemsarray = grade_item::fetch_all(array('courseid' => $this->courseid));
156         $gradeitems = phpunit_gradeimport_csv_load_data::fetch_grade_items($this->courseid);
158         // Make sure that each grade item is located in the gradeitemsarray.
159         foreach ($gradeitems as $key => $gradeitem) {
160             $this->assertArrayHasKey($key, $gradeitemsarray);
161         }
163         // Get the key for a specific grade item.
164         $quizkey = null;
165         foreach ($gradeitemsarray as $key => $value) {
166             if ($value->itemname == "Quiz grade item") {
167                 $quizkey = $key;
168             }
169         }
171         // Expected modified item name.
172         $testitemname = get_string('modulename', $gradeitemsarray[$quizkey]->itemmodule) . ': ' .
173                 $gradeitemsarray[$quizkey]->itemname;
174         // Check that an item that is a module, is concatenated properly.
175         $this->assertEquals($testitemname, $gradeitems[$quizkey]);
176     }
178     /**
179      * Test the inserting of grade record data.
180      */
181     public function test_insert_grade_record() {
182         global $DB, $USER;
184         $user = $this->getDataGenerator()->create_user();
185         $this->setAdminUser();
187         $record = new stdClass();
188         $record->itemid = 4;
189         $record->newgradeitem = 25;
190         $record->finalgrade = 62.00;
191         $record->feedback = 'Some test feedback';
193         $testobject = new phpunit_gradeimport_csv_load_data();
194         $testobject->test_insert_grade_record($record, $user->id);
196         $gradeimportvalues = $DB->get_records('grade_import_values');
197         // Get the insert id.
198         $key = key($gradeimportvalues);
200         $testarray = array();
201         $testarray[$key] = new stdClass();
202         $testarray[$key]->id = $key;
203         $testarray[$key]->itemid = $record->itemid;
204         $testarray[$key]->newgradeitem = $record->newgradeitem;
205         $testarray[$key]->userid = $user->id;
206         $testarray[$key]->finalgrade = $record->finalgrade;
207         $testarray[$key]->feedback = $record->feedback;
208         $testarray[$key]->importcode = $testobject->get_importcode();
209         $testarray[$key]->importer = $USER->id;
211         // Check that the record was inserted into the database.
212         $this->assertEquals($gradeimportvalues, $testarray);
213     }
215     /**
216      * Test preparing a new grade item for import into the gradebook.
217      */
218     public function test_import_new_grade_item() {
219         global $DB;
221         $this->setAdminUser();
222         $this->csv_load($this->oktext);
223         $columns = $this->columns;
225         // The assignment is item 6.
226         $key = 6;
227         $testobject = new phpunit_gradeimport_csv_load_data();
229         // Key for this assessment.
230         $this->csvimport->init();
231         $testarray = array();
232         while ($line = $this->csvimport->next()) {
233             $testarray[] = $testobject->test_import_new_grade_item($columns, $key, $line[$key]);
234         }
236         // Query the database and check how many results were inserted.
237         $newgradeimportitems = $DB->get_records('grade_import_newitem');
238         $this->assertEquals(count($testarray), count($newgradeimportitems));
239     }
241     /**
242      * Check that the user matches a user in the system.
243      */
244     public function test_check_user_exists() {
246         // Need to add one of the users into the system.
247         $user = new stdClass();
248         $user->firstname = 'Anne';
249         $user->lastname = 'Able';
250         $user->email = 'student7@mail.com';
251         $userdetail = $this->getDataGenerator()->create_user($user);
253         $testobject = new phpunit_gradeimport_csv_load_data();
255         $testarray = $this->csv_load($this->oktext);
257         $userfields = array('field' => 'email', 'label' => 'Email address');
258         // If the user exists then the user id is returned.
259         $userid = $testobject->test_check_user_exists($testarray[0][5] , $userfields);
260         // Check that the user id returned matches with the user that we created.
261         $this->assertEquals($userid, $userdetail->id);
263         // Check for failure.
264         // Try for an exception.
265         $userfields = array('field' => 'id', 'label' => 'userid');
266         $userid = $testobject->test_check_user_exists($testarray[0][0], $userfields);
267         // Check that the userid is null.
268         $this->assertNull($userid);
270         // Expected error message.
271         $mappingobject = new stdClass();
272         $mappingobject->field = $userfields['label'];
273         $mappingobject->value = $testarray[0][0];
274         $expectederrormessage = get_string('usermappingerror', 'grades', $mappingobject);
275         // Check that expected error message and actual message match.
276         $gradebookerrors = $testobject->get_gradebookerrors();
277         $this->assertEquals($expectederrormessage, $gradebookerrors[0]);
279         // The field mapping is correct, but the student does not exist.
280         $userid = $testobject->test_check_user_exists($testarray[1][5], $userfields);
281         // Check that the userid is null.
282         $this->assertNull($userid);
284         // Expected error message.
285         $mappingobject = new stdClass();
286         $mappingobject->field = $userfields['label'];
287         $mappingobject->value = $testarray[1][5];
288         $expectederrormessage = get_string('usermappingerror', 'grades', $mappingobject);
289         // Check that expected error message and actual message match.
290         $gradebookerrors = $testobject->get_gradebookerrors();
291         // This is the second error in the array of gradebook errors.
292         $this->assertEquals($expectederrormessage, $gradebookerrors[1]);
293     }
295     /**
296      * Test preparing feedback for inserting / updating into the gradebook.
297      */
298     public function test_create_feedback() {
300         $testarray = $this->csv_load($this->oktext);
301         $testobject = new phpunit_gradeimport_csv_load_data();
303         // Try to insert some feedback for an assessment.
304         $feedback = $testobject->test_create_feedback($this->courseid, 1, $testarray[0][7]);
306         // Expected result.
307         $expectedfeedback = array('itemid' => 1, 'feedback' => $testarray[0][7]);
308         $this->assertEquals((array)$feedback, $expectedfeedback);
309     }
311     /**
312      * Test preparing grade_items for upgrading into the gradebook.
313      */
314     public function test_update_grade_item() {
316         $testarray = $this->csv_load($this->oktext);
317         $testobject = new phpunit_gradeimport_csv_load_data();
319         // We're not using scales so no to this option.
320         $verbosescales = 0;
321         // Map and key are to retrieve the grade_item that we are updating.
322         $map = array(1);
323         $key = 0;
324         // We return the new grade array for saving.
325         $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, $testarray[0][6]);
327         $expectedresult = array();
328         $expectedresult[0] = new stdClass();
329         $expectedresult[0]->itemid = 1;
330         $expectedresult[0]->finalgrade = $testarray[0][6];
332         $this->assertEquals($newgrades, $expectedresult);
334         // Try sending a bad grade value (A letter instead of a float / int).
335         $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, 'A');
336         // The $newgrades variable should be null.
337         $this->assertNull($newgrades);
338         $expectederrormessage = get_string('badgrade', 'grades');
339         // Check that the error message is what we expect.
340         $gradebookerrors = $testobject->get_gradebookerrors();
341         $this->assertEquals($expectederrormessage, $gradebookerrors[0]);
342     }
344     /**
345      * Test importing data and mapping it with items in the course.
346      */
347     public function test_map_user_data_with_value() {
348         // Need to add one of the users into the system.
349         $user = new stdClass();
350         $user->firstname = 'Anne';
351         $user->lastname = 'Able';
352         $user->email = 'student7@mail.com';
353         $userdetail = $this->getDataGenerator()->create_user($user);
355         $testarray = $this->csv_load($this->oktext);
356         $testobject = new phpunit_gradeimport_csv_load_data();
358         // We're not using scales so no to this option.
359         $verbosescales = 0;
360         // Map and key are to retrieve the grade_item that we are updating.
361         $map = array(1);
362         $key = 0;
364         // Test new user mapping. This should return the user id if there were no problems.
365         $userid = $testobject->test_map_user_data_with_value('useremail', $testarray[0][5], $this->columns, $map, $key,
366                 $this->courseid, $map[$key], $verbosescales);
367         $this->assertEquals($userid, $userdetail->id);
369         $newgrades = $testobject->test_map_user_data_with_value('new', $testarray[0][6], $this->columns, $map, $key,
370                 $this->courseid, $map[$key], $verbosescales);
371         // Check that the final grade is the same as the one inserted.
372         $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade);
374         $feedback = $testobject->test_map_user_data_with_value('feedback', $testarray[0][7], $this->columns, $map, $key,
375                 $this->courseid, $map[$key], $verbosescales);
376         // Expected result.
377         $resultarray = array();
378         $resultarray[0] = new stdClass();
379         $resultarray[0]->itemid = 1;
380         $resultarray[0]->feedback = $testarray[0][7];
381         $this->assertEquals($feedback, $resultarray);
383         // Default behaviour (update a grade item).
384         $newgrades = $testobject->test_map_user_data_with_value('default', $testarray[0][6], $this->columns, $map, $key,
385                 $this->courseid, $map[$key], $verbosescales);
386         $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade);
387     }
389     /**
390      * Test importing data into the gradebook.
391      */
392     public function test_prepare_import_grade_data() {
393         global $DB;
395         // Need to add one of the users into the system.
396         $user = new stdClass();
397         $user->firstname = 'Anne';
398         $user->lastname = 'Able';
399         $user->email = 'student7@mail.com';
400         // Insert user 1.
401         $this->getDataGenerator()->create_user($user);
402         $user = new stdClass();
403         $user->firstname = 'Bobby';
404         $user->lastname = 'Bunce';
405         $user->email = 'student5@mail.com';
406         // Insert user 2.
407         $this->getDataGenerator()->create_user($user);
409         $this->csv_load($this->oktext);
411         $importcode = 007;
412         $verbosescales = 0;
414         // Form data object.
415         $formdata = new stdClass();
416         $formdata->mapfrom = 5;
417         $formdata->mapto = 'useremail';
418         $formdata->mapping_0 = 0;
419         $formdata->mapping_1 = 0;
420         $formdata->mapping_2 = 0;
421         $formdata->mapping_3 = 0;
422         $formdata->mapping_4 = 0;
423         $formdata->mapping_5 = 0;
424         $formdata->mapping_6 = 'new';
425         $formdata->mapping_7 = 'feedback_2';
426         $formdata->mapping_8 = 0;
427         $formdata->mapping_9 = 0;
428         $formdata->map = 1;
429         $formdata->id = 2;
430         $formdata->iid = $this->iid;
431         $formdata->importcode = $importcode;
432         $formdata->forceimport = false;
434         // Blam go time.
435         $testobject = new phpunit_gradeimport_csv_load_data();
436         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '',
437                 $verbosescales);
438         // If everything inserted properly then this should be true.
439         $this->assertTrue($dataloaded);
440     }
442     /*
443      * Test importing csv data into the gradebook using "Last downloaded from this course" column and force import option.
444      */
445     public function test_force_import_option () {
447         // Need to add users into the system.
448         $user = new stdClass();
449         $user->firstname = 'Anne';
450         $user->lastname = 'Able';
451         $user->email = 'student7@mail.com';
452         $user->id_number = 1;
453         $user1 = $this->getDataGenerator()->create_user($user);
454         $user = new stdClass();
455         $user->firstname = 'Bobby';
456         $user->lastname = 'Bunce';
457         $user->email = 'student5@mail.com';
458         $user->id_number = 2;
459         $user2 = $this->getDataGenerator()->create_user($user);
461         // Create a new grade item.
462         $params = array(
463             'itemtype'  => 'manual',
464             'itemname'  => 'Grade item 1',
465             'gradetype' => GRADE_TYPE_VALUE,
466             'courseid'  => $this->courseid
467         );
468         $gradeitem = new grade_item($params, false);
469         $gradeitemid = $gradeitem->insert();
471         $importcode = 001;
472         $verbosescales = 0;
474         // Form data object.
475         $formdata = new stdClass();
476         $formdata->mapfrom = 5;
477         $formdata->mapto = 'useremail';
478         $formdata->mapping_0 = 0;
479         $formdata->mapping_1 = 0;
480         $formdata->mapping_2 = 0;
481         $formdata->mapping_3 = 0;
482         $formdata->mapping_4 = 0;
483         $formdata->mapping_5 = 0;
484         $formdata->mapping_6 = $gradeitemid;
485         $formdata->mapping_7 = 'feedback_2';
486         $formdata->mapping_8 = 0;
487         $formdata->mapping_9 = 0;
488         $formdata->map = 1;
489         $formdata->id = 2;
490         $formdata->iid = $this->iid;
491         $formdata->importcode = $importcode;
492         $formdata->forceimport = false;
494         // Add last download from this course column to csv content.
495         $exportdate = time();
496         $newcsvdata = str_replace('{exportdate}', $exportdate, $this->csvtext);
497         $this->csv_load($newcsvdata);
498         $testobject = new phpunit_gradeimport_csv_load_data();
499         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
500                 $this->courseid, '', '', $verbosescales);
501         $this->assertTrue($dataloaded);
503         // We must update the last modified date.
504         grade_import_commit($this->courseid, $importcode, false, false);
506         // Test using force import disabled and a date in the past.
507         $pastdate = strtotime('-1 day', time());
508         $newcsvdata = str_replace('{exportdate}', $pastdate, $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->assertFalse($dataloaded);
514         $errors = $testobject->get_gradebookerrors();
515         $this->assertEquals($errors[0], get_string('gradealreadyupdated', 'grades', fullname($user1)));
517         // Test using force import enabled and a date in the past.
518         $formdata->forceimport = true;
519         $testobject = new phpunit_gradeimport_csv_load_data();
520         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
521                 $this->courseid, '', '', $verbosescales);
522         $this->assertTrue($dataloaded);
524         // Test importing using an old exported file (2 years ago).
525         $formdata->forceimport = false;
526         $twoyearsago = strtotime('-2 year', time());
527         $newcsvdata = str_replace('{exportdate}', $twoyearsago, $this->csvtext);
528         $this->csv_load($newcsvdata);
529         $testobject = new phpunit_gradeimport_csv_load_data();
530         $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport,
531                 $this->courseid, '', '', $verbosescales);
532         $this->assertFalse($dataloaded);
533         $errors = $testobject->get_gradebookerrors();
534         $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades'));
536         // Test importing using invalid exported date.
537         $baddate = '0123A56B89';
538         $newcsvdata = str_replace('{exportdate}', $baddate, $this->csvtext);
539         $this->csv_load($newcsvdata);
540         $formdata->mapping_6 = $gradeitemid;
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 date in the future.
549         $oneyearahead = strtotime('+1 year', time());
550         $oldcsv = str_replace('{exportdate}', $oneyearahead, $this->csvtext);
551         $this->csv_load($oldcsv);
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'));
559     }