From 38771055ec83ee34b3c4ce5be01f06454a281b97 Mon Sep 17 00:00:00 2001 From: Antony Videtta Date: Tue, 29 Jun 2021 13:48:46 +1200 Subject: [PATCH] Feature: add auto-marking by linked activity comletion. --- backup/moodle2/backup_attendance_stepslib.php | 2 +- classes/form/addsession.php | 9 +++- classes/form/updatesession.php | 8 ++- classes/task/auto_mark.php | 39 +++++++++++++-- db/install.xml | 1 + db/upgrade.php | 15 ++++++ externallib.php | 1 + lang/en/attendance.php | 2 + locallib.php | 49 +++++++++++++++++++ renderhelpers.php | 3 +- 10 files changed, 122 insertions(+), 7 deletions(-) diff --git a/backup/moodle2/backup_attendance_stepslib.php b/backup/moodle2/backup_attendance_stepslib.php index 67e90f4..a98e79b 100644 --- a/backup/moodle2/backup_attendance_stepslib.php +++ b/backup/moodle2/backup_attendance_stepslib.php @@ -59,7 +59,7 @@ class backup_attendance_activity_structure_step extends backup_activity_structur 'groupid', 'sessdate', 'duration', 'lasttaken', 'lasttakenby', 'timemodified', 'description', 'descriptionformat', 'studentscanmark', 'studentpassword', 'autoassignstatus', 'subnet', 'automark', 'automarkcompleted', 'statusset', 'absenteereport', 'preventsharedip', - 'preventsharediptime', 'caleventid', 'calendarevent', 'includeqrcode')); + 'preventsharediptime', 'caleventid', 'calendarevent', 'includeqrcode', 'automarkcmid')); // XML nodes declaration - user data. $logs = new backup_nested_element('logs'); diff --git a/classes/form/addsession.php b/classes/form/addsession.php index 4bf381c..ece7d8b 100644 --- a/classes/form/addsession.php +++ b/classes/form/addsession.php @@ -46,7 +46,7 @@ class addsession extends moodleform { */ public function definition() { - global $CFG, $USER; + global $CFG, $USER, $DB; $mform =& $this->_form; $course = $this->_customdata['course']; @@ -215,6 +215,12 @@ class addsession extends moodleform { $mform->addHelpButton('automark', 'automark', 'attendance'); $mform->setDefault('automark', $this->_customdata['att']->automark); + $automarkcmoptions = attendance_get_coursemodulenames($course->id); + + $mform->addElement('select', 'automarkcmid', get_string('selectactivity', 'attendance'), $automarkcmoptions); + $mform->setType('automarkcmid', PARAM_INT); + $mform->hideif('automarkcmid', 'automark', 'neq', '3'); + if (!empty($studentscanmark)) { $mgroup = array(); @@ -240,6 +246,7 @@ class addsession extends moodleform { $mform->addElement('checkbox', 'autoassignstatus', '', get_string('autoassignstatus', 'attendance')); $mform->addHelpButton('autoassignstatus', 'autoassignstatus', 'attendance'); $mform->hideif('autoassignstatus', 'studentscanmark', 'notchecked'); + if (isset($pluginconfig->autoassignstatus)) { $mform->setDefault('autoassignstatus', $pluginconfig->autoassignstatus); } diff --git a/classes/form/updatesession.php b/classes/form/updatesession.php index 0dcaf11..c4ce7da 100644 --- a/classes/form/updatesession.php +++ b/classes/form/updatesession.php @@ -41,7 +41,7 @@ class updatesession extends \moodleform { */ public function definition() { - global $DB; + global $DB, $COURSE; $mform =& $this->_form; $modcontext = $this->_customdata['modcontext']; @@ -148,6 +148,12 @@ class updatesession extends \moodleform { $mform->setType('automark', PARAM_INT); $mform->addHelpButton('automark', 'automark', 'attendance'); + $automarkcmoptions2 = attendance_get_coursemodulenames($COURSE->id); + + $mform->addElement('select', 'automarkcmid', get_string('selectactivity', 'attendance'), $automarkcmoptions2); + $mform->setType('automarkcmid', PARAM_INT); + $mform->hideif('automarkcmid', 'automark', 'neq', '3'); + if (!empty($studentscanmark)) { $mform->addElement('text', 'studentpassword', get_string('studentpassword', 'attendance')); $mform->setType('studentpassword', PARAM_TEXT); diff --git a/classes/task/auto_mark.php b/classes/task/auto_mark.php index c30b258..7d7876c 100644 --- a/classes/task/auto_mark.php +++ b/classes/task/auto_mark.php @@ -34,6 +34,7 @@ require_once($CFG->dirroot . '/mod/attendance/locallib.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class auto_mark extends \core\task\scheduled_task { + /** * Returns localised general event name. * @@ -76,10 +77,12 @@ class auto_mark extends \core\task\scheduled_task { $setunmarked = $DB->get_field('attendance_statuses', 'id', array('attendanceid' => $session->attendanceid, 'setnumber' => $session->statusset, 'setunmarked' => 1, 'deleted' => 0)); + if (empty($setunmarked)) { mtrace("No unmarked status configured for session id: ".$session->id); continue; } + if (empty($cacheatt[$session->attendanceid])) { $cacheatt[$session->attendanceid] = $DB->get_record('attendance', array('id' => $session->attendanceid)); } @@ -102,6 +105,9 @@ class auto_mark extends \core\task\scheduled_task { } $pageparams->sessionid = $session->id; + $att = new \mod_attendance_structure($cacheatt[$session->attendanceid], + $cachecm[$session->attendanceid], $cachecourse[$courseid], $context, $pageparams); + if ($session->automark == 1) { $userfirstacess = array(); // If set to do full automarking, get all users that have accessed course during session open. @@ -136,12 +142,39 @@ class auto_mark extends \core\task\scheduled_task { } } $logusers->close(); + + } else if ($session->automark == 3) { + + $completedusers = array(); + $newlog = new \stdClass(); + $newlog->timetaken = $now; + $newlog->takenby = 0; + $newlog->sessionid = $session->id; + $newlog->remarks = get_string('autorecorded', 'attendance'); + $newlog->statusset = implode(',', array_keys( (array)$att->get_statuses())); + + // Get users who have completed the course in this session. + $completedusers[] = $DB->get_record('course_modules_completion', array( + 'coursemoduleid' => $session->automarkcmid, + 'completionstate' => 1 + )); + + if (!empty($completedusers)) { + + // Get automark status the users and update the attendance log. + foreach ($completedusers as $completionuser) { + + $newlog->statusid = $att->get_automark_status($completionuser->timemodified, $session->id); + + if (!empty($newlog->statusid)) { + $newlog->studentid = $completionuser->userid; + $DB->insert_record('attendance_log', $newlog); + } + } + } } // Get all unmarked students. - $att = new \mod_attendance_structure($cacheatt[$session->attendanceid], - $cachecm[$session->attendanceid], $cachecourse[$courseid], $context, $pageparams); - $users = $att->get_users($session->groupid, 0); $existinglog = $DB->get_recordset('attendance_log', array('sessionid' => $session->id)); diff --git a/db/install.xml b/db/install.xml index ae0d375..8a0f170 100755 --- a/db/install.xml +++ b/db/install.xml @@ -52,6 +52,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index d3f1c16..5ea87d9 100755 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -650,5 +650,20 @@ function xmldb_attendance_upgrade($oldversion=0) { upgrade_mod_savepoint(true, 2021050700, 'attendance'); } + if ($oldversion < 2021060700) { + + // Changing precision of field statusset on table attendance_log to (1333). + $table = new xmldb_table('attendance_sessions'); + $field = new xmldb_field('automarkcmid', XMLDB_TYPE_CHAR, '10', null, null, null, null, 'rotateqrcodesecret'); + + // Launch change of precision for field statusset. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Attendance savepoint reached. + upgrade_mod_savepoint(true, 2021060700, 'attendance'); + } + return $result; } diff --git a/externallib.php b/externallib.php index 50f65ef..163a880 100644 --- a/externallib.php +++ b/externallib.php @@ -255,6 +255,7 @@ class mod_attendance_external extends external_api { $sess->subnet = $attendance->subnet; $sess->statusset = 0; $sess->groupid = $groupid; + $sess->automarkcmid = 0; $sessionid = $attendance->add_session($sess); return array('sessionid' => $sessionid); diff --git a/lang/en/attendance.php b/lang/en/attendance.php index df0b275..2f30a9f 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -76,11 +76,13 @@ $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['selectactivity'] = 'Select activity'; $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['onactivitycompletion'] = 'On activity completion'; $string['automarktask'] = 'Check for attendance sessions that require auto marking'; $string['autorecorded'] = 'system auto recorded'; $string['averageattendance'] = 'Average attendance'; diff --git a/locallib.php b/locallib.php index fb7c65b..bc35188 100644 --- a/locallib.php +++ b/locallib.php @@ -42,6 +42,8 @@ define('ATT_SORT_FIRSTNAME', 2); define('ATTENDANCE_AUTOMARK_DISABLED', 0); define('ATTENDANCE_AUTOMARK_ALL', 1); define('ATTENDANCE_AUTOMARK_CLOSE', 2); +define('ATTENDANCE_AUTOMARK_ACTIVITYCOMPLETION', 3); + define('ATTENDANCE_SHAREDIP_DISABLED', 0); define('ATTENDANCE_SHAREDIP_MINUTES', 1); @@ -788,6 +790,12 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st } $sess->automark = $formdata->automark; $sess->automarkcompleted = 0; + + if (!empty($formdata->automarkcmid)) { + $sess->automarkcmid = $formdata->automarkcmid; + } else { + $sess->automarkcmid = 0; + } if (!empty($formdata->preventsharedip)) { $sess->preventsharedip = $formdata->preventsharedip; } @@ -852,6 +860,13 @@ function attendance_construct_sessions_data_for_add($formdata, mod_attendance_st $sess->studentpassword = ''; $sess->automark = 0; $sess->automarkcompleted = 0; + + if (!empty($formdata->automarkcmid)) { + $sess->automarkcmid = $formdata->automarkcmid; + } else { + $sess->automarkcmid = 0; + } + $sess->absenteereport = $absenteereport; $sess->includeqrcode = 0; $sess->rotateqrcode = 0; @@ -1150,15 +1165,49 @@ function attendance_session_get_highest_status(mod_attendance_structure $att, $a * @return array */ function attendance_get_automarkoptions() { + $options = array(); + $options[ATTENDANCE_AUTOMARK_DISABLED] = get_string('noautomark', 'attendance'); if (strpos(get_config('tool_log', 'enabled_stores'), 'logstore_standard') !== false) { $options[ATTENDANCE_AUTOMARK_ALL] = get_string('automarkall', 'attendance'); } $options[ATTENDANCE_AUTOMARK_CLOSE] = get_string('automarkclose', 'attendance'); + $options[ATTENDANCE_AUTOMARK_ACTIVITYCOMPLETION] = get_string('onactivitycompletion', 'attendance'); + return $options; } +/** + * Get course module names associated to this course, if they're visible and complete. + * @param int $id - course id. + * @return array $automarkcmoptions - list of course module names associated to this course. + */ +function attendance_get_coursemodulenames($id) { + + global $DB; + $automarkcmoptions = []; + + $sql = "SELECT DISTINCT cm.id, cm.module, m.name + FROM {course_modules} cm + JOIN {modules} m ON m.id = cm.module + WHERE cm.course = :cmcourse + AND cm.visible = :cmvisible + AND cm.completion = :cmcompletion"; + + $coursemodulenames = $DB->get_records_sql($sql, array( + 'cmcourse' => $id, + 'cmvisible' => 1, + 'cmcompletion' => 1 + )); + if ($coursemodulenames) { + foreach ($coursemodulenames as $coursemodulename) { + $automarkcmoptions[$coursemodulename->id] = format_string($coursemodulename->name); + } + } + return $automarkcmoptions; +} + /** * Get available sharedip options. * diff --git a/renderhelpers.php b/renderhelpers.php index c5754e1..86783d1 100644 --- a/renderhelpers.php +++ b/renderhelpers.php @@ -68,7 +68,8 @@ class user_sessions_cells_generator { $this->construct_existing_status_cell($this->reportdata->statuses[$statusid]->acronym . " ({$points}/{$maxpoints})"); } else { - if (!empty($this->reportdata->allstatuses[$statusid] && isset($this->reportdata->allstatuses[$statusid]->acronym))) { + if (!empty($this->reportdata->allstatuses[$statusid] && + isset($this->reportdata->allstatuses[$statusid]->acronym))) { $statusac = $this->reportdata->allstatuses[$statusid]->acronym; } else { $statusac = get_string('unknownstatus', 'mod_attendance', $statusid);