You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

869 lines
31 KiB

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* local functions and constants for module attendance
*
* @package mod_attendance
* @copyright 2011 Artem Andreev <andreev.artem@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/gradelib.php');
require_once(dirname(__FILE__).'/renderhelpers.php');
define('ATT_VIEW_DAYS', 1);
define('ATT_VIEW_WEEKS', 2);
define('ATT_VIEW_MONTHS', 3);
define('ATT_VIEW_ALLPAST', 4);
define('ATT_VIEW_ALL', 5);
define('ATT_VIEW_NOTPRESENT', 6);
define('ATT_VIEW_SUMMARY', 7);
define('ATT_SORT_DEFAULT', 0);
define('ATT_SORT_LASTNAME', 1);
define('ATT_SORT_FIRSTNAME', 2);
define('ATTENDANCE_AUTOMARK_DISABLED', 0);
define('ATTENDANCE_AUTOMARK_ALL', 1);
define('ATTENDANCE_AUTOMARK_CLOSE', 2);
8 years ago
/**
* Get statuses,
*
* @param int $attid
* @param bool $onlyvisible
* @param int $statusset
* @return array
*/
function attendance_get_statuses($attid, $onlyvisible=true, $statusset = -1) {
global $DB;
// Set selector.
$params = array('aid' => $attid);
$setsql = '';
if ($statusset >= 0) {
$params['statusset'] = $statusset;
$setsql = ' AND setnumber = :statusset ';
}
if ($onlyvisible) {
$statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND visible = 1 AND deleted = 0 $setsql",
$params, 'setnumber ASC, grade DESC');
} else {
$statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND deleted = 0 $setsql",
$params, 'setnumber ASC, grade DESC');
}
return $statuses;
}
/**
* Get the name of the status set.
*
* @param int $attid
* @param int $statusset
* @param bool $includevalues
* @return string
*/
function attendance_get_setname($attid, $statusset, $includevalues = true) {
$statusname = get_string('statusset', 'mod_attendance', $statusset + 1);
if ($includevalues) {
$statuses = attendance_get_statuses($attid, true, $statusset);
$statusesout = array();
foreach ($statuses as $status) {
$statusesout[] = $status->acronym;
}
if ($statusesout) {
if (count($statusesout) > 6) {
$statusesout = array_slice($statusesout, 0, 6);
$statusesout[] = '&helip;';
}
$statusesout = implode(' ', $statusesout);
$statusname .= ' ('.$statusesout.')';
}
}
return $statusname;
}
8 years ago
/**
* Get users courses and the relevant attendances.
*
* @param int $userid
* @return array
*/
function attendance_get_user_courses_attendances($userid) {
global $DB;
$usercourses = enrol_get_users_courses($userid);
list($usql, $uparams) = $DB->get_in_or_equal(array_keys($usercourses), SQL_PARAMS_NAMED, 'cid0');
$sql = "SELECT att.id as attid, att.course as courseid, course.fullname as coursefullname,
course.startdate as coursestartdate, att.name as attname, att.grade as attgrade
FROM {attendance} att
JOIN {course} course
ON att.course = course.id
WHERE att.course $usql
ORDER BY coursefullname ASC, attname ASC";
$params = array_merge($uparams, array('uid' => $userid));
return $DB->get_records_sql($sql, $params);
}
/**
* Used to calculate a fraction based on the part and total values
*
* @param float $part - part of the total value
* @param float $total - total value.
* @return float the calculated fraction.
*/
function attendance_calc_fraction($part, $total) {
if ($total == 0) {
return 0;
} else {
return $part / $total;
}
}
/**
* Check to see if statusid in use to help prevent deletion etc.
*
* @param integer $statusid
*/
function attendance_has_logs_for_status($statusid) {
global $DB;
return $DB->record_exists('attendance_log', array('statusid' => $statusid));
}
/**
* Helper function to add sessiondate_selector to add/update forms.
*
* @param MoodleQuickForm $mform
*/
function attendance_form_sessiondate_selector (MoodleQuickForm $mform) {
$mform->addElement('date_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
for ($i = 0; $i <= 23; $i++) {
$hours[$i] = sprintf("%02d", $i);
}
for ($i = 0; $i < 60; $i += 5) {
$minutes[$i] = sprintf("%02d", $i);
}
$sesendtime = array();
$sesendtime[] =& $mform->createElement('static', 'from', '', get_string('from', 'attendance'));
$sesendtime[] =& $mform->createElement('select', 'starthour', get_string('hour', 'form'), $hours, false, true);
$sesendtime[] =& $mform->createElement('select', 'startminute', get_string('minute', 'form'), $minutes, false, true);
$sesendtime[] =& $mform->createElement('static', 'to', '', get_string('to', 'attendance'));
$sesendtime[] =& $mform->createElement('select', 'endhour', get_string('hour', 'form'), $hours, false, true);
$sesendtime[] =& $mform->createElement('select', 'endminute', get_string('minute', 'form'), $minutes, false, true);
$mform->addGroup($sesendtime, 'sestime', get_string('time', 'attendance'), array(' '), true);
}
/**
* Count the number of status sets that exist for this instance.
*
* @param int $attendanceid
* @return int
*/
function attendance_get_max_statusset($attendanceid) {
global $DB;
$max = $DB->get_field_sql('SELECT MAX(setnumber) FROM {attendance_statuses} WHERE attendanceid = ? AND deleted = 0',
array($attendanceid));
if ($max) {
return $max;
}
return 0;
}
/**
* Returns the maxpoints for each statusset
*
8 years ago
* @param array $statuses
* @return array
*/
function attendance_get_statusset_maxpoints($statuses) {
$statussetmaxpoints = array();
foreach ($statuses as $st) {
if (!isset($statussetmaxpoints[$st->setnumber])) {
$statussetmaxpoints[$st->setnumber] = $st->grade;
}
}
return $statussetmaxpoints;
}
/**
* Update user grades
*
8 years ago
* @param mod_attendance_structure|stdClass $attendance
* @param array $userids
*/
function attendance_update_users_grade($attendance, $userids=array()) {
global $DB;
if (empty($attendance->grade)) {
return false;
}
list($course, $cm) = get_course_and_cm_from_instance($attendance->id, 'attendance');
$summary = new mod_attendance_summary($attendance->id, $userids);
if (empty($userids)) {
$context = context_module::instance($cm->id);
$userids = array_keys(get_enrolled_users($context, 'mod/attendance:canbelisted', 0, 'u.id'));
}
if ($attendance->grade < 0) {
$dbparams = array('id' => -($attendance->grade));
$scale = $DB->get_record('scale', $dbparams);
$scalearray = explode(',', $scale->scale);
$attendancegrade = count($scalearray);
} else {
$attendancegrade = $attendance->grade;
}
$grades = array();
foreach ($userids as $userid) {
$grades[$userid] = new stdClass();
$grades[$userid]->userid = $userid;
if ($summary->has_taken_sessions($userid)) {
$usersummary = $summary->get_taken_sessions_summary_for($userid);
$grades[$userid]->rawgrade = $usersummary->takensessionspercentage * $attendancegrade;
} else {
$grades[$userid]->rawgrade = null;
}
}
return grade_update('mod/attendance', $course->id, 'mod', 'attendance', $attendance->id, 0, $grades);
}
/**
* Add an attendance status variable
*
* @param stdClass $status
8 years ago
* @return bool
*/
function attendance_add_status($status) {
global $DB;
if (empty($status->context)) {
$status->context = context_system::instance();
}
if (!empty($status->acronym) && !empty($status->description)) {
$status->deleted = 0;
$status->visible = 1;
$status->setunmarked = 0;
$id = $DB->insert_record('attendance_statuses', $status);
$status->id = $id;
$event = \mod_attendance\event\status_added::create(array(
'objectid' => $status->attendanceid,
'context' => $status->context,
'other' => array('acronym' => $status->acronym,
'description' => $status->description,
'grade' => $status->grade)));
if (!empty($status->cm)) {
$event->add_record_snapshot('course_modules', $status->cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
return true;
} else {
return false;
}
}
/**
* Remove a status variable from an attendance instance
*
* @param stdClass $status
8 years ago
* @param stdClass $context
* @param stdClass $cm
*/
function attendance_remove_status($status, $context = null, $cm = null) {
global $DB;
if (empty($context)) {
$context = context_system::instance();
}
$DB->set_field('attendance_statuses', 'deleted', 1, array('id' => $status->id));
$event = \mod_attendance\event\status_removed::create(array(
'objectid' => $status->id,
'context' => $context,
'other' => array(
'acronym' => $status->acronym,
'description' => $status->description
)));
if (!empty($cm)) {
$event->add_record_snapshot('course_modules', $cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
}
/**
* Update status variable for a particular Attendance module instance
*
* @param stdClass $status
* @param string $acronym
* @param string $description
* @param int $grade
* @param bool $visible
8 years ago
* @param stdClass $context
* @param stdClass $cm
* @param int $studentavailability
* @param bool $setunmarked
8 years ago
* @return array
*/
function attendance_update_status($status, $acronym, $description, $grade, $visible,
$context = null, $cm = null, $studentavailability = null, $setunmarked = false) {
global $DB;
if (empty($context)) {
$context = context_system::instance();
}
if (isset($visible)) {
$status->visible = $visible;
$updated[] = $visible ? get_string('show') : get_string('hide');
} else if (empty($acronym) || empty($description)) {
return array('acronym' => $acronym, 'description' => $description);
}
$updated = array();
if ($acronym) {
$status->acronym = $acronym;
$updated[] = $acronym;
}
if ($description) {
$status->description = $description;
$updated[] = $description;
}
if (isset($grade)) {
$status->grade = $grade;
$updated[] = $grade;
}
if (isset($studentavailability)) {
if (empty($studentavailability)) {
if ($studentavailability !== '0') {
$studentavailability = null;
}
}
$status->studentavailability = $studentavailability;
$updated[] = $studentavailability;
}
if ($setunmarked) {
$status->setunmarked = 1;
} else {
$status->setunmarked = 0;
}
$DB->update_record('attendance_statuses', $status);
$event = \mod_attendance\event\status_updated::create(array(
'objectid' => $status->attendanceid,
'context' => $context,
'other' => array('acronym' => $acronym, 'description' => $description, 'grade' => $grade,
'updated' => implode(' ', $updated))));
if (!empty($cm)) {
$event->add_record_snapshot('course_modules', $cm);
}
$event->add_record_snapshot('attendance_statuses', $status);
$event->trigger();
}
/**
* Similar to core random_string function but only lowercase letters.
* designed to make it relatively easy to provide a simple password in class.
*
* @param int $length The length of the string to be created.
* @return string
*/
function attendance_random_string($length=6) {
$randombytes = random_bytes_emulate($length);
$pool = 'abcdefghijklmnopqrstuvwxyz';
$pool .= '0123456789';
$poollen = strlen($pool);
$string = '';
for ($i = 0; $i < $length; $i++) {
$rand = ord($randombytes[$i]);
$string .= substr($pool, ($rand % ($poollen)), 1);
}
return $string;
}
/**
* Check to see if this session is open for student marking.
*
* @param stdclass $sess the session record from attendance_sessions.
* @return boolean
*/
function attendance_can_student_mark($sess) {
$canmark = false;
$attconfig = get_config('attendance');
if (!empty($attconfig->studentscanmark) && !empty($sess->studentscanmark)) {
if (empty($attconfig->studentscanmarksessiontime)) {
$canmark = true;
} else {
$duration = $sess->duration;
if (empty($duration)) {
$duration = $attconfig->studentscanmarksessiontimeend * 60;
}
if ($sess->sessdate < time() && time() < ($sess->sessdate + $duration)) {
$canmark = true;
}
}
}
return $canmark;
}
/**
* Generate worksheet for Attendance export
*
* @param stdclass $data The data for the report
* @param string $filename The name of the file
* @param string $format excel|ods
*
*/
function attendance_exporttotableed($data, $filename, $format) {
global $CFG;
if ($format === 'excel') {
require_once("$CFG->libdir/excellib.class.php");
$filename .= ".xls";
$workbook = new MoodleExcelWorkbook("-");
} else {
require_once("$CFG->libdir/odslib.class.php");
$filename .= ".ods";
$workbook = new MoodleODSWorkbook("-");
}
// Sending HTTP headers.
$workbook->send($filename);
// Creating the first worksheet.
$myxls = $workbook->add_worksheet('Attendances');
// Format types.
$formatbc = $workbook->add_format();
$formatbc->set_bold(1);
$myxls->write(0, 0, get_string('course'), $formatbc);
$myxls->write(0, 1, $data->course);
$myxls->write(1, 0, get_string('group'), $formatbc);
$myxls->write(1, 1, $data->group);
$i = 3;
$j = 0;
foreach ($data->tabhead as $cell) {
// Merge cells if the heading would be empty (remarks column).
if (empty($cell)) {
$myxls->merge_cells($i, $j - 1, $i, $j);
} else {
$myxls->write($i, $j, $cell, $formatbc);
}
$j++;
}
$i++;
$j = 0;
foreach ($data->table as $row) {
foreach ($row as $cell) {
$myxls->write($i, $j++, $cell);
}
$i++;
$j = 0;
}
$workbook->close();
}
/**
* Generate csv for Attendance export
*
* @param stdclass $data The data for the report
* @param string $filename The name of the file
*
*/
function attendance_exporttocsv($data, $filename) {
$filename .= ".txt";
header("Content-Type: application/download\n");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
header("Pragma: public");
echo get_string('course')."\t".$data->course."\n";
echo get_string('group')."\t".$data->group."\n\n";
echo implode("\t", $data->tabhead)."\n";
foreach ($data->table as $row) {
echo implode("\t", $row)."\n";
}
}
/**
8 years ago
* Get session data for form.
* @param stdClass $formdata moodleform - attendance form.
7 years ago
* @param mod_attendance_structure $att - used to get attendance level subnet.
* @return array.
*/
function attendance_construct_sessions_data_for_add($formdata, mod_attendance_structure $att) {
global $CFG;
$sesstarttime = $formdata->sestime['starthour'] * HOURSECS + $formdata->sestime['startminute'] * MINSECS;
$sesendtime = $formdata->sestime['endhour'] * HOURSECS + $formdata->sestime['endminute'] * MINSECS;
$sessiondate = $formdata->sessiondate + $sesstarttime;
$duration = $sesendtime - $sesstarttime;
$now = time();
if (empty(get_config('attendance', 'studentscanmark'))) {
$formdata->studentscanmark = 0;
}
$sessions = array();
if (isset($formdata->addmultiply)) {
$startdate = $sessiondate;
$enddate = $formdata->sessionenddate + DAYSECS; // Because enddate in 0:0am.
if ($enddate < $startdate) {
return null;
}
// Getting first day of week.
$sdate = $startdate;
$dinfo = usergetdate($sdate);
if ($CFG->calendar_startwday === '0') { // Week start from sunday.
$startweek = $startdate - $dinfo['wday'] * DAYSECS; // Call new variable.
} else {
$wday = $dinfo['wday'] === 0 ? 7 : $dinfo['wday'];
$startweek = $startdate - ($wday - 1) * DAYSECS;
}
$wdaydesc = array(0 => 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
while ($sdate < $enddate) {
if ($sdate < $startweek + WEEKSECS) {
$dinfo = usergetdate($sdate);
if (isset($formdata->sdays) && array_key_exists($wdaydesc[$dinfo['wday']], $formdata->sdays)) {
$sess = new stdClass();
$sess->sessdate = make_timestamp($dinfo['year'], $dinfo['mon'], $dinfo['mday'],
$formdata->sestime['starthour'], $formdata->sestime['startminute']);
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
if (!empty($formdata->usedefaultsubnet)) {
$sess->subnet = $att->subnet;
} else {
$sess->subnet = $formdata->subnet;
}
$sess->automark = $formdata->automark;
if (isset($formdata->autoassignstatus)) {
$sess->autoassignstatus = 1;
}
$sess->automarkcompleted = 0;
if (!empty($formdata->randompassword)) {
$sess->studentpassword = attendance_random_string();
} else {
$sess->studentpassword = $formdata->studentpassword;
}
} else {
$sess->studentpassword = '';
$sess->subnet = '';
$sess->automark = 0;
$sess->automarkcompleted = 0;
}
$sess->statusset = $formdata->statusset;
attendance_fill_groupid($formdata, $sessions, $sess);
}
$sdate += DAYSECS;
} else {
$startweek += WEEKSECS * $formdata->period;
$sdate = $startweek;
}
}
} else {
$sess = new stdClass();
$sess->sessdate = $sessiondate;
$sess->duration = $duration;
$sess->descriptionitemid = $formdata->sdescription['itemid'];
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
$sess->studentscanmark = 0;
$sess->autoassignstatus = 0;
$sess->subnet = '';
$sess->studentpassword = '';
$sess->automark = 0;
$sess->automarkcompleted = 0;
$sess->absenteereport = 1;
if (isset($formdata->studentscanmark) && !empty($formdata->studentscanmark)) {
// Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
if (isset($formdata->autoassignstatus) && !empty($formdata->autoassignstatus)) {
$sess->autoassignstatus = 1;
}
if (!empty($formdata->randompassword)) {
$sess->studentpassword = attendance_random_string();
} else if (!empty($formdata->studentpassword)) {
$sess->studentpassword = $formdata->studentpassword;
}
if (!empty($formdata->usedefaultsubnet)) {
$sess->subnet = $att->subnet;
} else {
$sess->subnet = $formdata->subnet;
}
if (!empty($formdata->automark)) {
$sess->automark = $formdata->automark;
}
}
$sess->statusset = $formdata->statusset;
attendance_fill_groupid($formdata, $sessions, $sess);
}
return $sessions;
}
/**
* Helper function for attendance_construct_sessions_data_for_add().
*
8 years ago
* @param stdClass $formdata
* @param stdClass $sessions
* @param stdClass $sess
*/
function attendance_fill_groupid($formdata, &$sessions, $sess) {
if ($formdata->sessiontype == mod_attendance_structure::SESSION_COMMON) {
$sess = clone $sess;
$sess->groupid = 0;
$sessions[] = $sess;
} else {
foreach ($formdata->groups as $groupid) {
$sess = clone $sess;
$sess->groupid = $groupid;
$sessions[] = $sess;
}
}
}
/**
* Generates a summary of points for the courses selected.
*
* @param array $courseids optional list of courses to return
* @param string $orderby - optional order by param
* @return stdClass
*/
function attendance_course_users_points($courseids = array(), $orderby = '') {
global $DB;
$where = '';
$params = array();
$where .= ' AND ats.sessdate < :enddate ';
$params['enddate'] = time();
$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)';
if (!empty($courseids)) {
list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
$where .= ' AND c.id ' . $insql;
$params = array_merge($params, $inparams);
}
$sql = "SELECT courseid, coursename, sum(points) / sum(maxpoints) as percentage FROM (
SELECT a.id, a.course as courseid, c.fullname as coursename, atl.studentid AS userid, COUNT(DISTINCT ats.id) AS numtakensessions,
SUM(stg.grade) AS points, SUM(stm.maxgrade) AS maxpoints
FROM {attendance_sessions} ats
JOIN {attendance} a ON a.id = ats.attendanceid
JOIN {course} c ON c.id = a.course
JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE deleted = 0
AND visible = 1
GROUP BY attendanceid, setnumber) stm
ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
{$joingroup}
WHERE ats.sessdate >= c.startdate
AND ats.lasttaken != 0
{$where}
GROUP BY a.id, a.course, c.fullname, atl.studentid
) p GROUP by courseid, coursename {$orderby}";
return $DB->get_records_sql($sql, $params);
}
/**
* Generates a list of users flagged absent.
*
* @param array $courseids optional list of courses to return
7 years ago
* @param string $orderby how to order results.
* @param bool $allfornotify get notification list for scheduled task.
* @return stdClass
*/
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)) {
list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
$where .= ' AND c.id ' . $insql;
$params = array_merge($params, $inparams);
}
if ($allfornotify) {
// Exclude warnings that have already sent the max num.
$having .= ' AND n.maxwarn > COUNT(DISTINCT ns.id) ';
}
$unames = get_all_user_name_fields(true);
$unames2 = get_all_user_name_fields(true, 'u');
$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, 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
JOIN {course_modules} cm ON cm.instance = a.id
JOIN {course} c on c.id = cm.course
JOIN {modules} md ON md.id = cm.module AND md.name = 'attendance'
JOIN {attendance_log} atl ON (atl.sessionid = ats.id)
JOIN {user} u ON (u.id = atl.studentid)
JOIN {attendance_statuses} stg ON (stg.id = atl.statusid AND stg.deleted = 0 AND stg.visible = 1)
JOIN {attendance_warning} n ON n.idnumber = a.id
LEFT JOIN {attendance_warning_done} ns ON ns.notifyid = n.id AND ns.userid = atl.studentid
JOIN (SELECT attendanceid, setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE deleted = 0
AND visible = 1
GROUP BY attendanceid, setnumber) stm
ON (stm.setnumber = ats.statusset AND stm.attendanceid = ats.attendanceid)
{$joingroup}
WHERE ats.absenteereport = 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.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 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,
percent, coursename, {$unames} {$orderby}";
}
return $DB->get_records_sql($sql, $params);
}
/**
* Template variables into place in supplied email content.
*
* @param object $record db record of details
* @return array - the content of the fields after templating.
*/
function attendance_template_variables($record) {
$templatevars = array(
'/%coursename%/' => $record->coursename,
'/%courseid%/' => $record->courseid,
'/%userfirstname%/' => $record->firstname,
'/%userlastname%/' => $record->lastname,
'/%userid%/' => $record->userid,
'/%warningpercent%/' => $record->warningpercent,
'/%attendancename%/' => $record->aname,
'/%cmid%/' => $record->cmid,
'/%numtakensessions%/' => $record->numtakensessions,
'/%points%/' => $record->points,
'/%maxpoints%/' => $record->maxpoints,
'/%percent%/' => $record->percent,
);
$extrauserfields = get_all_user_name_fields();
foreach ($extrauserfields as $extra) {
$templatevars['/%'.$extra.'%/'] = $record->$extra;
}
$patterns = array_keys($templatevars); // The placeholders which are to be replaced.
$replacements = array_values($templatevars); // The values which are to be templated in for the placeholders.
// Array to describe which fields in reengagement object should have a template replacement.
$replacementfields = array('emailsubject', 'emailcontent');
// Replace %variable% with relevant value everywhere it occurs in reengagement->field.
foreach ($replacementfields as $field) {
$record->$field = preg_replace($patterns, $replacements, $record->$field);
}
return $record;
}
/**
* Find highest available status for a user.
*
* @param mod_attendance_structure $att attendance structure
* @param stdclass $attforsession attendance_session record.
* @return bool/int
*/
function attendance_session_get_highest_status(mod_attendance_structure $att, $attforsession) {
// Find the status to set here.
$statuses = $att->get_statuses();
$highestavailablegrade = 0;
$highestavailablestatus = new stdClass();
foreach ($statuses as $status) {
if ($status->studentavailability === '0') {
// This status is never available to students.
continue;
}
if (!empty($status->studentavailability)) {
$toolateforstatus = (($attforsession->sessdate + ($status->studentavailability * 60)) < time());
if ($toolateforstatus) {
continue;
}
}
// This status is available to the student.
if ($status->grade > $highestavailablegrade) {
// This is the most favourable grade so far; save it.
$highestavailablegrade = $status->grade;
$highestavailablestatus = $status;
}
}
if (empty($highestavailablestatus)) {
return false;
}
return $highestavailablestatus->id;
}