diff --git a/classes/form/import/marksessions.php b/classes/form/import/marksessions.php new file mode 100644 index 0000000..c8e5690 --- /dev/null +++ b/classes/form/import/marksessions.php @@ -0,0 +1,97 @@ +. + +/** + * This file contains the form used to upload a csv attendance file to automatically update attendance records. + * + * @package mod_attendance + * @copyright 2019 Jonathan Chan + * @copyright based on work by 2012 NetSpot {@link http://www.netspot.com.au} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_attendance\form\import; + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +use core_text; +use moodleform; +require_once($CFG->libdir.'/formslib.php'); + +/** + * Class for displaying the csv upload form. + * + * @package mod_attendance + * @copyright 2019 Jonathan Chan + * @copyright based on work by 2012 NetSpot {@link http://www.netspot.com.au} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class marksessions extends moodleform { + + /** + * Called to define this moodle form + * + * @return void + */ + public function definition() { + global $COURSE; + + $mform = $this->_form; + $params = $this->_customdata; + + $mform->addElement('header', 'uploadattendance', get_string('uploadattendance', 'attendance')); + + $fileoptions = array('subdirs' => 0, + 'maxbytes' => $COURSE->maxbytes, + 'accepted_types' => 'csv', + 'maxfiles' => 1); + + $mform->addElement('filepicker', 'attendancefile', get_string('uploadafile'), null, $fileoptions); + $mform->addRule('attendancefile', get_string('uploadnofilefound'), 'required', null, 'client'); + $mform->addHelpButton('attendancefile', 'attendancefile', 'attendance'); + + $encodings = core_text::get_encodings(); + $mform->addElement('select', 'encoding', get_string('encoding', 'grades'), $encodings); + $mform->addHelpButton('encoding', 'encoding', 'grades'); + + $radio = array(); + $radio[] = $mform->createElement('radio', 'separator', null, get_string('septab', 'grades'), 'tab'); + $radio[] = $mform->createElement('radio', 'separator', null, get_string('sepcomma', 'grades'), 'comma'); + $radio[] = $mform->createElement('radio', 'separator', null, get_string('sepcolon', 'grades'), 'colon'); + $radio[] = $mform->createElement('radio', 'separator', null, get_string('sepsemicolon', 'grades'), 'semicolon'); + $mform->addGroup($radio, 'separator', get_string('separator', 'grades'), ' ', false); + $mform->addHelpButton('separator', 'separator', 'grades'); + $mform->setDefault('separator', 'comma'); + + $mform->addElement('hidden', 'id', $params['id']); + $mform->setType('id', PARAM_INT); + $mform->addElement('hidden', 'sessionid', $params['sessionid']); + $mform->setType('sessionid', PARAM_INT); + $mform->addElement('hidden', 'grouptype', $params['grouptype']); + $mform->setType('grouptype', PARAM_INT); + $mform->addElement('hidden', 'confirm', 0); + $mform->setType('confirm', PARAM_BOOL); + $this->add_action_buttons(true, get_string('uploadattendance', 'attendance')); + } + /** + * Display an error on the import form. + * + * @param string $msg + */ + public function set_import_error($msg) { + $mform = $this->_form; + + $mform->setElementError('attendancefile', $msg); + } +} diff --git a/classes/form/import/marksessions_confirm.php b/classes/form/import/marksessions_confirm.php new file mode 100644 index 0000000..b0805bc --- /dev/null +++ b/classes/form/import/marksessions_confirm.php @@ -0,0 +1,132 @@ +. + +/** + * This file contains the form used to upload a csv attendance file to automatically update attendance records. + * + * @package mod_attendance + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace mod_attendance\form\import; + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +use core_text; +use moodleform; +require_once($CFG->libdir.'/formslib.php'); + +/** + * Mark attendance sessions confirm csv upload. + * + * @package mod_attendance + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class marksessions_confirm extends moodleform { + + /** + * Called to define this moodle form + * + * @return void + */ + public function definition() { + $params = $this->_customdata; + $importer = $this->_customdata['importer']; + + $mform = $this->_form; + $mform->addElement('hidden', 'confirm', 1); + $mform->setType('confirm', PARAM_BOOL); + + $foundheaders = $importer->list_found_headers(); + + // Add user mapping. + $mform->addElement('select', 'userfrom', get_string('userimportfield', 'attendance'), $foundheaders); + $mform->addHelpButton('userfrom', 'userimportfield', 'attendance'); + // This allows the user to choose which field in the user database the identifying column will map to. + $useroptions = array( + 'userid' => get_string('userid', 'attendance'), + 'username' => get_string('username'), + 'idnumber' => get_string('idnumber'), + 'email' => get_string('email') + ); + $mform->addElement('select', 'userto', get_string('userimportto', 'attendance'), $useroptions); + + // Check if we can set an easy default value. + foreach (array_keys($useroptions) as $o) { + if (in_array($o, $foundheaders)) { + $mform->setDefault('userto', $o); + $mform->setDefault('userfrom', $o); + break; + } + } + + $mform->addHelpButton('userto', 'userimportto', 'attendance'); + + // Below options need a "none" option in the headers. + $foundheaders[- 1] = get_string('notset', 'mod_attendance'); + ksort($foundheaders); + + // Add scan time mapping. + $mform->addElement('select', 'scantime', get_string('scantime', 'attendance'), $foundheaders); + $mform->addHelpButton('scantime', 'scantime', 'attendance'); + $mform->setDefault('scantime', -1); + + // Add status mapping. + $mform->addElement('select', 'status', get_string('importstatus', 'attendance'), $foundheaders); + $mform->addHelpButton('status', 'importstatus', 'attendance'); + $mform->disabledif('status', 'scantime', 'noteq', -1); + $mform->disabledif('scantime', 'status', 'noteq', -1); + + // Try to set a useful default value for scantime or status. + $key = array_search('status', $foundheaders); + + if ($key !== false) { + // Status is passed in CSV - set that as default. + $mform->setDefault('status', $key); + $mform->setDefault('scantime', -1); + } else { + $keyscan = array_search('scantime', $foundheaders); + if ($keyscan !== false) { + // The Scantime var exists in the csv. + $mform->setDefault('status', -1); + $mform->setDefault('scantime', $keyscan); + } else { + $mform->setDefault('status', -1); + $mform->setDefault('scantime', -1); + } + + } + foreach (array_keys($useroptions) as $o) { + if (in_array($o, $foundheaders)) { + $mform->setDefault('userto', $o); + $mform->setDefault('userfrom', $o); + break; + } + } + + $mform->addElement('hidden', 'id', $params['id']); + $mform->setType('id', PARAM_INT); + $mform->addElement('hidden', 'sessionid', $params['sessionid']); + $mform->setType('sessionid', PARAM_INT); + $mform->addElement('hidden', 'grouptype', $params['grouptype']); + $mform->setType('grouptype', PARAM_INT); + $mform->addElement('hidden', 'importid', $importer->get_importid()); + $mform->setType('importid', PARAM_INT); + $mform->setConstant('importid', $importer->get_importid()); + + $this->add_action_buttons(true, get_string('uploadattendance', 'attendance')); + } +} diff --git a/classes/import/marksessions.php b/classes/import/marksessions.php new file mode 100644 index 0000000..8a00a7f --- /dev/null +++ b/classes/import/marksessions.php @@ -0,0 +1,302 @@ +. + +/** + * 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[$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 = $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')); + } +} diff --git a/classes/structure.php b/classes/structure.php index 187d762..87e0e86 100644 --- a/classes/structure.php +++ b/classes/structure.php @@ -703,16 +703,18 @@ class mod_attendance_structure { /** * Take attendance from form data. * - * @param stdClass $formdata + * @param stdClass $data */ - public function take_from_form_data($formdata) { - global $DB, $USER; - // TODO: WARNING - $formdata is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below. - // This whole function could do with a nice clean up. + public function take_from_form_data($data) { + global $USER; + // WARNING - $data is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below. + $statuses = implode(',', array_keys( (array)$this->get_statuses() )); $now = time(); $sesslog = array(); - $formdata = (array)$formdata; + + $formdata = (array)$data; + foreach ($formdata as $key => $value) { // Look at Remarks field because the user options may not be passed if empty. if (substr($key, 0, 7) == 'remarks') { @@ -722,7 +724,7 @@ class mod_attendance_structure { } $sesslog[$sid] = new stdClass(); $sesslog[$sid]->studentid = $sid; // We check is_numeric on this above. - if (array_key_exists('user'.$sid, $formdata) && is_numeric($formdata['user' . $sid])) { + if (array_key_exists('user' . $sid, $formdata) && is_numeric($formdata['user' . $sid])) { $sesslog[$sid]->statusid = $formdata['user' . $sid]; } $sesslog[$sid]->statusset = $statuses; @@ -732,6 +734,19 @@ class mod_attendance_structure { $sesslog[$sid]->takenby = $USER->id; } } + + $this->save_log($sesslog); + } + + /** + * Helper function to save attendance and trigger events. + * + * @param array $sesslog + * @throws coding_exception + * @throws dml_exception + */ + public function save_log($sesslog) { + global $DB, $USER; // Get existing session log. $dbsesslog = $this->get_session_log($this->pageparams->sessionid); foreach ($sesslog as $log) { @@ -754,7 +769,7 @@ class mod_attendance_structure { } $session = $this->get_session_info($this->pageparams->sessionid); - $session->lasttaken = $now; + $session->lasttaken = time(); $session->lasttakenby = $USER->id; $DB->update_record('attendance_sessions', $session); diff --git a/import/marksessions.php b/import/marksessions.php new file mode 100644 index 0000000..378b17c --- /dev/null +++ b/import/marksessions.php @@ -0,0 +1,122 @@ +. + +/** + * Mark attendance sessions using a csv import. + * + * @package mod_attendance + * @author Dan Marsden + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('NO_OUTPUT_BUFFERING', true); + +require(__DIR__ . '/../../../config.php'); +require_once($CFG->dirroot . '/mod/attendance/lib.php'); +require_once($CFG->dirroot . '/mod/attendance/locallib.php'); + +$pageparams = new mod_attendance_take_page_params(); + +$id = required_param('id', PARAM_INT); +$pageparams->sessionid = required_param('sessionid', PARAM_INT); +$pageparams->grouptype = optional_param('grouptype', null, PARAM_INT); +$pageparams->page = optional_param('page', 1, PARAM_INT); +$importid = optional_param('importid', null, PARAM_INT); + +$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attendance', array('id' => $cm->instance), '*', MUST_EXIST); + +// Check this is a valid session for this attendance. +$session = $DB->get_record('attendance_sessions', array('id' => $pageparams->sessionid, 'attendanceid' => $att->id), + '*', MUST_EXIST); + +require_login($course, true, $cm); +$context = context_module::instance($cm->id); +require_capability('mod/attendance:takeattendances', $context); + +$pageparams->init($course->id); + +$PAGE->set_context($context); +$url = new moodle_url('/mod/attendance/import/marksessions.php'); +$PAGE->set_url($url); +$PAGE->set_title($course->shortname. ": ".$att->name); +$PAGE->set_heading($course->fullname); +$PAGE->set_cacheable(true); +$PAGE->navbar->add($att->name); + +$att = new mod_attendance_structure($att, $cm, $course, $PAGE->context, $pageparams); + +// Form processing and displaying is done here. +$output = $PAGE->get_renderer('mod_attendance'); + +$formparams = ['id' => $cm->id, + 'sessionid' => $pageparams->sessionid, + 'grouptype' => $pageparams->grouptype]; +$form = null; +if (optional_param('needsconfirm', 0, PARAM_BOOL)) { + $form = new \mod_attendance\form\import\marksessions($url->out(false), $formparams); +} else if (optional_param('confirm', 0, PARAM_BOOL)) { + $importer = new \mod_attendance\import\marksessions(null, $att, null, null, $importid); + $formparams['importer'] = $importer; + $form = new \mod_attendance\form\import\marksessions_confirm(null, $formparams); +} else { + $form = new \mod_attendance\form\import\marksessions($url->out(false), $formparams); +} + +if ($form->is_cancelled()) { + redirect(new moodle_url('/mod/attendance/take.php', + array('id' => $cm->id, + 'sessionid' => $pageparams->sessionid, + 'grouptype' => $pageparams->grouptype))); + return; +} else if ($data = $form->get_data()) { + if ($data->confirm) { + $importid = $data->importid; + $importer = new \mod_attendance\import\marksessions(null, $att, null, null, $importid, $data, true); + $error = $importer->get_error(); + if ($error) { + $form = new \mod_attendance\form\import\marksessions($url->out(false), $formparams); + $form->set_import_error($error); + } else { + echo $output->header(); + $sessions = $importer->import(); + mod_attendance_notifyqueue::show(); + $url = new moodle_url('/mod/attendance/manage.php', array('id' => $att->cmid)); + echo $output->continue_button($url); + echo $output->footer(); + die(); + } + } else { + $text = $form->get_file_content('attendancefile'); + $encoding = $data->encoding; + $delimiter = $data->separator; + $importer = new \mod_attendance\import\marksessions($text, $att, $encoding, $delimiter, 0, null, true); + $formparams['importer'] = $importer; + $confirmform = new \mod_attendance\form\import\marksessions_confirm(null, $formparams); + $form = $confirmform; + $pagetitle = get_string('confirmcolumnmappings', 'attendance'); + } +} + +// Output for the file upload form starts here. +echo $output->header(); +echo $output->heading(get_string('attendanceforthecourse', 'attendance') . ' :: ' . format_string($course->fullname)); +echo $output->box(get_string('marksessionimportcsvhelp', 'attendance')); +mod_attendance_notifyqueue::show(); +$form->display(); +echo $output->footer(); diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 75c771d..199f7ad 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -30,13 +30,15 @@ $string['Lacronym'] = 'L'; $string['Lfull'] = 'Late'; $string['Pacronym'] = 'P'; $string['Pfull'] = 'Present'; -$string['acronym'] = 'Acronym'; $string['absenteereport'] = 'Absentee report'; +$string['acronym'] = 'Acronym'; $string['add'] = 'Add'; +$string['addedrecip'] = 'Added {$a} new recipient'; +$string['addedrecips'] = 'Added {$a} new recipients'; $string['addmultiplesessions'] = 'Multiple sessions'; -$string['addwarning'] = 'Add warning'; $string['addsession'] = 'Add session'; $string['adduser'] = 'Add user'; +$string['addwarning'] = 'Add warning'; $string['all'] = 'All'; $string['allcourses'] = 'All courses'; $string['allpast'] = 'All past'; @@ -54,7 +56,10 @@ $string['attendance:viewreports'] = 'Viewing Reports'; $string['attendance:viewsummaryreports'] = 'View course summary reports'; $string['attendance:warningemails'] = 'Can be subscribed to emails with absentee users'; $string['attendance_already_submitted'] = 'Your attendance has already been set.'; +$string['attendance_no_status'] = 'No valid status was available - you may be too late to record attendance.'; $string['attendancedata'] = 'Attendance data'; +$string['attendancefile'] = 'Attendance file (csv format)'; +$string['attendancefile_help'] = 'The file must be a CSV file with a header row and fields for identifying the user and the time attendance was recorded eg (email,scantime) or (username,time)'; $string['attendanceforthecourse'] = 'Attendance for the course'; $string['attendancegrade'] = 'Attendance grade'; $string['attendancenotset'] = 'You must set your attendance'; @@ -67,16 +72,20 @@ $string['attendancesuccess'] = 'Attendance has been successfully taken'; $string['attendanceupdated'] = 'Attendance successfully updated'; $string['attforblockdirstillexists'] = 'old mod/attforblock directory still exists - you must delete this directory on your server before running this upgrade.'; $string['attrecords'] = 'Attendances records'; +$string['autoassignstatus'] = 'Automatically select highest status available'; +$string['autoassignstatus_help'] = 'If this is selected, students will automatically be assigned the highest available grade.'; $string['automark'] = 'Automatic marking'; -$string['automarkall'] = 'Yes'; -$string['automarkclose'] = 'Set unmarked at end of session'; $string['automark_help'] = 'Allows marking to be completed automatically. If "Yes" students will be automatically marked depending on their first access to the course. If "Set unmarked at end of session" any students who have not marked their attendance will be set to the unmarked status selected.'; +$string['automarkall'] = 'Yes'; +$string['automarkclose'] = 'Set unmarked at end of session'; $string['automarktask'] = 'Check for attendance sessions that require auto marking'; $string['autorecorded'] = 'system auto recorded'; $string['averageattendance'] = 'Average attendance'; $string['averageattendancegraded'] = 'Average attendance'; +$string['backtoparticipants'] = 'Back to participants list'; +$string['below'] = 'Below {$a}%'; $string['calclose'] = 'Close'; $string['calendarevent'] = 'Create calendar event for session'; $string['calendarevent_help'] = 'If enabled, a calendar event will be created for this session. @@ -96,6 +105,8 @@ $string['changesession'] = 'Change session'; $string['checkweekdays'] = 'Select weekdays that fall within your selected session date range.'; $string['closed'] = 'This session is not currently available for self-marking'; $string['column'] = 'column'; +$string['columnmap'] = 'Column mapping'; +$string['columnmap_help'] = 'For each of the fields presented, select the corresponding column in the csv file.'; $string['columns'] = 'columns'; $string['commonsession'] = 'All students'; $string['commonsessions'] = 'All students'; @@ -106,6 +117,7 @@ $string['confirmdeleteuser'] = 'Are you sure you want to delete user \'{$a->full $string['copyfrom'] = 'Copy attendance data from'; $string['countofselected'] = 'Count of selected'; $string['course'] = 'Course'; +$string['coursemessage'] = 'Message course users'; $string['courseshortname'] = 'Course shortname'; $string['coursesummary'] = 'Course summary report'; $string['createmultiplesessions'] = 'Create multiple sessions'; @@ -116,16 +128,12 @@ The sessions begin on the date of the base session and continue until the \'repe * Repeat every: This allows for a frequency setting. If your class will meet every week, select 1; if it will meet every other week, select 2; every 3rd week, select 3, etc. * Repeat until: Select the last day of class (the last day you want to take attendance). '; -$string['autoassignstatus'] = 'Automatically select highest status available'; -$string['autoassignstatus_help'] = 'If this is selected, students will automatically be assigned the highest available grade.'; $string['createonesession'] = 'Create one session for the course'; $string['csvdelimiter'] = 'CSV delimiter'; +$string['currentlyselectedusers'] = 'Currently selected users'; $string['date'] = 'Date'; $string['days'] = 'Days'; $string['defaultdisplaymode'] = 'Default display mode'; -$string['defaultwarnings'] = 'Default warning set'; -$string['defaultwarningsettings'] = 'Default warning settings'; -$string['defaultwarningsettings_help'] = 'These settings define the defaults for all new warnings'; $string['defaults'] = 'Defaults'; $string['defaultsessionsettings'] = 'Default session settings'; $string['defaultsessionsettings_help'] = 'These settings define the defaults for all new sessions'; @@ -136,16 +144,19 @@ $string['defaultsubnet'] = 'Default network address'; $string['defaultsubnet_help'] = 'Attendance recording may be restricted to particular subnets by specifying a comma-separated list of partial or full IP addresses. This is the default value used when creating new sessions.'; $string['defaultview'] = 'Default view on login'; $string['defaultview_desc'] = 'This is the default view shown to teachers on first login.'; +$string['defaultwarnings'] = 'Default warning set'; +$string['defaultwarningsettings'] = 'Default warning settings'; +$string['defaultwarningsettings_help'] = 'These settings define the defaults for all new warnings'; $string['delete'] = 'Delete'; -$string['deletewarningconfirm'] = 'Are you sure you want to delete this warning?'; -$string['deletedgroup'] = 'The group associated with this session has been deleted'; $string['deletecheckfull'] = 'Are you absolutely sure you want to completely delete the {$a}, including all user data?'; +$string['deletedgroup'] = 'The group associated with this session has been deleted'; $string['deletehiddensessions'] = 'Delete all hidden sessions'; $string['deletelogs'] = 'Delete attendance data'; $string['deleteselected'] = 'Delete selected'; $string['deletesession'] = 'Delete session'; $string['deletesessions'] = 'Delete all sessions'; $string['deleteuser'] = 'Delete user'; +$string['deletewarningconfirm'] = 'Are you sure you want to delete this warning?'; $string['deletingsession'] = 'Deleting session for the course'; $string['deletingstatus'] = 'Deleting status for the course'; $string['description'] = 'Description'; @@ -158,11 +169,11 @@ $string['downloadtext'] = 'Download in text format'; $string['duration'] = 'Duration'; $string['editsession'] = 'Edit Session'; $string['edituser'] = 'Edit user'; +$string['emailcontent'] = 'Email content'; $string['emailcontent_default'] = 'Hi %userfirstname%, Your attendance in %coursename% %attendancename% has dropped below %warningpercent% and is currently %percent% - we hope you are ok! To get the most out of this course you should improve your attendance, please get in touch if you require any further support.'; -$string['emailcontent'] = 'Email content'; $string['emailcontent_help'] = 'When a warning is sent to a student, it takes the email content from this field. The following wildcards can be used: '; - $string['emailsubject'] = 'Email subject'; -$string['emailsubject_help'] = 'When a warning is sent to a student, it takes the email subject from this field.'; $string['emailsubject_default'] = 'Attendance warning'; +$string['emailsubject_help'] = 'When a warning is sent to a student, it takes the email subject from this field.'; $string['emailuser'] = 'Email user'; $string['emailuser_help'] = 'If checked, a warning will be sent to the student.'; $string['emptyacronym'] = 'Empty acronyms are not allowed. Status record not updated.'; @@ -190,18 +200,24 @@ $string['enablecalendar_desc'] = 'If enabled, a calendar event will be created f $string['enablewarnings'] = 'Enable warnings'; $string['enablewarnings_desc'] = 'This allows a warning set to be defined for an attendance and email notifications to users when attendance drops below the configured threshold.
WARNING: This is a new feature and has not been tested extensively. Please use at your own-risk and provide feeback in the moodle forums if you find it works well.'; $string['encoding'] = 'Encoding'; +$string['encoding_help'] = 'This refers to the type of barcode encoding used on the students\' id card. Typical types of barcode encoding schemes include Code-39, Code-128 and UPC-A.'; $string['endofperiod'] = 'End of period'; $string['endtime'] = 'Session end time'; $string['enrolmentend'] = 'User enrolment ends {$a}'; $string['enrolmentstart'] = 'User enrolment starts {$a}'; $string['enrolmentsuspended'] = 'Enrolment suspended'; -$string['error:coursenotfound'] = 'A course with the short name {$a} can not be found.'; +$string['enterpassword'] = 'Enter password'; $string['error:coursehasnoattendance'] = 'The course with the short name {$a} has no attendance activities.'; +$string['error:coursenotfound'] = 'A course with the short name {$a} can not be found.'; +$string['error:qrcode'] = 'Allow students to record own attendance must be enabled to use QR code! Skipping.'; $string['error:sessioncourseinvalid'] = 'A session course is invalid! Skipping.'; $string['error:sessiondateinvalid'] = 'A session date is invalid! Skipping.'; $string['error:sessionendinvalid'] = 'A session end time is invalid! Skipping.'; $string['error:sessionstartinvalid'] = 'A session start time is invalid! Skipping.'; -$string['error:qrcode'] = 'Allow students to record own attendance must be enabled to use QR code! Skipping.'; +$string['error:statusnotfound'] = 'User: {$a->extuser} has a status value that could not be found: {$a->status}'; +$string['error:timenotreadable'] = 'User: {$a->extuser} has a scantime that could not be converted by strtotime: {$a->scantime}'; +$string['error:userduplicate'] = 'User {$a} was found twice in the import. please only include one record per user.'; +$string['error:usernotfound'] = 'A user with the {$a->userfield} set to {$a->extuser} could not be found'; $string['errorgroupsnotselected'] = 'Select one or more groups'; $string['errorinaddingsession'] = 'Error in adding session'; $string['erroringeneratingsessions'] = 'Error in generating sessions '; @@ -211,8 +227,8 @@ $string['eventscreated'] = 'Calendar events created'; $string['eventsdeleted'] = 'Calendar events deleted'; $string['eventsessionadded'] = 'Session added'; $string['eventsessiondeleted'] = 'Session deleted'; -$string['eventsessionsimported'] = 'Sessions imported'; $string['eventsessionipshared'] = 'Attendance self-marking IP conflict'; +$string['eventsessionsimported'] = 'Sessions imported'; $string['eventsessionupdated'] = 'Session updated'; $string['eventstatusadded'] = 'Status added'; $string['eventstatusupdated'] = 'Status updated'; @@ -221,11 +237,13 @@ $string['eventtaken'] = 'Attendance taken'; $string['eventtakenbystudent'] = 'Attendance taken by student'; $string['export'] = 'Export'; $string['extrarestrictions'] = 'Extra restrictions'; +$string['formattexttype'] = 'Formatting'; $string['from'] = 'from:'; $string['gradebookexplanation'] = 'Grade in gradebook'; $string['gradebookexplanation_help'] = 'The Attendance module displays your current attendance grade based on the number of points you have earned to date and the number of points that could have been earned to date; it does not include class periods in the future. In the gradebook, your attendance grade is based on your current attendance percentage and the number of points that can be earned over the entire duration of the course, including future class periods. As such, your attendance grades displayed in the Attendance module and in the gradebook may not be the same number of points but they are the same percentage. For example, if you have earned 8 of 10 points to date (80% attendance) and attendance for the entire course is worth 50 points, the Attendance module will display 8/10 and the gradebook will display 40/50. You have not yet earned 40 points but 40 is the equivalent point value to your current attendance percentage of 80%. The point value you have earned in the Attendance module can never decrease, as it is based only on attendance to date; however, the attendance point value shown in the gradebook may increase or decrease depending on your future attendance, as it is based on attendance for the entire course.'; +$string['graded'] = 'Graded sessions'; $string['gridcolumns'] = 'Grid columns'; $string['group'] = 'Group'; $string['groups'] = 'Groups'; @@ -237,20 +255,18 @@ You can use this feature to hide older sessions instead of deleting them. Only v $string['hiddensessionsdeleted'] = 'All hidden sessions were delete'; $string['hideextrauserdetails'] = 'Hide extra user details'; $string['hidensessiondetails'] = 'Hide session details'; +$string['identifyby'] = 'Identify student by'; $string['import'] = 'Import'; $string['importfile'] = 'Import file'; $string['importfile_help'] = 'Import file'; $string['importsessions'] = 'Import Sessions'; -$string['identifyby'] = 'Identify student by'; +$string['importstatus'] = 'Status field'; +$string['importstatus_help'] = 'This allows a status value to be included in the import - eg values like P, L, or A'; +$string['includeabsentee'] = 'Include session when calculating absentee report'; +$string['includeabsentee_help'] = 'If checked this session will be included in the absentee report calculations.'; $string['includeall'] = 'Select all sessions'; $string['includenottaken'] = 'Include not taken sessions'; $string['includeqrcode'] = 'Include QR code'; -$string['rotateqrcode'] = 'Rotate QR code'; -$string['rotateqrcodeinterval'] = 'Rotate QR code/password interval (seconds)'; -$string['rotateqrcodeinterval_desc'] = 'Time interval (seconds) to rotate QR code/password by.'; -$string['rotateqrcodeexpirymargin'] = 'Rotate QR code/password expiry margin (seconds)'; -$string['rotateqrcodeexpirymargin_desc'] = 'Time interval (seconds) to allow expired QR code/password by.'; -$string['rotateqrcode_cleartemppass_task'] = 'Task to clear temporary passwords generated by rotate QR code functionality.'; $string['includeremarks'] = 'Include remarks'; $string['incorrectpassword'] = 'You have entered an incorrect password and your attendance has not been recorded, please enter the correct password.'; $string['incorrectpasswordshort'] = 'Incorrect password, attendance not recorded.'; @@ -263,7 +279,8 @@ $string['invalidsessionendtime'] = 'The end time must be greater than start time $string['invalidstatus'] = 'You have selected an invalid status, please try again'; $string['iptimemissing'] = 'Invalid minutes to release'; $string['jumpto'] = 'Jump to'; -$string['below'] = 'Below {$a}%'; +$string['keepsearching'] = 'Keep searching'; +$string['marksessionimportcsvhelp'] = 'This form allows you to upload a csv file containing a user identifier and a status - the status field can be the status acronym or the time that attendance was recorded for that user. If a time value is passed then it will try to assign the status value with the highest grade available at that time.'; $string['maxpossible'] = 'Maximum possible'; $string['maxpossible_help'] = 'Shows the score each user can reach if they receive the maximum points in each session not yet taken (past and future):