Feature: Allow warnings to send multiple emails.
authorDan Marsden <dan@danmarsden.com>
Wed, 19 Jul 2017 02:52:27 +0000 (14:52 +1200)
committerDan Marsden <dan@danmarsden.com>
Fri, 28 Jul 2017 00:19:00 +0000 (12:19 +1200)
13 files changed:
backup/moodle2/backup_attendance_stepslib.php
classes/add_warning_form.php
classes/structure.php
classes/task/notify.php
db/install.xml
db/upgrade.php
lang/en/attendance.php
locallib.php
renderer.php
resetcalendar.php
settings.php
version.php
warnings.php

index 9275784..8c10d11 100644 (file)
@@ -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(
index f337ce8..c21ad5e 100644 (file)
@@ -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);
index d517ccf..3fce0d1 100644 (file)
@@ -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');
     }
 
     /**
index 0c9f80f..138097c 100644 (file)
@@ -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
index 482b8e0..efd9c6c 100644 (file)
         <FIELD NAME="idnumber" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="attendance id or other id relating to this warning."/>
         <FIELD NAME="warningpercent" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Percentage that triggers this warning."/>
         <FIELD NAME="warnafter" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Start warning after this number of taken sessions."/>
+        <FIELD NAME="maxwarn" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Maximum number of warnings to send."/>
         <FIELD NAME="emailuser" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Should the user be notified at this level."/>
         <FIELD NAME="emailsubject" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Email subject line for emails going to user"/>
         <FIELD NAME="emailcontent" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The html-formatted text that should be sent to the user"/>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
       </KEYS>
       <INDEXES>
-        <INDEX NAME="notifyid_userid" UNIQUE="true" FIELDS="notifyid, userid"/>
+        <INDEX NAME="notifyid_userid" UNIQUE="false" FIELDS="notifyid, userid"/>
       </INDEXES>
     </TABLE>
   </TABLES>
index 0917366..68f9f5a 100644 (file)
@@ -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;
 }
index 1eb0354..9f0391d 100644 (file)
@@ -227,6 +227,8 @@ $string['maxpossible_help'] = 'Shows the score each user can reach if they recei
     </ul>';
 $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.
index 7f663bf..52ae86e 100644 (file)
@@ -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);
index b7a6bb8..3c2bcb7 100644 (file)
@@ -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) {
index be8b598..3632078 100644 (file)
@@ -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');
index fc99010..d89f3ad 100644 (file)
@@ -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));
 
index cc73377..527ec38 100644 (file)
@@ -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';
index eaa3bba..0716575 100644 (file)
@@ -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'];