ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PK)B\oB88lang/en/dataformat_csv.phpnu[. /** * CSV dataformat lang strings. * * @package dataformat_csv * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['dataformat'] = 'Comma separated values (.csv)'; $string['privacy:metadata'] = 'The CSV data format plugin does not store any personal data.'; $string['shortname'] = 'CSV'; PK)B\+@M version.phpnu[. /** * Version details * * @package gradeimport * @subpackage csv * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2025041400; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2025040800; // Requires this Moodle version. $plugin->component = 'gradeimport_csv'; // Full name of the plugin (used for diagnostics) PK)B\M+classes/privacy/provider.phpnu[. /** * Privacy Subsystem implementation for gradeimport_csv. * * @package gradeimport_csv * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace gradeimport_csv\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for gradeimport_csv implementing null_provider. * * @copyright 2018 Sara Arjona * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } } PK)B\=>>classes/writer.phpnu[. /** * CSV data format writer * * @package dataformat_csv * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace dataformat_csv; defined('MOODLE_INTERNAL') || die(); /** * CSV data format writer * * @package dataformat_csv * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class writer extends \core\dataformat\spout_base { /** @var $mimetype */ protected $mimetype = "text/csv"; /** @var $extension */ protected $extension = ".csv"; } PK\ ^ db/access.phpnu[. /** * Capabilities gradeimport plugin. * * @package gradeimport_csv * @copyright 2007 Martin Dougiamas * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $capabilities = array( 'gradeimport/csv:view' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ) ); PK\'Glang/en/gradeimport_csv.phpnu[. /** * Strings for component 'gradeimport_csv', language 'en', branch 'MOODLE_20_STABLE' * * @package gradeimport_csv * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['csv:view'] = 'Import grades from CSV'; $string['gradepenalties'] = 'Grade penalties will not be applied to imported grades.'; $string['pluginname'] = 'CSV file'; $string['privacy:metadata'] = 'The import grades from CSV plugin does not store any personal data.'; PK\otͮ classes/output/renderer.phpnu[. /** * Renderers for the import of CSV files into the gradebook. * * @package gradeimport_csv * @copyright 2014 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Renderers for the import of CSV files into the gradebook. * * @package gradeimport_csv * @copyright 2014 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class gradeimport_csv_renderer extends plugin_renderer_base { /** * A renderer for the standard upload file form. * * @param object $course The course we are doing all of this action in. * @param object $mform The mform for uploading CSV files. * @return string html to be displayed. */ public function standard_upload_file_form($course, $mform) { $output = groups_print_course_menu($course, 'index.php?id=' . $course->id, true); $output .= html_writer::start_tag('div', array('class' => 'clearer')); $output .= html_writer::end_tag('div'); // Form. ob_start(); $mform->display(); $output .= ob_get_contents(); ob_end_clean(); return $output; } /** * A renderer for the CSV file preview. * * @param array $header Column headers from the CSV file. * @param array $data The rest of the data from the CSV file. * @return string html to be displayed. */ public function import_preview_page($header, $data) { $html = $this->output->heading(get_string('importpreview', 'grades')); $table = new html_table(); $table->head = array_map('s', $header); $table->data = array_map(static function($row) { return array_map('s', $row); }, $data); $html .= html_writer::table($table); return $html; } /** * A renderer for errors generated trying to import the CSV file. * * @param array $errors Display import errors. * @return string errors as html to be displayed. */ public function errors($errors) { $html = ''; foreach ($errors as $error) { $html .= $this->output->notification($error); } return $html; } } PK\Cjjclasses/load_data.phpnu[. /** * A class for loading and preparing grade data from import. * * @package gradeimport_csv * @copyright 2014 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * A class for loading and preparing grade data from import. * * @package gradeimport_csv * @copyright 2014 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class gradeimport_csv_load_data { /** @var string $error csv import error. */ protected $error; /** @var int $iid Unique identifier for these csv records. */ protected $iid; /** @var array $headers Column names for the data. */ protected $headers; /** @var array $previewdata A subsection of the csv imported data. */ protected $previewdata; // The map_user_data_with_value variables. /** @var array $newgrades Grades to be inserted into the gradebook. */ protected $newgrades; /** @var array $newfeedbacks Feedback to be inserted into the gradebook. */ protected $newfeedbacks; /** @var int $studentid Student ID*/ protected $studentid; // The prepare_import_grade_data() variables. /** @var bool $status The current status of the import. True = okay, False = errors. */ protected $status; /** @var int $importcode The code for this batch insert. */ protected $importcode; /** @var array $gradebookerrors An array of errors from trying to import into the gradebook. */ protected $gradebookerrors; /** @var array $newgradeitems An array of new grade items to be inserted into the gradebook. */ protected $newgradeitems; /** * Load CSV content for previewing. * * @param string $text The grade data being imported. * @param string $encoding The type of encoding the file uses. * @param string $separator The separator being used to define each field. * @param int $previewrows How many rows are being previewed. */ public function load_csv_content($text, $encoding, $separator, $previewrows) { $this->raise_limits(); $this->iid = csv_import_reader::get_new_iid('grade'); $csvimport = new csv_import_reader($this->iid, 'grade'); $csvimport->load_csv_content($text, $encoding, $separator); $this->error = $csvimport->get_error(); // If there are no import errors then proceed. if (empty($this->error)) { // Get header (field names). $this->headers = $csvimport->get_columns(); $this->trim_headers(); $csvimport->init(); $this->previewdata = array(); for ($numlines = 0; $numlines <= $previewrows; $numlines++) { $lines = $csvimport->next(); if ($lines) { $this->previewdata[] = $lines; } } } } /** * Gets all of the grade items in this course. * * @param int $courseid Course id; * @return array An array of grade items for the course. */ public static function fetch_grade_items($courseid) { $gradeitems = null; if ($allgradeitems = grade_item::fetch_all(array('courseid' => $courseid))) { foreach ($allgradeitems as $gradeitem) { // Skip course type and category type. if ($gradeitem->itemtype == 'course' || $gradeitem->itemtype == 'category') { continue; } $displaystring = null; if (!empty($gradeitem->itemmodule)) { $displaystring = get_string('modulename', $gradeitem->itemmodule).get_string('labelsep', 'langconfig') .$gradeitem->get_name(); } else { $displaystring = $gradeitem->get_name(); } $gradeitems[$gradeitem->id] = $displaystring; } } return $gradeitems; } /** * Cleans the column headers from the CSV file. */ protected function trim_headers() { foreach ($this->headers as $i => $h) { $h = trim($h); // Remove whitespace. $h = clean_param($h, PARAM_RAW); // Clean the header. $this->headers[$i] = $h; } } /** * Raises the php execution time and memory limits for importing the CSV file. */ protected function raise_limits() { // Large files are likely to take their time and memory. Let PHP know // that we'll take longer, and that the process should be recycled soon // to free up memory. core_php_time_limit::raise(); raise_memory_limit(MEMORY_EXTRA); } /** * Inserts a record into the grade_import_values table. This also adds common record information. * * @param stdClass $record The grade record being inserted into the database. * @param int $studentid The student ID. * @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(stdClass $record, int $studentid, grade_item $gradeitem): mixed { global $DB, $USER, $CFG; $record->importcode = $this->importcode; $record->userid = $studentid; $record->importer = $USER->id; // 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. $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 { 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; } } /** * Insert the new grade into the grade item buffer table. * * @param array $header The column headers from the CSV file. * @param int $key Current row identifier. * @param string $value The value for this row (final grade). * @return stdClass new grade that is ready for commiting to the gradebook. */ protected function import_new_grade_item($header, $key, $value) { global $DB, $USER; // First check if header is already in temp database. if (empty($this->newgradeitems[$key])) { $newgradeitem = new stdClass(); $newgradeitem->itemname = $header[$key]; $newgradeitem->importcode = $this->importcode; $newgradeitem->importer = $USER->id; // Insert into new grade item buffer. $this->newgradeitems[$key] = $DB->insert_record('grade_import_newitem', $newgradeitem); } $newgrade = new stdClass(); $newgrade->newgradeitem = $this->newgradeitems[$key]; $trimmed = trim($value); if ($trimmed === '' or $trimmed == '-') { // Blank or dash grade means null, ie "no grade". $newgrade->finalgrade = null; } else { // We have an actual grade. $newgrade->finalgrade = $value; } $this->newgrades[] = $newgrade; return $newgrade; } /** * Check that the user is in the system. * * @param string $value The value, from the csv file, being mapped to identify the user. * @param array $userfields Contains the field and label being mapped from. * @return int Returns the user ID if it exists, otherwise null. */ protected function check_user_exists($value, $userfields) { global $DB; $user = null; $errorkey = false; // The user may use the incorrect field to match the user. This could result in an exception. try { $field = $userfields['field']; // Fields that can be queried in a case-insensitive manner. $caseinsensitivefields = [ 'email', 'username', ]; // Build query predicate. if (in_array($field, $caseinsensitivefields)) { // Case-insensitive. $select = $DB->sql_equal($field, ':' . $field, false); } else { // Exact-value. $select = "{$field} = :{$field}"; } // Validate if the user id value is numerical. if ($field === 'id' && !is_numeric($value)) { $errorkey = 'usermappingerror'; } // Make sure the record exists and that there's only one matching record found. $user = $DB->get_record_select('user', $select, array($userfields['field'] => $value), '*', MUST_EXIST); } catch (dml_missing_record_exception $missingex) { $errorkey = 'usermappingerror'; } catch (dml_multiple_records_exception $multiex) { $errorkey = 'usermappingerrormultipleusersfound'; } // Field may be fine, but no records were returned. if ($errorkey) { $usermappingerrorobj = new stdClass(); $usermappingerrorobj->field = $userfields['label']; $usermappingerrorobj->value = $value; $this->cleanup_import(get_string($errorkey, 'grades', $usermappingerrorobj)); unset($usermappingerrorobj); return null; } return $user->id; } /** * Check to see if the feedback matches a grade item. * * @param int $courseid The course ID. * @param int $itemid The ID of the grade item that the feedback relates to. * @param string $value The actual feedback being imported. * @return object Creates a feedback object with the item ID and the feedback value. */ protected function create_feedback($courseid, $itemid, $value) { // Case of an id, only maps id of a grade_item. // This was idnumber. if (!new grade_item(array('id' => $itemid, 'courseid' => $courseid))) { // Supplied bad mapping, should not be possible since user // had to pick mapping. $this->cleanup_import(get_string('importfailed', 'grades')); return null; } // The itemid is the id of the grade item. $feedback = new stdClass(); $feedback->itemid = $itemid; $feedback->feedback = $value; return $feedback; } /** * This updates existing grade items. * * @param int $courseid The course ID. * @param array $map Mapping information provided by the user. * @param int $key The line that we are currently working on. * @param bool $verbosescales Form setting for grading with scales. * @param string $value The grade value. * @return array grades to be updated. */ protected function update_grade_item($courseid, $map, $key, $verbosescales, $value) { // Case of an id, only maps id of a grade_item. // This was idnumber. if (!$gradeitem = new grade_item(array('id' => $map[$key], 'courseid' => $courseid))) { // Supplied bad mapping, should not be possible since user // had to pick mapping. $this->cleanup_import(get_string('importfailed', 'grades')); return null; } // Check if grade item is locked if so, abort. if ($gradeitem->is_locked()) { $this->cleanup_import(get_string('gradeitemlocked', 'grades')); return null; } $newgrade = new stdClass(); $newgrade->itemid = $gradeitem->id; if ($gradeitem->gradetype == GRADE_TYPE_SCALE and $verbosescales) { if ($value === '' or $value == '-') { $value = null; // No grade. } else { $scale = $gradeitem->load_scale(); $scales = explode(',', $scale->scale); $scales = array_map('trim', $scales); // Hack - trim whitespace around scale options. array_unshift($scales, '-'); // Scales start at key 1. $key = array_search($value, $scales); if ($key === false) { $this->cleanup_import(get_string('badgrade', 'grades')); return null; } $value = $key; } $newgrade->finalgrade = $value; } else { if ($value === '' or $value == '-') { $value = null; // No grade. } else { // If the value has a local decimal or can correctly be unformatted, do it. $validvalue = unformat_float($value, true); if ($validvalue !== false) { $value = $validvalue; } else { // Non numeric grade value supplied, possibly mapped wrong column. $this->cleanup_import(get_string('badgrade', 'grades')); return null; } } $newgrade->finalgrade = $value; } $this->newgrades[] = $newgrade; return $this->newgrades; } /** * Clean up failed CSV grade import. Clears the temp table for inserting grades. * * @param string $notification The error message to display from the unsuccessful grade import. */ protected function cleanup_import($notification) { $this->status = false; import_cleanup($this->importcode); $this->gradebookerrors[] = $notification; } /** * Check user mapping. * * @param string $mappingidentifier The user field that we are matching together. * @param string $value The value we are checking / importing. * @param array $header The column headers of the csv file. * @param array $map Mapping information provided by the user. * @param int $key Current row identifier. * @param int $courseid The course ID. * @param int $feedbackgradeid The ID of the grade item that the feedback relates to. * @param bool $verbosescales Form setting for grading with scales. */ protected function map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid, $verbosescales) { // Fields that the user can be mapped from. $userfields = array( 'userid' => array( 'field' => 'id', 'label' => 'id', ), 'useridnumber' => array( 'field' => 'idnumber', 'label' => 'idnumber', ), 'useremail' => array( 'field' => 'email', 'label' => 'email address', ), 'username' => array( 'field' => 'username', 'label' => 'username', ), ); switch ($mappingidentifier) { case 'userid': case 'useridnumber': case 'useremail': case 'username': $this->studentid = $this->check_user_exists($value, $userfields[$mappingidentifier]); break; case 'new': $this->import_new_grade_item($header, $key, $value); break; case 'feedback': if ($feedbackgradeid) { $feedback = $this->create_feedback($courseid, $feedbackgradeid, $value); if (isset($feedback)) { $this->newfeedbacks[] = $feedback; } } break; default: // Existing grade items. if (!empty($map[$key])) { $this->newgrades = $this->update_grade_item($courseid, $map, $key, $verbosescales, $value, $mappingidentifier); } // Otherwise, we ignore this column altogether because user has chosen // to ignore them (e.g. institution, address etc). break; } } /** * Checks and prepares grade data for inserting into the gradebook. * * @param array $header Column headers of the CSV file. * @param object $formdata Mapping information from the preview page. * @param object $csvimport csv import reader object for iterating over the imported CSV file. * @param int $courseid The course ID. * @param bool $separatemode If we have groups are they separate? * @param mixed $currentgroup current group information. * @param bool $verbosescales Form setting for grading with scales. * @return bool True if the status for importing is okay, false if there are errors. */ public function prepare_import_grade_data($header, $formdata, $csvimport, $courseid, $separatemode, $currentgroup, $verbosescales) { global $DB, $USER; // The import code is used for inserting data into the grade tables. $this->importcode = $formdata->importcode; $this->status = true; $this->headers = $header; $this->studentid = null; $this->gradebookerrors = null; $forceimport = $formdata->forceimport; // Temporary array to keep track of what new headers are processed. $this->newgradeitems = array(); $this->trim_headers(); $timeexportkey = null; $map = array(); // Loops mapping_0, mapping_1 .. mapping_n and construct $map array. foreach ($header as $i => $head) { if (isset($formdata->{'mapping_'.$i})) { $map[$i] = $formdata->{'mapping_'.$i}; } if ($head == get_string('timeexported', 'gradeexport_txt')) { $timeexportkey = $i; } } // If mapping information is supplied. $map[clean_param($formdata->mapfrom, PARAM_RAW)] = clean_param($formdata->mapto, PARAM_RAW); // Check for mapto collisions. $maperrors = array(); foreach ($map as $i => $j) { if (($j == 0) || ($j == 'new')) { // You can have multiple ignores or multiple new grade items. continue; } else { if (!isset($maperrors[$j])) { $maperrors[$j] = true; } else { // Collision. throw new \moodle_exception('cannotmapfield', '', '', $j); } } } $this->raise_limits(); $csvimport->init(); while ($line = $csvimport->next()) { if (count($line) <= 1) { // There is no data on this line, move on. continue; } // Array to hold all grades to be inserted. $this->newgrades = array(); // Array to hold all feedback. $this->newfeedbacks = array(); // Each line is a student record. foreach ($line as $key => $value) { $value = clean_param($value, PARAM_RAW); $value = trim($value); /* * the options are * 1) userid, useridnumber, usermail, username - used to identify user row * 2) new - new grade item * 3) id - id of the old grade item to map onto * 3) feedback_id - feedback for grade item id */ // Explode the mapping for feedback into a label 'feedback' and the identifying number. $mappingbase = explode("_", $map[$key]); $mappingidentifier = $mappingbase[0]; // Set the feedback identifier if it exists. if (isset($mappingbase[1])) { $feedbackgradeid = (int)$mappingbase[1]; } else { $feedbackgradeid = ''; } $this->map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid, $verbosescales); if ($this->status === false) { return $this->status; } } // No user mapping supplied at all, or user mapping failed. if (empty($this->studentid) || !is_numeric($this->studentid)) { // User not found, abort whole import. $this->cleanup_import(get_string('usermappingerrorusernotfound', 'grades')); break; } if ($separatemode and !groups_is_member($currentgroup, $this->studentid)) { // Not allowed to import into this group, abort. $this->cleanup_import(get_string('usermappingerrorcurrentgroup', 'grades')); break; } // Insert results of this students into buffer. if ($this->status and !empty($this->newgrades)) { foreach ($this->newgrades as $newgrade) { // Check if grade_grade is locked and if so, abort. if (!empty($newgrade->itemid) and $gradegrade = new grade_grade(array('itemid' => $newgrade->itemid, 'userid' => $this->studentid))) { if ($gradegrade->is_locked()) { // Individual grade locked. $this->cleanup_import(get_string('gradelocked', 'grades')); return $this->status; } // Check if the force import option is disabled and the last exported date column is present. if (!$forceimport && !empty($timeexportkey)) { $exportedtime = $line[$timeexportkey]; if (clean_param($exportedtime, PARAM_INT) != $exportedtime || $exportedtime > time() || $exportedtime < strtotime("-1 year", time())) { // The date is invalid, or in the future, or more than a year old. $this->cleanup_import(get_string('invalidgradeexporteddate', 'grades')); return $this->status; } $timemodified = $gradegrade->get_dategraded(); if (!empty($timemodified) && ($exportedtime < $timemodified)) { // The item was graded after we exported it, we return here not to override it. $user = core_user::get_user($this->studentid); $this->cleanup_import(get_string('gradealreadyupdated', 'grades', fullname($user))); return $this->status; } } } 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; } } } // Updating/inserting all comments here. if ($this->status and !empty($this->newfeedbacks)) { foreach ($this->newfeedbacks as $newfeedback) { $sql = "SELECT * FROM {grade_import_values} WHERE importcode=? AND userid=? AND itemid=? AND importer=?"; if ($feedback = $DB->get_record_sql($sql, array($this->importcode, $this->studentid, $newfeedback->itemid, $USER->id))) { $newfeedback->id = $feedback->id; $DB->update_record('grade_import_values', $newfeedback); } else { // The grade item for this is not updated. $newfeedback->importonlyfeedback = true; $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; } } } } } return $this->status; } /** * Returns the headers parameter for this class. * * @return array returns headers parameter for this class. */ public function get_headers() { return $this->headers; } /** * Returns the error parameter for this class. * * @return string returns error parameter for this class. */ public function get_error() { return $this->error; } /** * Returns the iid parameter for this class. * * @return int returns iid parameter for this class. */ public function get_iid() { return $this->iid; } /** * Returns the preview_data parameter for this class. * * @return array returns previewdata parameter for this class. */ public function get_previewdata() { return $this->previewdata; } /** * Returns the gradebookerrors parameter for this class. * * @return array returns gradebookerrors parameter for this class. */ public function get_gradebookerrors() { return $this->gradebookerrors; } } PK\C-- index.phpnu[. require_once("../../../config.php"); require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->dirroot.'/grade/lib.php'); require_once($CFG->dirroot. '/grade/import/grade_import_form.php'); require_once($CFG->dirroot.'/grade/import/lib.php'); require_once($CFG->libdir . '/csvlib.class.php'); $id = required_param('id', PARAM_INT); // Course id. $separator = optional_param('separator', '', PARAM_ALPHA); $verbosescales = optional_param('verbosescales', 1, PARAM_BOOL); $iid = optional_param('iid', null, PARAM_INT); $importcode = optional_param('importcode', '', PARAM_FILE); $forceimport = optional_param('forceimport', false, PARAM_BOOL); $url = new moodle_url('/grade/import/csv/index.php', array('id'=>$id)); if ($separator !== '') { $url->param('separator', $separator); } if ($verbosescales !== 1) { $url->param('verbosescales', $verbosescales); } $PAGE->set_url($url); if (!$course = $DB->get_record('course', array('id'=>$id))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($id); require_capability('moodle/grade:import', $context); require_capability('gradeimport/csv:view', $context); $separatemode = (groups_get_course_groupmode($COURSE) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)); $currentgroup = groups_get_course_group($course); $actionbar = new \core_grades\output\import_action_bar($context, null, 'csv'); print_grade_page_head($course->id, 'import', 'csv', get_string('importcsv', 'grades'), false, false, true, 'importcsv', 'grades', null, $actionbar); $renderer = $PAGE->get_renderer('gradeimport_csv'); // Get the grade items to be matched with the import mapping columns. $gradeitems = gradeimport_csv_load_data::fetch_grade_items($course->id); // If the csv file hasn't been imported yet then look for a form submission or // show the initial submission form. if (!$iid) { // Set up the import form. $mform = new grade_import_form(null, array('includeseparator' => true, 'verbosescales' => $verbosescales, 'acceptedtypes' => array('.csv', '.txt'))); // If the import form has been submitted. if ($formdata = $mform->get_data()) { $text = $mform->get_file_content('userfile'); $csvimport = new gradeimport_csv_load_data(); $csvimport->load_csv_content($text, $formdata->encoding, $separator, $formdata->previewrows); $csvimporterror = $csvimport->get_error(); if (!empty($csvimporterror)) { echo $renderer->errors(array($csvimport->get_error())); echo $OUTPUT->continue_button(new moodle_url('/grade/import/csv/index.php', ['id' => $course->id])); echo $OUTPUT->footer(); die(); } $iid = $csvimport->get_iid(); echo $renderer->import_preview_page($csvimport->get_headers(), $csvimport->get_previewdata()); } else { // Display the standard upload file form. echo $renderer->standard_upload_file_form($course, $mform); echo $OUTPUT->footer(); die(); } } // Data has already been submitted so we can use the $iid to retrieve it. $csvimport = new csv_import_reader($iid, 'grade'); $header = $csvimport->get_columns(); // Get a new import code for updating to the grade book. if (empty($importcode)) { $importcode = get_new_importcode(); } $mappingformdata = array( 'gradeitems' => $gradeitems, 'header' => $header, 'iid' => $iid, 'id' => $id, 'importcode' => $importcode, 'forceimport' => $forceimport, 'verbosescales' => $verbosescales ); // we create a form to handle mapping data from the file to the database. $mform2 = new grade_import_mapping_form(null, $mappingformdata); // Here, if we have data, we process the fields and enter the information into the database. if ($formdata = $mform2->get_data()) { $gradeimport = new gradeimport_csv_load_data(); $status = $gradeimport->prepare_import_grade_data($header, $formdata, $csvimport, $course->id, $separatemode, $currentgroup, $verbosescales); // At this stage if things are all ok, we commit the changes from temp table. if ($status) { grade_import_commit($course->id, $importcode); } else { $errors = $gradeimport->get_gradebookerrors(); $errors[] = get_string('importfailed', 'grades'); echo $renderer->errors($errors); echo $OUTPUT->continue_button(new moodle_url('/grade/import/csv/index.php', ['id' => $course->id])); } echo $OUTPUT->footer(); } else { // If data hasn't been submitted then display the data mapping form. $mform2->display(); echo $OUTPUT->footer(); } PK\N4tests/fixtures/phpunit_gradeimport_csv_load_data.phpnu[. require_once($CFG->dirroot . '/grade/import/csv/classes/load_data.php'); require_once($CFG->dirroot . '/grade/import/lib.php'); /** * Class to open up private methods in gradeimport_csv_load_data(). */ class phpunit_gradeimport_csv_load_data extends gradeimport_csv_load_data { /** * Method to open up the appropriate method for unit testing. * * @param object $record * @param int $studentid * @param grade_item $gradeitem */ public function test_insert_grade_record($record, $studentid, grade_item $gradeitem) { $this->importcode = 00001; $this->insert_grade_record($record, $studentid, $gradeitem); } /** * Method to open up the appropriate method for unit testing. */ public function get_importcode() { return $this->importcode; } /** * Method to open up the appropriate method for unit testing. * * @param array $header The column headers from the CSV file. * @param int $key Current row identifier. * @param string $value The value for this row (final grade). * @return array new grades that are ready for commiting to the gradebook. */ public function test_import_new_grade_item($header, $key, $value) { $this->newgradeitems = null; $this->importcode = 00001; return $this->import_new_grade_item($header, $key, $value); } /** * Method to open up the appropriate method for unit testing. * * @param string $value The value, from the csv file, being mapped to identify the user. * @param array $userfields Contains the field and label being mapped from. * @return int Returns the user ID if it exists, otherwise null. */ public function test_check_user_exists($value, $userfields) { return $this->check_user_exists($value, $userfields); } /** * Method to open up the appropriate method for unit testing. * * @param int $courseid The course ID. * @param int $itemid The ID of the grade item that the feedback relates to. * @param string $value The actual feedback being imported. * @return object Creates a feedback object with the item ID and the feedback value. */ public function test_create_feedback($courseid, $itemid, $value) { return $this->create_feedback($courseid, $itemid, $value); } /** * Method to open up the appropriate method for unit testing. */ public function test_update_grade_item($courseid, $map, $key, $verbosescales, $value) { return $this->update_grade_item($courseid, $map, $key, $verbosescales, $value); } /** * Method to open up the appropriate method for unit testing. * * @param int $courseid The course ID. * @param array $map Mapping information provided by the user. * @param int $key The line that we are currently working on. * @param bool $verbosescales Form setting for grading with scales. * @param string $value The grade value . * @return array grades to be updated. */ public function test_map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid, $verbosescales) { // Set an import code. $this->importcode = 00001; $this->map_user_data_with_value($mappingidentifier, $value, $header, $map, $key, $courseid, $feedbackgradeid, $verbosescales); switch ($mappingidentifier) { case 'userid': case 'useridnumber': case 'useremail': case 'username': return $this->studentid; break; case 'new': return $this->newgrades; break; case 'feedback': return $this->newfeedbacks; break; default: return $this->newgrades; break; } } } PK\W4d4dtests/load_data_test.phpnu[. namespace gradeimport_csv; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/grade/import/csv/tests/fixtures/phpunit_gradeimport_csv_load_data.php'); require_once($CFG->libdir . '/csvlib.class.php'); require_once($CFG->libdir . '/grade/grade_item.php'); require_once($CFG->libdir . '/grade/tests/fixtures/lib.php'); /** * Unit tests for lib.php * * @package gradeimport_csv * @copyright 2014 Adrian Greeve * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class load_data_test extends \grade_base_testcase { /** @var string $oktext Text to be imported. This data should have no issues being imported. */ protected $oktext = '"First name","Last name","ID number",Institution,Department,"Email address","Assignment: Assignment for grape group", "Feedback: Assignment for grape group","Assignment: Second new grade item","Course total" Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,"We welcome feedback",,56.00 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,,45.0,75.00'; /** @var string $badtext Text to be imported. This data has an extra column and should not succeed in being imported. */ protected $badtext = '"First name","Last name","ID number",Institution,Department,"Email address","Assignment: Assignment for grape group","Course total" Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,56.00,78.00 Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,75.00'; /** @var string $csvtext CSV data to be imported with Last download from this course column. */ protected $csvtext = '"First name","Last name","ID number",Institution,Department,"Email address","Assignment: Assignment for grape group", "Feedback: Assignment for grape group","Course total","Last downloaded from this course" Anne,Able,,"Moodle HQ","Rock on!",student7@example.com,56.00,"We welcome feedback",56.00,{exportdate} Bobby,Bunce,,"Moodle HQ","Rock on!",student5@example.com,75.00,,75.00,{exportdate}'; /** @var int $iid Import ID. */ protected $iid; /** @var object $csvimport a csv_import_reader object that handles the csv import. */ protected $csvimport; /** @var array $columns The first row of the csv file. These are the columns of the import file.*/ protected $columns; public function tearDown(): void { $this->csvimport = null; parent::tearDown(); } /** * Load up the above text through the csv import. * * @param string $content Text to be imported into the gradebook. * @return array All text separated by commas now in an array. */ protected function csv_load($content) { // Import the csv strings. $this->iid = \csv_import_reader::get_new_iid('grade'); $this->csvimport = new \csv_import_reader($this->iid, 'grade'); $this->csvimport->load_csv_content($content, 'utf8', 'comma'); $this->columns = $this->csvimport->get_columns(); $this->csvimport->init(); while ($line = $this->csvimport->next()) { $testarray[] = $line; } return $testarray; } /** * Test loading data and returning preview content. */ public function test_load_csv_content(): void { $encoding = 'utf8'; $separator = 'comma'; $previewrows = 5; $csvpreview = new \phpunit_gradeimport_csv_load_data(); $csvpreview->load_csv_content($this->oktext, $encoding, $separator, $previewrows); $expecteddata = array(array( 'Anne', 'Able', '', 'Moodle HQ', 'Rock on!', 'student7@example.com', 56.00, 'We welcome feedback', '', 56.00 ), array( 'Bobby', 'Bunce', '', 'Moodle HQ', 'Rock on!', 'student5@example.com', 75.00, '', 45.0, 75.00 ) ); $expectedheaders = array( 'First name', 'Last name', 'ID number', 'Institution', 'Department', 'Email address', 'Assignment: Assignment for grape group', 'Feedback: Assignment for grape group', 'Assignment: Second new grade item', 'Course total' ); // Check that general data is returned as expected. $this->assertEquals($csvpreview->get_previewdata(), $expecteddata); // Check that headers are returned as expected. $this->assertEquals($csvpreview->get_headers(), $expectedheaders); // Check that errors are being recorded. $csvpreview = new \phpunit_gradeimport_csv_load_data(); $csvpreview->load_csv_content($this->badtext, $encoding, $separator, $previewrows); // Columns shouldn't match. $this->assertEquals($csvpreview->get_error(), get_string('csvweirdcolumns', 'error')); } /** * Test fetching grade items for the course. */ public function test_fetch_grade_items(): void { $gradeitemsarray = \grade_item::fetch_all(array('courseid' => $this->courseid)); $gradeitems = \phpunit_gradeimport_csv_load_data::fetch_grade_items($this->courseid); // Make sure that each grade item is located in the gradeitemsarray. foreach ($gradeitems as $key => $gradeitem) { $this->assertArrayHasKey($key, $gradeitemsarray); } // Get the key for a specific grade item. $quizkey = null; foreach ($gradeitemsarray as $key => $value) { if ($value->itemname == "Quiz grade item") { $quizkey = $key; } } // Expected modified item name. $testitemname = get_string('modulename', $gradeitemsarray[$quizkey]->itemmodule) . ': ' . $gradeitemsarray[$quizkey]->itemname; // Check that an item that is a module, is concatenated properly. $this->assertEquals($testitemname, $gradeitems[$quizkey]); } /** * Test the inserting of grade record data. */ public function test_insert_grade_record(): void { global $DB, $USER; $user = $this->getDataGenerator()->create_user(); $this->setAdminUser(); $record = new \stdClass(); $record->itemid = 4; $record->newgradeitem = 25; $record->finalgrade = 62.00; $record->feedback = 'Some test feedback'; $testobject = new \phpunit_gradeimport_csv_load_data(); $testobject->test_insert_grade_record($record, $user->id, new \grade_item()); $gradeimportvalues = $DB->get_records('grade_import_values'); // Get the insert id. $key = key($gradeimportvalues); $testarray = array(); $testarray[$key] = new \stdClass(); $testarray[$key]->id = $key; $testarray[$key]->itemid = $record->itemid; $testarray[$key]->newgradeitem = $record->newgradeitem; $testarray[$key]->userid = $user->id; $testarray[$key]->finalgrade = $record->finalgrade; $testarray[$key]->feedback = $record->feedback; $testarray[$key]->importcode = $testobject->get_importcode(); $testarray[$key]->importer = $USER->id; $testarray[$key]->importonlyfeedback = 0; // Check that the record was inserted into the database. $this->assertEquals($gradeimportvalues, $testarray); } /** * Test preparing a new grade item for import into the gradebook. */ public function test_import_new_grade_item(): void { global $DB; $this->setAdminUser(); $this->csv_load($this->oktext); $columns = $this->columns; // The assignment is item 6. $key = 6; $testobject = new \phpunit_gradeimport_csv_load_data(); // Key for this assessment. $this->csvimport->init(); $testarray = array(); while ($line = $this->csvimport->next()) { $testarray[] = $testobject->test_import_new_grade_item($columns, $key, $line[$key]); } // Query the database and check how many results were inserted. $newgradeimportitems = $DB->get_records('grade_import_newitem'); $this->assertEquals(count($testarray), count($newgradeimportitems)); } /** * Data provider for \gradeimport_csv_load_data_testcase::test_check_user_exists(). * * @return array */ public static function check_user_exists_provider(): array { return [ 'Fetch by email' => [ 'email', 's1@example.com', true ], 'Fetch by email, different case' => [ 'email', 'S1@EXAMPLE.COM', true ], 'Fetch data using a non-existent email' => [ 'email', 's2@example.com', false ], 'Multiple accounts with the same email' => [ 'email', 's1@example.com', false, 1 ], 'Fetch data using a valid user ID' => [ 'id', true, true ], 'Fetch data using a non-existent user ID' => [ 'id', false, false ], 'Fetch data using a valid username' => [ 'username', 's1', true ], 'Fetch data using a valid username, different case' => [ 'username', 'S1', true ], 'Fetch data using an invalid username' => [ 'username', 's2', false ], 'Fetch data using a valid ID Number' => [ 'idnumber', 's1', true ], 'Fetch data using an invalid ID Number' => [ 'idnumber', 's2', false ], ]; } /** * Check that the user matches a user in the system. * * @dataProvider check_user_exists_provider * @param string $field The field to use for the query. * @param string|boolean $value The field value. When fetching by ID, set true to fetch valid user ID, false otherwise. * @param boolean $successexpected Whether we expect for a user to be found or not. * @param int $allowaccountssameemail Value for $CFG->allowaccountssameemail */ public function test_check_user_exists($field, $value, $successexpected, $allowaccountssameemail = 0): void { $this->resetAfterTest(); $generator = $this->getDataGenerator(); // Need to add one of the users into the system. $user = $generator->create_user([ 'firstname' => 'Anne', 'lastname' => 'Able', 'email' => 's1@example.com', 'idnumber' => 's1', 'username' => 's1', ]); if ($allowaccountssameemail) { // Create another user with the same email address. $generator->create_user(['email' => 's1@example.com']); } // Since the data provider can't know what user ID to use, do a special handling for ID field tests. if ($field === 'id') { if ($value) { // Test for fetching data using a valid user ID. Use the generated user's ID. $value = $user->id; } else { // Test for fetching data using a non-existent user ID. $value = $user->id + 1; } } $userfields = [ 'field' => $field, 'label' => 'Field label: ' . $field ]; $testobject = new \phpunit_gradeimport_csv_load_data(); // Check whether the user exists. If so, then the user id is returned. Otherwise, it returns null. $userid = $testobject->test_check_user_exists($value, $userfields); if ($successexpected) { // Check that the user id returned matches with the user that we created. $this->assertEquals($user->id, $userid); // Check that there are no errors. $this->assertEmpty($testobject->get_gradebookerrors()); } else { // Check that the userid is null. $this->assertNull($userid); // Check that expected error message and actual message match. $gradebookerrors = $testobject->get_gradebookerrors(); $mappingobject = (object)[ 'field' => $userfields['label'], 'value' => $value, ]; if ($allowaccountssameemail) { $expectederrormessage = get_string('usermappingerrormultipleusersfound', 'grades', $mappingobject); } else { $expectederrormessage = get_string('usermappingerror', 'grades', $mappingobject); } $this->assertEquals($expectederrormessage, $gradebookerrors[0]); } } /** * Test preparing feedback for inserting / updating into the gradebook. */ public function test_create_feedback(): void { $testarray = $this->csv_load($this->oktext); $testobject = new \phpunit_gradeimport_csv_load_data(); // Try to insert some feedback for an assessment. $feedback = $testobject->test_create_feedback($this->courseid, 1, $testarray[0][7]); // Expected result. $expectedfeedback = array('itemid' => 1, 'feedback' => $testarray[0][7]); $this->assertEquals((array)$feedback, $expectedfeedback); } /** * Test preparing grade_items for upgrading into the gradebook. */ public function test_update_grade_item(): void { $testarray = $this->csv_load($this->oktext); $testobject = new \phpunit_gradeimport_csv_load_data(); // We're not using scales so no to this option. $verbosescales = 0; // Map and key are to retrieve the grade_item that we are updating. $map = array(1); $key = 0; // We return the new grade array for saving. $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, $testarray[0][6]); $expectedresult = array(); $expectedresult[0] = new \stdClass(); $expectedresult[0]->itemid = 1; $expectedresult[0]->finalgrade = $testarray[0][6]; $this->assertEquals($newgrades, $expectedresult); // Try sending a bad grade value (A letter instead of a float / int). $newgrades = $testobject->test_update_grade_item($this->courseid, $map, $key, $verbosescales, 'A'); // The $newgrades variable should be null. $this->assertNull($newgrades); $expectederrormessage = get_string('badgrade', 'grades'); // Check that the error message is what we expect. $gradebookerrors = $testobject->get_gradebookerrors(); $this->assertEquals($expectederrormessage, $gradebookerrors[0]); } /** * Test importing data and mapping it with items in the course. */ public function test_map_user_data_with_value(): void { // Need to add one of the users into the system. $user = new \stdClass(); $user->firstname = 'Anne'; $user->lastname = 'Able'; $user->email = 'student7@example.com'; $userdetail = $this->getDataGenerator()->create_user($user); $testarray = $this->csv_load($this->oktext); $testobject = new \phpunit_gradeimport_csv_load_data(); // We're not using scales so no to this option. $verbosescales = 0; // Map and key are to retrieve the grade_item that we are updating. $map = array(1); $key = 0; // Test new user mapping. This should return the user id if there were no problems. $userid = $testobject->test_map_user_data_with_value('useremail', $testarray[0][5], $this->columns, $map, $key, $this->courseid, $map[$key], $verbosescales); $this->assertEquals($userid, $userdetail->id); $newgrades = $testobject->test_map_user_data_with_value('new', $testarray[0][6], $this->columns, $map, $key, $this->courseid, $map[$key], $verbosescales); // Check that the final grade is the same as the one inserted. $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade); $newgrades = $testobject->test_map_user_data_with_value('new', $testarray[0][8], $this->columns, $map, $key, $this->courseid, $map[$key], $verbosescales); // Check that the final grade is the same as the one inserted. // The testobject should now contain 2 new grade items. $this->assertEquals(2, count($newgrades)); // Because this grade item is empty, the value for final grade should be null. $this->assertNull($newgrades[1]->finalgrade); $feedback = $testobject->test_map_user_data_with_value('feedback', $testarray[0][7], $this->columns, $map, $key, $this->courseid, $map[$key], $verbosescales); // Expected result. $resultarray = array(); $resultarray[0] = new \stdClass(); $resultarray[0]->itemid = 1; $resultarray[0]->feedback = $testarray[0][7]; $this->assertEquals($feedback, $resultarray); // Default behaviour (update a grade item). $newgrades = $testobject->test_map_user_data_with_value('default', $testarray[0][6], $this->columns, $map, $key, $this->courseid, $map[$key], $verbosescales); $this->assertEquals($testarray[0][6], $newgrades[0]->finalgrade); } /** * Test importing data into the gradebook. */ public function test_prepare_import_grade_data(): void { global $DB; // Need to add one of the users into the system. $user = new \stdClass(); $user->firstname = 'Anne'; $user->lastname = 'Able'; $user->email = 'student7@example.com'; // Insert user 1. $this->getDataGenerator()->create_user($user); $user = new \stdClass(); $user->firstname = 'Bobby'; $user->lastname = 'Bunce'; $user->email = 'student5@example.com'; // Insert user 2. $this->getDataGenerator()->create_user($user); $this->csv_load($this->oktext); $importcode = 007; $verbosescales = 0; // Form data object. $formdata = new \stdClass(); $formdata->mapfrom = 5; $formdata->mapto = 'useremail'; $formdata->mapping_0 = 0; $formdata->mapping_1 = 0; $formdata->mapping_2 = 0; $formdata->mapping_3 = 0; $formdata->mapping_4 = 0; $formdata->mapping_5 = 0; $formdata->mapping_6 = 'new'; $formdata->mapping_7 = 'feedback_2'; $formdata->mapping_8 = 0; $formdata->mapping_9 = 0; $formdata->map = 1; $formdata->id = 2; $formdata->iid = $this->iid; $formdata->importcode = $importcode; $formdata->forceimport = false; // Blam go time. $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); // If everything inserted properly then this should be true. $this->assertTrue($dataloaded); } /* * Test importing csv data into the gradebook using "Last downloaded from this course" column and force import option. */ public function test_force_import_option(): void { // Need to add users into the system. $user = new \stdClass(); $user->firstname = 'Anne'; $user->lastname = 'Able'; $user->email = 'student7@example.com'; $user->id_number = 1; $user1 = $this->getDataGenerator()->create_user($user); $user = new \stdClass(); $user->firstname = 'Bobby'; $user->lastname = 'Bunce'; $user->email = 'student5@example.com'; $user->id_number = 2; $user2 = $this->getDataGenerator()->create_user($user); // Create a new grade item. $params = array( 'itemtype' => 'manual', 'itemname' => 'Grade item 1', 'gradetype' => GRADE_TYPE_VALUE, 'courseid' => $this->courseid ); $gradeitem = new \grade_item($params, false); $gradeitemid = $gradeitem->insert(); $importcode = 001; $verbosescales = 0; // Form data object. $formdata = new \stdClass(); $formdata->mapfrom = 5; $formdata->mapto = 'useremail'; $formdata->mapping_0 = 0; $formdata->mapping_1 = 0; $formdata->mapping_2 = 0; $formdata->mapping_3 = 0; $formdata->mapping_4 = 0; $formdata->mapping_5 = 0; $formdata->mapping_6 = $gradeitemid; $formdata->mapping_7 = 'feedback_2'; $formdata->mapping_8 = 0; $formdata->mapping_9 = 0; $formdata->map = 1; $formdata->id = 2; $formdata->iid = $this->iid; $formdata->importcode = $importcode; $formdata->forceimport = false; // Add last download from this course column to csv content. $exportdate = time(); $newcsvdata = str_replace('{exportdate}', $exportdate, $this->csvtext); $this->csv_load($newcsvdata); $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertTrue($dataloaded); // We must update the last modified date. grade_import_commit($this->courseid, $importcode, false, false); // Test using force import disabled and a date in the past. $pastdate = strtotime('-1 day', time()); $newcsvdata = str_replace('{exportdate}', $pastdate, $this->csvtext); $this->csv_load($newcsvdata); $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertFalse($dataloaded); $errors = $testobject->get_gradebookerrors(); $this->assertEquals($errors[0], get_string('gradealreadyupdated', 'grades', fullname($user1))); // Test using force import enabled and a date in the past. $formdata->forceimport = true; $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertTrue($dataloaded); // Test importing using an old exported file (2 years ago). $formdata->forceimport = false; $twoyearsago = strtotime('-2 year', time()); $newcsvdata = str_replace('{exportdate}', $twoyearsago, $this->csvtext); $this->csv_load($newcsvdata); $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertFalse($dataloaded); $errors = $testobject->get_gradebookerrors(); $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades')); // Test importing using invalid exported date. $baddate = '0123A56B89'; $newcsvdata = str_replace('{exportdate}', $baddate, $this->csvtext); $this->csv_load($newcsvdata); $formdata->mapping_6 = $gradeitemid; $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertFalse($dataloaded); $errors = $testobject->get_gradebookerrors(); $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades')); // Test importing using date in the future. $oneyearahead = strtotime('+1 year', time()); $oldcsv = str_replace('{exportdate}', $oneyearahead, $this->csvtext); $this->csv_load($oldcsv); $formdata->mapping_6 = $gradeitemid; $testobject = new \phpunit_gradeimport_csv_load_data(); $dataloaded = $testobject->prepare_import_grade_data($this->columns, $formdata, $this->csvimport, $this->courseid, '', '', $verbosescales); $this->assertFalse($dataloaded); $errors = $testobject->get_gradebookerrors(); $this->assertEquals($errors[0], get_string('invalidgradeexporteddate', 'grades')); } } PK)B\oB88lang/en/dataformat_csv.phpnu[PK)B\+@M version.phpnu[PK)B\M+k classes/privacy/provider.phpnu[PK)B\=>>classes/writer.phpnu[PK\ ^ -db/access.phpnu[PK\'Glang/en/gradeimport_csv.phpnu[PK\otͮ  classes/output/renderer.phpnu[PK\Cjj+classes/load_data.phpnu[PK\C-- rindex.phpnu[PK\N4تtests/fixtures/phpunit_gradeimport_csv_load_data.phpnu[PK\W4d4dMtests/load_data_test.phpnu[PK !