2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * local functions and constants for module attendance
20 * @package mod_attendance
21 * @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
27 require_once($CFG->libdir
. '/gradelib.php');
28 require_once(dirname(__FILE__
).'/renderhelpers.php');
30 define('ATT_VIEW_DAYS', 1);
31 define('ATT_VIEW_WEEKS', 2);
32 define('ATT_VIEW_MONTHS', 3);
33 define('ATT_VIEW_ALLPAST', 4);
34 define('ATT_VIEW_ALL', 5);
35 define('ATT_VIEW_NOTPRESENT', 6);
36 define('ATT_VIEW_SUMMARY', 7);
38 define('ATT_SORT_DEFAULT', 0);
39 define('ATT_SORT_LASTNAME', 1);
40 define('ATT_SORT_FIRSTNAME', 2);
42 define('ATTENDANCE_AUTOMARK_DISABLED', 0);
43 define('ATTENDANCE_AUTOMARK_ALL', 1);
44 define('ATTENDANCE_AUTOMARK_CLOSE', 2);
46 // Max number of sessions available in the warnings set form to trigger warnings.
47 define('ATTENDANCE_MAXWARNAFTER', 100);
53 * @param bool $onlyvisible
54 * @param int $statusset
57 function attendance_get_statuses($attid, $onlyvisible=true
, $statusset = -1) {
61 $params = array('aid' => $attid);
63 if ($statusset >= 0) {
64 $params['statusset'] = $statusset;
65 $setsql = ' AND setnumber = :statusset ';
69 $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND visible = 1 AND deleted = 0 $setsql",
70 $params, 'setnumber ASC, grade DESC');
72 $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND deleted = 0 $setsql",
73 $params, 'setnumber ASC, grade DESC');
80 * Get the name of the status set.
83 * @param int $statusset
84 * @param bool $includevalues
87 function attendance_get_setname($attid, $statusset, $includevalues = true
) {
88 $statusname = get_string('statusset', 'mod_attendance', $statusset +
1);
90 $statuses = attendance_get_statuses($attid, true
, $statusset);
91 $statusesout = array();
92 foreach ($statuses as $status) {
93 $statusesout[] = $status->acronym
;
96 if (count($statusesout) > 6) {
97 $statusesout = array_slice($statusesout, 0, 6);
98 $statusesout[] = '&helip;';
100 $statusesout = implode(' ', $statusesout);
101 $statusname .= ' ('.$statusesout.')';
109 * Get users courses and the relevant attendances.
114 function attendance_get_user_courses_attendances($userid) {
117 $usercourses = enrol_get_users_courses($userid);
119 list($usql, $uparams) = $DB->get_in_or_equal(array_keys($usercourses), SQL_PARAMS_NAMED
, 'cid0');
121 $sql = "SELECT att.id as attid, att.course as courseid, course.fullname as coursefullname,
122 course.startdate as coursestartdate, att.name as attname, att.grade as attgrade
123 FROM {attendance} att
125 ON att.course = course.id
126 WHERE att.course $usql
127 ORDER BY coursefullname ASC, attname ASC";
129 $params = array_merge($uparams, array('uid' => $userid));
131 return $DB->get_records_sql($sql, $params);
135 * Used to calculate a fraction based on the part and total values
137 * @param float $part - part of the total value
138 * @param float $total - total value.
139 * @return float the calculated fraction.
141 function attendance_calc_fraction($part, $total) {
145 return $part / $total;
150 * Check to see if statusid in use to help prevent deletion etc.
152 * @param integer $statusid
154 function attendance_has_logs_for_status($statusid) {
156 return $DB->record_exists('attendance_log', array('statusid' => $statusid));
160 * Helper function to add sessiondate_selector to add/update forms.
162 * @param MoodleQuickForm $mform
164 function attendance_form_sessiondate_selector (MoodleQuickForm
$mform) {
166 $mform->addElement('date_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
168 for ($i = 0; $i <= 23; $i++
) {
169 $hours[$i] = sprintf("%02d", $i);
171 for ($i = 0; $i < 60; $i +
= 5) {
172 $minutes[$i] = sprintf("%02d", $i);
175 $sesendtime = array();
176 $sesendtime[] =& $mform->createElement('static', 'from', '', get_string('from', 'attendance'));
177 $sesendtime[] =& $mform->createElement('select', 'starthour', get_string('hour', 'form'), $hours, false
, true
);
178 $sesendtime[] =& $mform->createElement('select', 'startminute', get_string('minute', 'form'), $minutes, false
, true
);
179 $sesendtime[] =& $mform->createElement('static', 'to', '', get_string('to', 'attendance'));
180 $sesendtime[] =& $mform->createElement('select', 'endhour', get_string('hour', 'form'), $hours, false
, true
);
181 $sesendtime[] =& $mform->createElement('select', 'endminute', get_string('minute', 'form'), $minutes, false
, true
);
182 $mform->addGroup($sesendtime, 'sestime', get_string('time', 'attendance'), array(' '), true
);
186 * Count the number of status sets that exist for this instance.
188 * @param int $attendanceid
191 function attendance_get_max_statusset($attendanceid) {
194 $max = $DB->get_field_sql('SELECT MAX(setnumber) FROM {attendance_statuses} WHERE attendanceid = ? AND deleted = 0',
195 array($attendanceid));
203 * Returns the maxpoints for each statusset
205 * @param array $statuses
208 function attendance_get_statusset_maxpoints($statuses) {
209 $statussetmaxpoints = array();
210 foreach ($statuses as $st) {
211 if (!isset($statussetmaxpoints[$st->setnumber
])) {
212 $statussetmaxpoints[$st->setnumber
] = $st->grade
;
215 return $statussetmaxpoints;
221 * @param mod_attendance_structure|stdClass $attendance
222 * @param array $userids
224 function attendance_update_users_grade($attendance, $userids=array()) {
227 if (empty($attendance->grade
)) {
231 list($course, $cm) = get_course_and_cm_from_instance($attendance->id
, 'attendance');
233 $summary = new mod_attendance_summary($attendance->id
, $userids);
235 if (empty($userids)) {
236 $context = context_module
::instance($cm->id
);
237 $userids = array_keys(get_enrolled_users($context, 'mod/attendance:canbelisted', 0, 'u.id'));
240 if ($attendance->grade
< 0) {
241 $dbparams = array('id' => -($attendance->grade
));
242 $scale = $DB->get_record('scale', $dbparams);
243 $scalearray = explode(',', $scale->scale
);
244 $attendancegrade = count($scalearray);
246 $attendancegrade = $attendance->grade
;
250 foreach ($userids as $userid) {
251 $grades[$userid] = new stdClass();
252 $grades[$userid]->userid
= $userid;
254 if ($summary->has_taken_sessions($userid)) {
255 $usersummary = $summary->get_taken_sessions_summary_for($userid);
256 $grades[$userid]->rawgrade
= $usersummary->takensessionspercentage
* $attendancegrade;
258 $grades[$userid]->rawgrade
= null
;
262 return grade_update('mod/attendance', $course->id
, 'mod', 'attendance', $attendance->id
, 0, $grades);
266 * Add an attendance status variable
268 * @param stdClass $status
271 function attendance_add_status($status) {
273 if (empty($status->context
)) {
274 $status->context
= context_system
::instance();
277 if (!empty($status->acronym
) && !empty($status->description
)) {
278 $status->deleted
= 0;
279 $status->visible
= 1;
280 $status->setunmarked
= 0;
282 $id = $DB->insert_record('attendance_statuses', $status);
285 $event = \mod_attendance\event\status_added
::create(array(
286 'objectid' => $status->attendanceid
,
287 'context' => $status->context
,
288 'other' => array('acronym' => $status->acronym
,
289 'description' => $status->description
,
290 'grade' => $status->grade
)));
291 if (!empty($status->cm
)) {
292 $event->add_record_snapshot('course_modules', $status->cm
);
294 $event->add_record_snapshot('attendance_statuses', $status);
303 * Remove a status variable from an attendance instance
305 * @param stdClass $status
306 * @param stdClass $context
307 * @param stdClass $cm
309 function attendance_remove_status($status, $context = null
, $cm = null
) {
311 if (empty($context)) {
312 $context = context_system
::instance();
314 $DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id
));
315 $event = \mod_attendance\event\status_removed
::create(array(
316 'objectid' => $status->id
,
317 'context' => $context,
319 'acronym' => $status->acronym
,
320 'description' => $status->description
323 $event->add_record_snapshot('course_modules', $cm);
325 $event->add_record_snapshot('attendance_statuses', $status);
330 * Update status variable for a particular Attendance module instance
332 * @param stdClass $status
333 * @param string $acronym
334 * @param string $description
336 * @param bool $visible
337 * @param stdClass $context
338 * @param stdClass $cm
339 * @param int $studentavailability
340 * @param bool $setunmarked
343 function attendance_update_status($status, $acronym, $description, $grade, $visible,
344 $context = null
, $cm = null
, $studentavailability = null
, $setunmarked = false
) {
347 if (empty($context)) {
348 $context = context_system
::instance();
351 if (isset($visible)) {
352 $status->visible
= $visible;
353 $updated[] = $visible ?
get_string('show') : get_string('hide');
354 } else if (empty($acronym) ||
empty($description)) {
355 return array('acronym' => $acronym, 'description' => $description);
361 $status->acronym
= $acronym;
362 $updated[] = $acronym;
365 $status->description
= $description;
366 $updated[] = $description;
369 $status->grade
= $grade;
372 if (isset($studentavailability)) {
373 if (empty($studentavailability)) {
374 if ($studentavailability !== '0') {
375 $studentavailability = null
;
379 $status->studentavailability
= $studentavailability;
380 $updated[] = $studentavailability;
383 $status->setunmarked
= 1;
385 $status->setunmarked
= 0;
387 $DB->update_record('attendance_statuses', $status);
389 $event = \mod_attendance\event\status_updated
::create(array(
390 'objectid' => $status->attendanceid
,
391 'context' => $context,
392 'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade,
393 'updated' => implode(' ', $updated))));
395 $event->add_record_snapshot('course_modules', $cm);
397 $event->add_record_snapshot('attendance_statuses', $status);
402 * Similar to core random_string function but only lowercase letters.
403 * designed to make it relatively easy to provide a simple password in class.
405 * @param int $length The length of the string to be created.
408 function attendance_random_string($length=6) {
409 $randombytes = random_bytes_emulate($length);
410 $pool = 'abcdefghijklmnopqrstuvwxyz';
411 $pool .= '0123456789';
412 $poollen = strlen($pool);
414 for ($i = 0; $i < $length; $i++
) {
415 $rand = ord($randombytes[$i]);
416 $string .= substr($pool, ($rand %
($poollen)), 1);
422 * Check to see if this session is open for student marking.
424 * @param stdclass $sess the session record from attendance_sessions.
427 function attendance_can_student_mark($sess) {
429 $attconfig = get_config('attendance');
430 if (!empty($attconfig->studentscanmark
) && !empty($sess->studentscanmark
)) {
431 if (empty($attconfig->studentscanmarksessiontime
)) {
434 $duration = $sess->duration
;
435 if (empty($duration)) {
436 $duration = $attconfig->studentscanmarksessiontimeend
* 60;
438 if ($sess->sessdate
< time() && time() < ($sess->sessdate +
$duration)) {
447 * Generate worksheet for Attendance export
449 * @param stdclass $data The data for the report
450 * @param string $filename The name of the file
451 * @param string $format excel|ods
454 function attendance_exporttotableed($data, $filename, $format) {
457 if ($format === 'excel') {
458 require_once("$CFG->libdir/excellib.class.php");
460 $workbook = new MoodleExcelWorkbook("-");
462 require_once("$CFG->libdir/odslib.class.php");
464 $workbook = new MoodleODSWorkbook("-");
466 // Sending HTTP headers.
467 $workbook->send($filename);
468 // Creating the first worksheet.
469 $myxls = $workbook->add_worksheet('Attendances');
471 $formatbc = $workbook->add_format();
472 $formatbc->set_bold(1);
474 $myxls->write(0, 0, get_string('course'), $formatbc);
475 $myxls->write(0, 1, $data->course
);
476 $myxls->write(1, 0, get_string('group'), $formatbc);
477 $myxls->write(1, 1, $data->group
);
481 foreach ($data->tabhead
as $cell) {
482 // Merge cells if the heading would be empty (remarks column).
484 $myxls->merge_cells($i, $j - 1, $i, $j);
486 $myxls->write($i, $j, $cell, $formatbc);
492 foreach ($data->table
as $row) {
493 foreach ($row as $cell) {
494 $myxls->write($i, $j++
, $cell);
503 * Generate csv for Attendance export
505 * @param stdclass $data The data for the report
506 * @param string $filename The name of the file
509 function attendance_exporttocsv($data, $filename) {
512 header("Content-Type: application/download\n");
513 header("Content-Disposition: attachment; filename=\"$filename\"");
514 header("Expires: 0");
515 header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
516 header("Pragma: public");
518 echo get_string('course')."\t".$data->course
."\n";
519 echo get_string('group')."\t".$data->group
."\n\n";
521 echo implode("\t", $data->tabhead
)."\n";
522 foreach ($data->table
as $row) {
523 echo implode("\t", $row)."\n";
528 * Get session data for form.
529 * @param stdClass $formdata moodleform - attendance form.
530 * @param mod_attendance_structure $att - used to get attendance level subnet.
533 function attendance_construct_sessions_data_for_add($formdata, mod_attendance_structure
$att) {
536 $sesstarttime = $formdata->sestime
['starthour'] * HOURSECS +
$formdata->sestime
['startminute'] * MINSECS
;
537 $sesendtime = $formdata->sestime
['endhour'] * HOURSECS +
$formdata->sestime
['endminute'] * MINSECS
;
538 $sessiondate = $formdata->sessiondate +
$sesstarttime;
539 $duration = $sesendtime - $sesstarttime;
540 if (empty(get_config('attendance', 'enablewarnings'))) {
541 $absenteereport = get_config('attendance', 'absenteereport_default');
543 $absenteereport = empty($formdata->absenteereport
) ?
0 : 1;
548 if (empty(get_config('attendance', 'studentscanmark'))) {
549 $formdata->studentscanmark
= 0;
553 if (isset($formdata->addmultiply
)) {
554 $startdate = $sessiondate;
555 $enddate = $formdata->sessionenddate + DAYSECS
; // Because enddate in 0:0am.
557 if ($enddate < $startdate) {
561 // Getting first day of week.
563 $dinfo = usergetdate($sdate);
564 if ($CFG->calendar_startwday
=== '0') { // Week start from sunday.
565 $startweek = $startdate - $dinfo['wday'] * DAYSECS
; // Call new variable.
567 $wday = $dinfo['wday'] === 0 ?
7 : $dinfo['wday'];
568 $startweek = $startdate - ($wday - 1) * DAYSECS
;
571 $wdaydesc = array(0 => 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
573 while ($sdate < $enddate) {
574 if ($sdate < $startweek + WEEKSECS
) {
575 $dinfo = usergetdate($sdate);
576 if (isset($formdata->sdays
) && array_key_exists($wdaydesc[$dinfo['wday']], $formdata->sdays
)) {
577 $sess = new stdClass();
578 $sess->sessdate
= make_timestamp($dinfo['year'], $dinfo['mon'], $dinfo['mday'],
579 $formdata->sestime
['starthour'], $formdata->sestime
['startminute']);
580 $sess->duration
= $duration;
581 $sess->descriptionitemid
= $formdata->sdescription
['itemid'];
582 $sess->description
= $formdata->sdescription
['text'];
583 $sess->descriptionformat
= $formdata->sdescription
['format'];
584 $sess->timemodified
= $now;
585 $sess->absenteereport
= $absenteereport;
586 $sess->studentpassword
= '';
587 if (isset($formdata->studentscanmark
)) { // Students will be able to mark their own attendance.
588 $sess->studentscanmark
= 1;
589 if (!empty($formdata->usedefaultsubnet
)) {
590 $sess->subnet
= $att->subnet
;
592 $sess->subnet
= $formdata->subnet
;
594 $sess->automark
= $formdata->automark
;
595 if (isset($formdata->autoassignstatus
)) {
596 $sess->autoassignstatus
= 1;
598 $sess->automarkcompleted
= 0;
599 if (!empty($formdata->randompassword
)) {
600 $sess->studentpassword
= attendance_random_string();
601 } else if (!empty($formdata->studentpassword
)) {
602 $sess->studentpassword
= $formdata->studentpassword
;
607 $sess->automarkcompleted
= 0;
609 $sess->statusset
= $formdata->statusset
;
611 attendance_fill_groupid($formdata, $sessions, $sess);
615 $startweek +
= WEEKSECS
* $formdata->period
;
620 $sess = new stdClass();
621 $sess->sessdate
= $sessiondate;
622 $sess->duration
= $duration;
623 $sess->descriptionitemid
= $formdata->sdescription
['itemid'];
624 $sess->description
= $formdata->sdescription
['text'];
625 $sess->descriptionformat
= $formdata->sdescription
['format'];
626 $sess->timemodified
= $now;
627 $sess->studentscanmark
= 0;
628 $sess->autoassignstatus
= 0;
630 $sess->studentpassword
= '';
632 $sess->automarkcompleted
= 0;
633 $sess->absenteereport
= $absenteereport;
635 if (isset($formdata->studentscanmark
) && !empty($formdata->studentscanmark
)) {
636 // Students will be able to mark their own attendance.
637 $sess->studentscanmark
= 1;
638 if (isset($formdata->autoassignstatus
) && !empty($formdata->autoassignstatus
)) {
639 $sess->autoassignstatus
= 1;
641 if (!empty($formdata->randompassword
)) {
642 $sess->studentpassword
= attendance_random_string();
643 } else if (!empty($formdata->studentpassword
)) {
644 $sess->studentpassword
= $formdata->studentpassword
;
646 if (!empty($formdata->usedefaultsubnet
)) {
647 $sess->subnet
= $att->subnet
;
649 $sess->subnet
= $formdata->subnet
;
652 if (!empty($formdata->automark
)) {
653 $sess->automark
= $formdata->automark
;
656 $sess->statusset
= $formdata->statusset
;
658 attendance_fill_groupid($formdata, $sessions, $sess);
665 * Helper function for attendance_construct_sessions_data_for_add().
667 * @param stdClass $formdata
668 * @param stdClass $sessions
669 * @param stdClass $sess
671 function attendance_fill_groupid($formdata, &$sessions, $sess) {
672 if ($formdata->sessiontype
== mod_attendance_structure
::SESSION_COMMON
) {
677 foreach ($formdata->groups
as $groupid) {
679 $sess->groupid
= $groupid;
686 * Generates a summary of points for the courses selected.
688 * @param array $courseids optional list of courses to return
689 * @param string $orderby - optional order by param
692 function attendance_course_users_points($courseids = array(), $orderby = '') {
697 $where .= ' AND ats.sessdate < :enddate ';
698 $params['enddate'] = time();
700 $joingroup = 'LEFT JOIN {groups_members} gm ON (gm.userid = atl.studentid AND gm.groupid = ats.groupid)';
701 $where .= ' AND (ats.groupid = 0 or gm.id is NOT NULL)';
703 if (!empty($courseids)) {
704 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED
);
705 $where .= ' AND c.id ' . $insql;
706 $params = array_merge($params, $inparams);
709 $sql = "SELECT courseid, coursename, sum(points) / sum(maxpoints) as percentage FROM (
710 SELECT a.id, a.course as courseid, c.fullname as coursename, atl.studentid AS userid, COUNT(DISTINCT ats.id) AS numtakensessions,
711 SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints
712 FROM {attendance_sessions} ats
713 JOIN {attendance} a ON a.id = ats.attendanceid
714 JOIN {course} c ON c.id = a.course
715 JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
716 JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
717 JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
718 FROM {attendance_statuses}
721 GROUP BY attendanceid, setnumber) stm
722 ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
724 WHERE ats.sessdate >= c.startdate
725 AND ats.lasttaken != 0
727 GROUP BY a.id, a.course, c.fullname, atl.studentid
728 ) p GROUP by courseid, coursename {$orderby}";
730 return $DB->get_records_sql($sql, $params);
734 * Generates a list of users flagged absent.
736 * @param array $courseids optional list of courses to return
737 * @param string $orderby how to order results.
738 * @param bool $allfornotify get notification list for scheduled task.
741 function attendance_get_users_to_notify($courseids = array(), $orderby = '', $allfornotify = false
) {
744 $joingroup = 'LEFT JOIN {groups_members} gm ON (gm.userid = atl.studentid AND gm.groupid = ats.groupid)';
745 $where = ' AND (ats.groupid = 0 or gm.id is NOT NULL)';
749 if (!empty($courseids)) {
750 list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED
);
751 $where .= ' AND c.id ' . $insql;
752 $params = array_merge($params, $inparams);
755 // Exclude warnings that have already sent the max num.
756 $having .= ' AND n.maxwarn > COUNT(DISTINCT ns.id) ';
759 $unames = get_all_user_name_fields(true
);
760 $unames2 = get_all_user_name_fields(true
, 'u');
762 $idfield = $DB->sql_concat('cm.id', 'atl.studentid', 'n.id');
763 $sql = "SELECT {$idfield} as uniqueid, a.id as aid, {$unames2}, a.name as aname, cm.id as cmid, c.id as courseid,
764 c.fullname as coursename, atl.studentid AS userid, n.id as notifyid, n.warningpercent, n.emailsubject,
765 n.emailcontent, n.emailcontentformat, n.emailuser, n.thirdpartyemails, n.warnafter, n.maxwarn,
766 COUNT(DISTINCT ats.id) AS numtakensessions, SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints,
767 COUNT(DISTINCT ns.id) as nscount, MAX(ns.timesent) as timesent,
768 SUM(stg.grade) / SUM(stm.maxgrade) AS percent
769 FROM {attendance_sessions} ats
770 JOIN {attendance} a ON a.id = ats.attendanceid
771 JOIN {course_modules} cm ON cm.instance = a.id
772 JOIN {course} c on c.id = cm.course
773 JOIN {modules} md ON md.id = cm.module AND md.name = 'attendance'
774 JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
775 JOIN {user} u ON (u.id = atl.studentid)
776 JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
777 JOIN {attendance_warning} n ON n.idnumber = a.id
778 LEFT JOIN {attendance_warning_done} ns ON ns.notifyid = n.id AND ns.userid = atl.studentid
779 JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
780 FROM {attendance_statuses}
783 GROUP BY attendanceid, setnumber) stm
784 ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
786 WHERE ats.absenteereport = 1 {$where}
787 GROUP BY uniqueid, a.id, a.name, a.course, c.fullname, atl.studentid, n.id, n.warningpercent,
788 n.emailsubject, n.emailcontent, n.emailcontentformat, n.warnafter, n.maxwarn,
789 n.emailuser, n.thirdpartyemails, cm.id, c.id, {$unames2}, ns.userid
790 HAVING n.warnafter <= COUNT(DISTINCT ats.id) AND n.warningpercent > ((SUM(stg.grade) / SUM(stm.maxgrade)) * 100)
794 if (!$allfornotify) {
795 $idfield = $DB->sql_concat('cmid', 'userid');
796 // Only show one record per attendance for teacher reports.
797 $sql = "SELECT DISTINCT {$idfield} as id, {$unames}, aid, cmid, courseid, aname, coursename, userid,
798 numtakensessions, percent, MAX(timesent) as timesent
800 GROUP BY id, aid, cmid, courseid, aname, userid, numtakensessions,
801 percent, coursename, {$unames} {$orderby}";
804 return $DB->get_records_sql($sql, $params);
809 * Template variables into place in supplied email content.
811 * @param object $record db record of details
812 * @return array - the content of the fields after templating.
814 function attendance_template_variables($record) {
815 $templatevars = array(
816 '/%coursename%/' => $record->coursename
,
817 '/%courseid%/' => $record->courseid
,
818 '/%userfirstname%/' => $record->firstname
,
819 '/%userlastname%/' => $record->lastname
,
820 '/%userid%/' => $record->userid
,
821 '/%warningpercent%/' => $record->warningpercent
,
822 '/%attendancename%/' => $record->aname
,
823 '/%cmid%/' => $record->cmid
,
824 '/%numtakensessions%/' => $record->numtakensessions
,
825 '/%points%/' => $record->points
,
826 '/%maxpoints%/' => $record->maxpoints
,
827 '/%percent%/' => $record->percent
,
829 $extrauserfields = get_all_user_name_fields();
830 foreach ($extrauserfields as $extra) {
831 $templatevars['/%'.$extra.'%/'] = $record->$extra;
833 $patterns = array_keys($templatevars); // The placeholders which are to be replaced.
834 $replacements = array_values($templatevars); // The values which are to be templated in for the placeholders.
835 // Array to describe which fields in reengagement object should have a template replacement.
836 $replacementfields = array('emailsubject', 'emailcontent');
838 // Replace %variable% with relevant value everywhere it occurs in reengagement->field.
839 foreach ($replacementfields as $field) {
840 $record->$field = preg_replace($patterns, $replacements, $record->$field);
846 * Find highest available status for a user.
848 * @param mod_attendance_structure $att attendance structure
849 * @param stdclass $attforsession attendance_session record.
852 function attendance_session_get_highest_status(mod_attendance_structure
$att, $attforsession) {
853 // Find the status to set here.
854 $statuses = $att->get_statuses();
855 $highestavailablegrade = 0;
856 $highestavailablestatus = new stdClass();
857 foreach ($statuses as $status) {
858 if ($status->studentavailability
=== '0') {
859 // This status is never available to students.
862 if (!empty($status->studentavailability
)) {
863 $toolateforstatus = (($attforsession->sessdate +
($status->studentavailability
* 60)) < time());
864 if ($toolateforstatus) {
868 // This status is available to the student.
869 if ($status->grade
> $highestavailablegrade) {
870 // This is the most favourable grade so far; save it.
871 $highestavailablegrade = $status->grade
;
872 $highestavailablestatus = $status;
875 if (empty($highestavailablestatus)) {
878 return $highestavailablestatus->id
;
882 * Get available automark options.
886 function attendance_get_automarkoptions() {
888 $options[ATTENDANCE_AUTOMARK_DISABLED
] = get_string('noautomark', 'attendance');
889 if (strpos(get_config('tool_log', 'enabled_stores'), 'logstore_standard') !== false
) {
890 $options[ATTENDANCE_AUTOMARK_ALL
] = get_string('automarkall', 'attendance');
892 $options[ATTENDANCE_AUTOMARK_CLOSE
] = get_string('automarkclose', 'attendance');