. /** * Import attendance sessions class. * * @package mod_attendance * @copyright 2020 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_attendance\import; defined('MOODLE_INTERNAL') || die(); use csv_import_reader; use mod_attendance_notifyqueue; use mod_attendance_structure; use stdClass; /** * Import attendance sessions. * * @package mod_attendance * @copyright 2020 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class marksessions { /** @var string $error The errors message from reading the xml */ protected $error = ''; /** @var array $sessions The sessions info */ protected $sessions = array(); /** @var array $mappings The mappings info */ protected $mappings = array(); /** @var int The id of the csv import */ protected $importid = 0; /** @var csv_import_reader|null $importer */ protected $importer = null; /** @var array $foundheaders */ protected $foundheaders = array(); /** @var bool $useprogressbar Control whether importing should use progress bars or not. */ protected $useprogressbar = false; /** @var \core\progress\display_if_slow|null $progress The progress bar instance. */ protected $progress = null; /** @var mod_attendance_structure $att - the mod_attendance_structure class */ private $att; /** * Store an error message for display later * * @param string $msg */ public function fail($msg) { $this->error = $msg; return false; } /** * Get the CSV import id * * @return string The import id. */ public function get_importid() { return $this->importid; } /** * Get the list of headers found in the import. * * @return array The found headers (names from import) */ public function list_found_headers() { return $this->foundheaders; } /** * Read the data from the mapping form. * * @param array $data The mapping data. */ protected function read_mapping_data($data) { if ($data) { return array( 'user' => $data->userfrom, 'scantime' => $data->scantime, 'status' => $data->status ); } else { return array( 'user' => 0, 'scantime' => 1, 'status' => 2 ); } } /** * Get the a column from the imported data. * * @param array $row The imported raw row * @param int $index The column index we want * @return string The column data. */ protected function get_column_data($row, $index) { if ($index < 0) { return ''; } return isset($row[$index]) ? $row[$index] : ''; } /** * Constructor - parses the raw text for sanity. * * @param string $text The raw csv text. * @param mod_attendance_structure $att The current assignment * @param string $encoding The encoding of the csv file. * @param string $delimiter The specified delimiter for the file. * @param string $importid The id of the csv import. * @param array $mappingdata The mapping data from the import form. * @param bool $useprogressbar Whether progress bar should be displayed, to avoid html output on CLI. */ public function __construct($text = null, $att, $encoding = null, $delimiter = null, $importid = 0, $mappingdata = null, $useprogressbar = false) { global $CFG, $USER; require_once($CFG->libdir . '/csvlib.class.php'); $type = 'marksessions'; $this->att = $att; if (! $importid) { if ($text === null) { return; } $this->importid = csv_import_reader::get_new_iid($type); $this->importer = new csv_import_reader($this->importid, $type); if (! $this->importer->load_csv_content($text, $encoding, $delimiter)) { $this->fail(get_string('invalidimportfile', 'attendance')); $this->importer->cleanup(); echo $text; return; } } else { $this->importid = $importid; $this->importer = new csv_import_reader($this->importid, $type); } if (! $this->importer->init()) { $this->fail(get_string('invalidimportfile', 'attendance')); $this->importer->cleanup(); return; } $this->foundheaders = $this->importer->get_columns(); $this->useprogressbar = $useprogressbar; $sesslog = array(); $validusers = $this->att->get_users($this->att->pageparams->grouptype, 0); $users = array(); // Re-key validusers based on the identifier used by import. if (!empty($mappingdata) && $mappingdata->userto !== 'id') { foreach ($validusers as $u) { if (!empty($u->{$mappingdata->userto})) { $users[strtolower($u->{$mappingdata->userto})] = $u; } } } else { $users = $validusers; } $statuses = $this->att->get_statuses(); $statusmap = array(); foreach ($statuses as $st) { $statusmap[$st->acronym] = $st->id; } $sessioninfo = $this->att->get_session_info($this->att->pageparams->sessionid); while ($row = $this->importer->next()) { // This structure mimics what the UI form returns. if (empty($mappingdata)) { // Precheck - just return for now - would be nice to look at adding preview option in future. return; } $mapping = $this->read_mapping_data($mappingdata); // Get user. $extuser = strtolower($this->get_column_data($row, $mapping['user'])); if (empty($users[$extuser])) { $a = new \stdClass(); $a->extuser = $extuser; $a->userfield = $mappingdata->userto; \mod_attendance_notifyqueue::notify_problem(get_string('error:usernotfound', 'attendance', $a)); continue; } $userid = $users[$extuser]->id; if (isset($sesslog[$userid])) { \mod_attendance_notifyqueue::notify_problem(get_string('error:userduplicate', 'attendance', $extuser)); continue; } $sesslog[$userid] = new stdClass(); $sesslog[$userid]->studentid = $userid; $sesslog[$userid]->statusset = $statuses; $sesslog[$userid]->remarks = ''; $sesslog[$userid]->sessionid = $this->att->pageparams->sessionid; $sesslog[$userid]->timetaken = time(); $sesslog[$userid]->takenby = $USER->id; $scantime = $this->get_column_data($row, $mapping['scantime']); if (!empty($scantime)) { $t = strtotime($scantime); if ($t === false) { $a = new \stdClass(); $a->extuser = $extuser; $a->scantime = $scantime; \mod_attendance_notifyqueue::notify_problem(get_string('error:timenotreadable', 'attendance', $a)); continue; } $sesslog[$userid]->statusid = attendance_session_get_highest_status($this->att, $sessioninfo, $t); } else { $status = $this->get_column_data($row, $mapping['status']); if (!empty($statusmap[$status])) { $sesslog[$userid]->statusid = $statusmap[$status]; } else { $a = new \stdClass(); $a->extuser = $extuser; $a->status = $status; \mod_attendance_notifyqueue::notify_problem(get_string('error:statusnotfound', 'attendance', $a)); continue; } } } $this->sessions = $sesslog; $this->importer->close(); if (empty($sesslog)) { $this->fail(get_string('invalidimportfile', 'attendance')); return; } else { raise_memory_limit(MEMORY_EXTRA); // We are calling from browser, display progress bar. if ($this->useprogressbar === true) { $this->progress = new \core\progress\display_if_slow(get_string('processingfile', 'attendance')); $this->progress->start_html(); } else { $this->progress = new \core\progress\none(); } $this->progress->start_progress('', count($this->sessions)); $this->progress->end_progress(); } } /** * Get parse errors. * * @return array of errors from parsing the xml. */ public function get_error() { return $this->error; } /** * Create sessions using the CSV data. * * @return void */ public function import() { $this->att->save_log($this->sessions); \mod_attendance_notifyqueue::notify_success(get_string('sessionsupdated', 'mod_attendance')); } }