diff --git a/backup/moodle2/backup_attendance_stepslib.php b/backup/moodle2/backup_attendance_stepslib.php index 9275784..8c10d11 100644 --- a/backup/moodle2/backup_attendance_stepslib.php +++ b/backup/moodle2/backup_attendance_stepslib.php @@ -51,8 +51,8 @@ class backup_attendance_activity_structure_step extends backup_activity_structur 'acronym', 'description', 'grade', 'studentavailability', 'setunmarked', 'visible', 'deleted', 'setnumber')); $warnings = new backup_nested_element('warnings'); - $warning = new backup_nested_element('warning', array('id'), array( - 'warningpercent', 'warnafter', 'emailuser', 'emailsubject', 'emailcontent', 'emailcontentformat', 'thirdpartyemails')); + $warning = new backup_nested_element('warning', array('id'), array('warningpercent', 'warnafter', + 'maxwarn', 'emailuser', 'emailsubject', 'emailcontent', 'emailcontentformat', 'thirdpartyemails')); $sessions = new backup_nested_element('sessions'); $session = new backup_nested_element('session', array('id'), array( diff --git a/classes/add_warning_form.php b/classes/add_warning_form.php index f337ce8..c21ad5e 100644 --- a/classes/add_warning_form.php +++ b/classes/add_warning_form.php @@ -60,6 +60,11 @@ class mod_attendance_add_warning_form extends moodleform { $mform->setType('warnafter', PARAM_INT); $mform->setDefault('warnafter', $config->warnafter); + $mform->addElement('select', 'maxwarn', get_string('maxwarn', 'mod_attendance'), $options); + $mform->addHelpButton('maxwarn', 'maxwarn', 'mod_attendance'); + $mform->setType('maxwarn', PARAM_INT); + $mform->setDefault('maxwarn', $config->maxwarn); + $mform->addElement('checkbox', 'emailuser', get_string('emailuser', 'mod_attendance')); $mform->addHelpButton('emailuser', 'emailuser', 'mod_attendance'); $mform->setDefault('emailuser', $config->emailuser); diff --git a/classes/structure.php b/classes/structure.php index d517ccf..3fce0d1 100644 --- a/classes/structure.php +++ b/classes/structure.php @@ -647,14 +647,21 @@ class mod_attendance_structure { $sesslog[$sid]->takenby = $USER->id; } } - + // Get existing session log. $dbsesslog = $this->get_session_log($this->pageparams->sessionid); foreach ($sesslog as $log) { // Don't save a record if no statusid or remark. if (!empty($log->statusid) || !empty($log->remarks)) { if (array_key_exists($log->studentid, $dbsesslog)) { - $log->id = $dbsesslog[$log->studentid]->id; - $DB->update_record('attendance_log', $log); + // Check if anything important has changed before updating record. + // Don't update timetaken/takenby records if nothing has changed. + if ($dbsesslog[$log->studentid]->remarks <> $log->remarks || + $dbsesslog[$log->studentid]->statusid <> $log->statusid || + $dbsesslog[$log->studentid]->statusset <> $log->statusset) { + + $log->id = $dbsesslog[$log->studentid]->id; + $DB->update_record('attendance_log', $log); + } } else { $DB->insert_record('attendance_log', $log, false); } @@ -664,6 +671,7 @@ class mod_attendance_structure { $session = $this->get_session_info($this->pageparams->sessionid); $session->lasttaken = $now; $session->lasttakenby = $USER->id; + $DB->update_record('attendance_sessions', $session); if ($this->grade != 0) { @@ -967,7 +975,7 @@ class mod_attendance_structure { public function get_session_log($sessionid) { global $DB; - return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id'); + return $DB->get_records('attendance_log', array('sessionid' => $sessionid), '', 'studentid,statusid,remarks,id,statusset'); } /** diff --git a/classes/task/notify.php b/classes/task/notify.php index 0c9f80f..138097c 100644 --- a/classes/task/notify.php +++ b/classes/task/notify.php @@ -44,16 +44,14 @@ class notify extends \core\task\scheduled_task { return; // Warnings not enabled. } $now = time(); // Store current time to use in queries so they all match nicely. - $lastrun = get_config('mod_attendance', 'notifylastrun'); - if (empty($lastrun)) { - $lastrun = 0; - } - if (!empty($lastrun)) { - mtrace("Get warnings to send for sessions that have ended since: ".userdate($lastrun)); - } $orderby = 'ORDER BY cm.id, atl.studentid, n.warningpercent ASC'; - $records = attendance_get_users_to_notify(array(), $orderby, $lastrun, true); + + // Get records for attendance sessions that have been updated since last time this task ran. + // Note: this returns all users for these sessions - even if the users attendance wasn't changed + // since last time we ran, before sending a notification we check to see if the users have + // updated attendance logs since last time they were notified. + $records = attendance_get_users_to_notify(array(), $orderby, true); $sentnotifications = array(); $thirdpartynotifications = array(); $numsentusers = 0; @@ -64,8 +62,22 @@ class notify extends \core\task\scheduled_task { } if (!empty($record->emailuser)) { - // Only send one warning to this user from each attendance in this run. - flag any higher percent notifications as sent. + // Only send one warning to this user from each attendance in this run. + // Flag any higher percent notifications as sent. if (empty($sentnotifications[$record->userid]) || !in_array($record->aid, $sentnotifications[$record->userid])) { + + // If has previously been sent a warning, check to see if this user has + // attendance updated since the last time the notification was sent. + if (!empty($record->timesent)) { + $sql = "SELECT * + FROM {attendance_log} l + JOIN {attendance_sessions} s ON s.id = l.sessionid + WHERE s.attendanceid = ? AND studentid = ? AND timetaken > ?"; + if (!$DB->record_exists_sql($sql, array($record->aid, $record->userid, $record->timesent))) { + continue; // Skip this record and move to the next user. + } + } + // Convert variables in emailcontent. $record = attendance_template_variables($record); $user = $DB->get_record('user', array('id' => $record->userid)); @@ -91,14 +103,14 @@ class notify extends \core\task\scheduled_task { $thirdpartynotifications[$senduser] = array(); } if (!isset($thirdpartynotifications[$senduser][$record->aid . '_' . $record->userid])) { - $thirdpartynotifications[$senduser][$record->aid . '_' . $record->userid] = get_string('thirdpartyemailtext', 'attendance', $record); + $thirdpartynotifications[$senduser][$record->aid . '_' . $record->userid] + = get_string('thirdpartyemailtext', 'attendance', $record); } } else { mtrace("user".$senduser. "does not have capablity in cm".$record->cmid); } } } - $notify = new \stdClass(); $notify->userid = $record->userid; $notify->notifyid = $record->notifyid; @@ -125,7 +137,5 @@ class notify extends \core\task\scheduled_task { mtrace($numsentthird ." thirdparty emails sent"); } } - - set_config('notifylastrun', $now, 'mod_attendance'); } } \ No newline at end of file diff --git a/db/install.xml b/db/install.xml index 482b8e0..efd9c6c 100644 --- a/db/install.xml +++ b/db/install.xml @@ -119,6 +119,7 @@ + @@ -141,7 +142,7 @@ - + diff --git a/db/upgrade.php b/db/upgrade.php index 0917366..68f9f5a 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -408,5 +408,31 @@ function xmldb_attendance_upgrade($oldversion=0) { upgrade_mod_savepoint(true, 2017071305, 'attendance'); } + if ($oldversion < 2017071800) { + // Define field setunmarked to be added to attendance_statuses. + $table = new xmldb_table('attendance_warning'); + $field = new xmldb_field('maxwarn', XMLDB_TYPE_INTEGER, '10', null, true, null, '1', 'warnafter'); + + // Conditionally launch add field automark. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + // Attendance savepoint reached. + upgrade_mod_savepoint(true, 2017071800, 'attendance'); + } + + if ($oldversion < 2017071802) { + // Define field setunmarked to be added to attendance_statuses. + $table = new xmldb_table('attendance_warning_done'); + + $index = new xmldb_index('notifyid_userid', XMLDB_INDEX_UNIQUE, array('notifyid', 'userid')); + $dbman->drop_index($table, $index); + + $index = new xmldb_index('notifyid', XMLDB_INDEX_NOTUNIQUE, array('notifyid', 'userid')); + $dbman->add_index($table, $index); + + // Attendance savepoint reached. + upgrade_mod_savepoint(true, 2017071802, 'attendance'); + } return $result; } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 1eb0354..9f0391d 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -227,6 +227,8 @@ $string['maxpossible_help'] = 'Shows the score each user can reach if they recei '; $string['maxpossiblepercentage'] = 'Maximum possible percentage'; $string['maxpossiblepoints'] = 'Maximum possible points'; +$string['maxwarn'] = 'Maximum number of e-mail warnings'; +$string['maxwarn_help'] = 'The maximum number of times a warning should be sent (only one warning per session is sent)'; $string['mergeuser'] = 'Merge user'; $string['modulename'] = 'Attendance'; $string['modulename_help'] = 'The attendance activity module enables a teacher to take attendance during class and students to view their own attendance record. diff --git a/locallib.php b/locallib.php index 7f663bf..52ae86e 100644 --- a/locallib.php +++ b/locallib.php @@ -716,15 +716,15 @@ SELECT a.id, a.course as courseid, c.fullname as coursename, atl.studentid AS us * Generates a list of users flagged at-risk. * * @param array $courseids optional list of courses to return - * @param array $sincetime optional allows a list to be calculated for cron processing. * @param bool $allfornotify get notification list for scheduled task. * @return stdClass */ -function attendance_get_users_to_notify($courseids = array(), $orderby = '', $sincetime = 0, $allfornotify = false) { +function attendance_get_users_to_notify($courseids = array(), $orderby = '', $allfornotify = false) { global $DB; $joingroup = 'LEFT JOIN {groups_members} gm ON (gm.userid = atl.studentid AND gm.groupid = ats.groupid)'; $where = ' AND (ats.groupid = 0 or gm.id is NOT NULL)'; + $having = ''; $params = array(); if (!empty($courseids)) { @@ -733,8 +733,8 @@ function attendance_get_users_to_notify($courseids = array(), $orderby = '', $si $params = array_merge($params, $inparams); } if ($allfornotify) { - // Exclude warnings that have already been sent. - $where .= ' AND ns.id IS NULL '; + // Exclude warnings that have already sent the max num. + $having .= ' AND n.maxwarn > COUNT(DISTINCT ns.id) '; } $unames = get_all_user_name_fields(true); @@ -743,8 +743,9 @@ function attendance_get_users_to_notify($courseids = array(), $orderby = '', $si $idfield = $DB->sql_concat('cm.id', 'atl.studentid', 'n.id'); $sql = "SELECT {$idfield} as uniqueid, a.id as aid, {$unames2}, a.name as aname, cm.id as cmid, c.id as courseid, c.fullname as coursename, atl.studentid AS userid, n.id as notifyid, n.warningpercent, n.emailsubject, - n.emailcontent, n.emailcontentformat, n.emailuser, n.thirdpartyemails, ns.timesent, n.warnafter, + n.emailcontent, n.emailcontentformat, n.emailuser, n.thirdpartyemails, n.warnafter, n.maxwarn, COUNT(DISTINCT ats.id) AS numtakensessions, SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints, + COUNT(DISTINCT ns.id) as nscount, MAX(ns.timesent) as timesent, SUM(stg.grade) / SUM(stm.maxgrade) AS percent FROM {attendance_sessions} ats JOIN {attendance} a ON a.id = ats.attendanceid @@ -763,21 +764,22 @@ function attendance_get_users_to_notify($courseids = array(), $orderby = '', $si GROUP BY attendanceid, setnumber) stm ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid) {$joingroup} - WHERE ats.lasttaken >= {$sincetime} {$where} + WHERE 1 = 1 {$where} GROUP BY uniqueid, a.id, a.name, a.course, c.fullname, atl.studentid, n.id, n.warningpercent, - n.emailsubject, n.emailcontent, n.emailcontentformat, n.warnafter, - n.emailuser, n.thirdpartyemails, ns.timesent, cm.id, c.id, {$unames2} + n.emailsubject, n.emailcontent, n.emailcontentformat, n.warnafter, n.maxwarn, + n.emailuser, n.thirdpartyemails, cm.id, c.id, {$unames2}, ns.userid HAVING n.warnafter <= COUNT(DISTINCT ats.id) AND n.warningpercent > ((SUM(stg.grade) / SUM(stm.maxgrade)) * 100) + {$having} {$orderby}"; if (!$allfornotify) { $idfield = $DB->sql_concat('cmid', 'userid'); // Only show one record per attendance for teacher reports. - $sql = "SELECT {$idfield} as id, {$unames}, aid, cmid, courseid, aname, coursename, userid, MIN(warningpercent), - numtakensessions, points, maxpoints, percent, timesent + $sql = "SELECT DISTINCT {$idfield} as id, {$unames}, aid, cmid, courseid, aname, coursename, userid, + numtakensessions, percent, MAX(timesent) as timesent FROM ({$sql}) as m - GROUP BY id, aid, cmid, courseid, aname, userid, numtakensessions, points, maxpoints, - percent, coursename, timesent, {$unames} {$orderby}"; + GROUP BY id, aid, cmid, courseid, aname, userid, numtakensessions, + percent, coursename, {$unames} {$orderby}"; } return $DB->get_records_sql($sql, $params); diff --git a/renderer.php b/renderer.php index b7a6bb8..3c2bcb7 100644 --- a/renderer.php +++ b/renderer.php @@ -949,7 +949,8 @@ class mod_attendance_renderer extends plugin_renderer_base { get_string('pointssessionscompleted', 'attendance'), get_string('percentagesessionscompleted', 'attendance')); $table->align = array('left', 'left', 'center', 'center', 'center'); - $table->colclasses = array('colcourse', 'colatt', 'colsessionscompleted', 'colpointssessionscompleted', 'colpercentagesessionscompleted'); + $table->colclasses = array('colcourse', 'colatt', 'colsessionscompleted', + 'colpointssessionscompleted', 'colpercentagesessionscompleted'); $totalattendance = 0; $totalpercentage = 0; foreach ($userdata->coursesatts as $ca) { diff --git a/resetcalendar.php b/resetcalendar.php index be8b598..3632078 100644 --- a/resetcalendar.php +++ b/resetcalendar.php @@ -70,7 +70,8 @@ if (get_config('attendance', 'enablecalendar')) { } } else { if ($action == 'delete' && confirm_sesskey()) { - $caleventids = $DB->get_records_select_menu('attendance_sessions', 'caleventid > 0', array(), '', 'caleventid, caleventid as id2'); + $caleventids = $DB->get_records_select_menu('attendance_sessions', 'caleventid > 0', array(), + '', 'caleventid, caleventid as id2'); $DB->delete_records_list('event', 'id', $caleventids); $DB->execute("UPDATE {attendance_sessions} set caleventid = 0"); echo $OUTPUT->notification(get_string('eventsdeleted', 'mod_attendance'), 'notifysuccess'); diff --git a/settings.php b/settings.php index fc99010..d89f3ad 100644 --- a/settings.php +++ b/settings.php @@ -125,6 +125,9 @@ if ($ADMIN->fulltree) { $settings->add(new admin_setting_configselect('attendance/warnafter', get_string('warnafter', 'attendance'), get_string('warnafter_help', 'attendance'), 5, $options)); + $settings->add(new admin_setting_configselect('attendance/maxwarn', + get_string('maxwarn', 'attendance'), get_string('maxwarn_help', 'attendance'), 1, $options)); + $settings->add(new admin_setting_configcheckbox('attendance/emailuser', get_string('emailuser', 'attendance'), get_string('emailuser_help', 'attendance'), 1)); diff --git a/version.php b/version.php index cc73377..527ec38 100644 --- a/version.php +++ b/version.php @@ -23,9 +23,9 @@ */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2017071305; +$plugin->version = 2017071802; $plugin->requires = 2017042100; -$plugin->release = '3.3.9'; +$plugin->release = '3.3.10'; $plugin->maturity = MATURITY_ALPHA; $plugin->cron = 0; $plugin->component = 'mod_attendance'; diff --git a/warnings.php b/warnings.php index eaa3bba..0716575 100644 --- a/warnings.php +++ b/warnings.php @@ -84,6 +84,7 @@ if ($data = $mform->get_data()) { $notify->warningpercent = $data->warningpercent; $notify->warnafter = $data->warnafter; + $notify->maxwarn = $data->maxwarn; $notify->emailuser = empty($data->emailuser) ? 0 : $data->emailuser; $notify->emailsubject = $data->emailsubject; $notify->emailcontent = $data->emailcontent['text']; @@ -113,6 +114,7 @@ if ($data = $mform->get_data()) { $notify->idnumber = $data->idnumber; $notify->warningpercent = $data->warningpercent; $notify->warnafter = $data->warnafter; + $notify->maxwarn = $data->maxwarn; $notify->emailuser = empty($data->emailuser) ? 0 : $data->emailuser; $notify->emailsubject = $data->emailsubject; $notify->emailcontentformat = $data->emailcontent['format'];