3 ///////////////////////////////////////////////////////////////////////////
5 // NOTICE OF COPYRIGHT //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com //
12 // This program is free software; you can redistribute it and/or modify //
13 // it under the terms of the GNU General Public License as published by //
14 // the Free Software Foundation; either version 2 of the License, or //
15 // (at your option) any later version. //
17 // This program is distributed in the hope that it will be useful, //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20 // GNU General Public License for more details: //
22 // http://www.gnu.org/copyleft/gpl.html //
24 ///////////////////////////////////////////////////////////////////////////
25 require_once '../../../config.php';
26 require_once $CFG->libdir
.'/gradelib.php';
27 require_once $CFG->dirroot
.'/grade/lib.php';
28 require_once '../grade_import_form.php';
29 require_once '../lib.php';
31 $id = required_param('id', PARAM_INT
); // course id
32 $separator = optional_param('separator', '', PARAM_ALPHA
);
33 $verbosescales = optional_param('verbosescales', 1, PARAM_BOOL
);
35 if (!$course = get_record('course', 'id', $id)) {
36 print_error('nocourseid');
39 require_login($course);
40 $context = get_context_instance(CONTEXT_COURSE
, $id);
41 require_capability('moodle/grade:import', $context);
42 require_capability('gradeimport/csv:view', $context);
45 if (isset($CFG->CSV_DELIMITER
)) {
46 $csv_delimiter = '\\' . $CFG->CSV_DELIMITER
;
47 $csv_delimiter2 = $CFG->CSV_DELIMITER
;
49 if (isset($CFG->CSV_ENCODE
)) {
50 $csv_encode = '/\&\#' . $CFG->CSV_ENCODE
. '/';
52 } else if ($separator == 'tab') {
53 $csv_delimiter = "\t";
57 $csv_delimiter = "\,";
58 $csv_delimiter2 = ",";
59 $csv_encode = '/\&\#44/';
62 $strgrades = get_string('grades', 'grades');
63 $actionstr = get_string('csv', 'grades');
64 $navigation = grade_build_nav(__FILE__
, $actionstr, array('courseid' => $course->id
));
66 print_header($course->shortname
.': '.get_string('grades'), $course->fullname
, $navigation);
67 print_grade_plugin_selector($id, 'import', 'csv');
70 $mform = new grade_import_form(null, array('includeseparator'=>!isset($CFG->CSV_DELIMITER
), 'verbosescales'=>true));
72 // set up grade import mapping form
74 $gradeitems = array();
76 if ($grade_items = grade_item
::fetch_all(array('courseid'=>$id))) {
77 foreach ($grade_items as $grade_item) {
78 // skip course type and category type
79 if ($grade_item->itemtype
== 'course' ||
$grade_item->itemtype
== 'category') {
84 $gradeitems[$grade_item->id
] = $grade_item->get_name();
89 if ($importcode = optional_param('importcode', '', PARAM_FILE
)) {
90 $filename = $CFG->dataroot
.'/temp/gradeimport/cvs/'.$USER->id
.'/'.$importcode;
91 $fp = fopen($filename, "r");
92 $header = split($csv_delimiter, fgets($fp,1024), PARAM_RAW
);
95 $mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header));
97 // if import form is submitted
98 if ($formdata = $mform->get_data()) {
100 // Large files are likely to take their time and memory. Let PHP know
101 // that we'll take longer, and that the process should be recycled soon
102 // to free up memory.
104 @raise_memory_limit
("192M");
105 if (function_exists('apache_child_terminate')) {
106 @apache_child_terminate
();
109 // use current (non-conflicting) time stamp
110 $importcode = get_new_importcode();
111 if (!$filename = make_upload_directory('temp/gradeimport/cvs/'.$USER->id
, true)) {
114 $filename = $filename.'/'.$importcode;
116 $text = $mform->get_file_content('userfile');
118 $textlib = textlib_get_instance();
119 /// normalize line endings and do the encoding conversion
120 $text = $textlib->convert($text, $formdata->encoding
);
121 $text = $textlib->trim_utf8_bom($text);
122 // Fix mac/dos newlines
123 $text = preg_replace('!\r\n?!',"\n",$text);
124 $fp = fopen($filename, "w");
128 $fp = fopen($filename, "r");
130 // --- get header (field names) ---
131 $header = split($csv_delimiter, fgets($fp,1024), PARAM_RAW
);
133 // print some preview
134 $numlines = 0; // 0 preview lines displayed
136 print_heading(get_string('importpreview', 'grades'));
139 foreach ($header as $h) {
140 $h = clean_param($h, PARAM_RAW
);
141 echo '<th>'.$h.'</th>';
144 while (!feof ($fp) && $numlines <= $formdata->previewrows
) {
145 $lines = split($csv_delimiter, fgets($fp,1024));
147 foreach ($lines as $line) {
148 echo '<td>'.$line.'</td>';;
155 // display the mapping form with header info processed
156 $mform2 = new grade_import_mapping_form(null, array('gradeitems'=>$gradeitems, 'header'=>$header));
157 $mform2->set_data(array('importcode'=>$importcode, 'id'=>$id, 'verbosescales'=>$verbosescales, 'separator'=>$separator));
160 //} else if (($formdata = data_submitted()) && !empty($formdata->map)) {
162 // else if grade import mapping form is submitted
163 } else if ($formdata = $mform2->get_data()) {
165 $importcode = clean_param($formdata->importcode
, PARAM_FILE
);
166 $filename = $CFG->dataroot
.'/temp/gradeimport/cvs/'.$USER->id
.'/'.$importcode;
168 if (!file_exists($filename)) {
169 error('error processing upload file');
172 if ($fp = fopen($filename, "r")) {
173 // --- get header (field names) ---
174 $header = split($csv_delimiter, clean_param(fgets($fp,1024), PARAM_RAW
));
176 foreach ($header as $i => $h) {
177 $h = trim($h); $header[$i] = $h; // remove whitespace
180 error ('could not open file');
184 // loops mapping_0, mapping_1 .. mapping_n and construct $map array
185 foreach ($header as $i => $head) {
186 $map[$i] = $formdata->{'mapping_'.$i};
189 // if mapping informatioin is supplied
190 $map[clean_param($formdata->mapfrom
, PARAM_RAW
)] = clean_param($formdata->mapto
, PARAM_RAW
);
192 // check for mapto collisions
193 $maperrors = array();
194 foreach ($map as $i=>$j) {
196 // you can have multiple ignores
199 if (!isset($maperrors[$j])) {
200 $maperrors[$j] = true;
203 unlink($filename); // needs to be uploaded again, sorry
204 error('mapping collision detected, 2 fields maps to the same grade item '.$j);
209 // Large files are likely to take their time and memory. Let PHP know
210 // that we'll take longer, and that the process should be recycled soon
211 // to free up memory.
213 @raise_memory_limit
("192M");
214 if (function_exists('apache_child_terminate')) {
215 @apache_child_terminate
();
218 // we only operate if file is readable
219 if ($fp = fopen($filename, "r")) {
221 // read the first line makes sure this doesn't get read again
222 $header = split($csv_delimiter, fgets($fp,1024));
224 $newgradeitems = array(); // temporary array to keep track of what new headers are processed
227 while (!feof ($fp)) {
229 $line = split($csv_delimiter, fgets($fp,1024));
231 if(count($line) <= 1){
232 // there is no data on this line, move on
236 // array to hold all grades to be inserted
237 $newgrades = array();
238 // array to hold all feedback
239 $newfeedbacks = array();
240 // each line is a student record
241 foreach ($line as $key => $value) {
242 //decode encoded commas
243 $value = clean_param($value, PARAM_RAW
);
244 $value = trim($value);
245 if ($csv_encode != $csv_delimiter2) {
246 $value = preg_replace($csv_encode, $csv_delimiter2, $value);
251 * 1) userid, useridnumber, usermail, username - used to identify user row
252 * 2) new - new grade item
253 * 3) id - id of the old grade item to map onto
254 * 3) feedback_id - feedback for grade item id
257 $t = explode("_", $map[$key]);
267 if (!$user = get_record('user','id', addslashes($value))) {
268 // user not found, abort whold import
269 import_cleanup($importcode);
270 notify("user mapping error, could not find user with id \"$value\"");
277 if (!$user = get_record('user', 'idnumber', addslashes($value))) {
278 // user not found, abort whold import
279 import_cleanup($importcode);
280 notify("user mapping error, could not find user with idnumber \"$value\"");
284 $studentid = $user->id
;
287 if (!$user = get_record('user', 'email', addslashes($value))) {
288 import_cleanup($importcode);
289 notify("user mapping error, could not find user with email address \"$value\"");
293 $studentid = $user->id
;
296 if (!$user = get_record('user', 'username', addslashes($value))) {
297 import_cleanup($importcode);
298 notify("user mapping error, could not find user with username \"$value\"");
302 $studentid = $user->id
;
305 // first check if header is already in temp database
307 if (empty($newgradeitems[$key])) {
309 $newgradeitem = new object();
310 $newgradeitem->itemname
= $header[$key];
311 $newgradeitem->importcode
= $importcode;
312 $newgradeitem->importer
= $USER->id
;
314 // failed to insert into new grade item buffer
315 if (!$newgradeitems[$key] = insert_record('grade_import_newitem', addslashes_recursive($newgradeitem))) {
317 import_cleanup($importcode);
318 notify(get_string('importfailed', 'grades'));
321 // add this to grade_import_newitem table
322 // add the new id to $newgradeitem[$key]
324 $newgrade = new object();
325 $newgrade->newgradeitem
= $newgradeitems[$key];
326 $newgrade->finalgrade
= $value;
327 $newgrades[] = $newgrade;
330 // else, insert grade into the table
334 // case of an id, only maps id of a grade_item
336 if (!$gradeitem = new grade_item(array('id'=>$t1, 'courseid'=>$course->id
))) {
337 // supplied bad mapping, should not be possible since user
338 // had to pick mapping
340 import_cleanup($importcode);
341 notify(get_string('importfailed', 'grades'));
345 // t1 is the id of the grade item
346 $feedback = new object();
347 $feedback->itemid
= $t1;
348 $feedback->feedback
= $value;
349 $newfeedbacks[] = $feedback;
353 // existing grade items
354 if (!empty($map[$key])) {
355 // case of an id, only maps id of a grade_item
357 if (!$gradeitem = new grade_item(array('id'=>$map[$key], 'courseid'=>$course->id
))) {
358 // supplied bad mapping, should not be possible since user
359 // had to pick mapping
361 import_cleanup($importcode);
362 notify(get_string('importfailed', 'grades'));
366 // check if grade item is locked if so, abort
367 if ($gradeitem->is_locked()) {
369 import_cleanup($importcode);
370 notify(get_string('gradeitemlocked', 'grades'));
374 $newgrade = new object();
375 $newgrade->itemid
= $gradeitem->id
;
376 if ($gradeitem->gradetype
== GRADE_TYPE_SCALE
and $verbosescales) {
377 if ($value === '' or $value == '-') {
378 $value = null; // no grade
380 $scale = $gradeitem->load_scale();
381 $scales = explode(',', $scale->scale
);
382 array_unshift($scales, '-'); // scales start at key 1
383 $key = array_search($value, $scales);
384 if ($key === false) {
385 echo "<br/>t0 is $t0";
386 echo "<br/>grade is $value";
388 import_cleanup($importcode);
389 notify(get_string('badgrade', 'grades'));
394 $newgrade->finalgrade
= $value;
396 if ($value === '' or $value == '-') {
397 $value = null; // no grade
399 } else if (!is_numeric($value)) {
400 // non numeric grade value supplied, possibly mapped wrong column
401 echo "<br/>t0 is $t0";
402 echo "<br/>grade is $value";
404 import_cleanup($importcode);
405 notify(get_string('badgrade', 'grades'));
408 $newgrade->finalgrade
= $value;
410 $newgrades[] = $newgrade;
411 } // otherwise, we ignore this column altogether
412 // because user has chosen to ignore them (e.g. institution, address etc)
417 // no user mapping supplied at all, or user mapping failed
418 if (empty($studentid) ||
!is_numeric($studentid)) {
419 // user not found, abort whold import
421 import_cleanup($importcode);
422 notify('user mapping error, could not find user!');
426 // insert results of this students into buffer
427 if ($status and !empty($newgrades)) {
429 foreach ($newgrades as $newgrade) {
431 // check if grade_grade is locked and if so, abort
432 if (!empty($newgrade->itemid
) and $grade_grade = new grade_grade(array('itemid'=>$newgrade->itemid
, 'userid'=>$studentid))) {
433 if ($grade_grade->is_locked()) {
434 // individual grade locked
436 import_cleanup($importcode);
437 notify(get_string('gradelocked', 'grades'));
442 $newgrade->importcode
= $importcode;
443 $newgrade->userid
= $studentid;
444 $newgrade->importer
= $USER->id
;
445 if (!insert_record('grade_import_values', addslashes_recursive($newgrade))) {
446 // could not insert into temporary table
448 import_cleanup($importcode);
449 notify(get_string('importfailed', 'grades'));
455 // updating/inserting all comments here
456 if ($status and !empty($newfeedbacks)) {
457 foreach ($newfeedbacks as $newfeedback) {
459 FROM {$CFG->prefix}grade_import_values
460 WHERE importcode=$importcode AND userid=$studentid AND itemid=$newfeedback->itemid AND importer={$USER->id}";
461 if ($feedback = get_record_sql($sql)) {
462 $newfeedback->id
= $feedback->id
;
463 update_record('grade_import_values', addslashes_recursive($newfeedback));
466 // the grade item for this is not updated
467 $newfeedback->importcode
= $importcode;
468 $newfeedback->userid
= $studentid;
469 $newfeedback->importer
= $USER->id
;
470 insert_record('grade_import_values', addslashes_recursive($newfeedback));
476 /// at this stage if things are all ok, we commit the changes from temp table
478 grade_import_commit($course->id
, $importcode);
480 // temporary file can go now
483 error ('import file '.$filename.' not readable');
487 // display the standard upload file form