Browse Source

Merge pull request #201 from antonio-c-mariani/fix_reports

Addresses the #188, #127, #163, #38 and #66 issues
MOODLE_31_STABLE
Dan Marsden 9 years ago
parent
commit
0005d6b864
  1. 4
      classes/page_with_filter_controls.php
  2. 142
      classes/structure.php
  3. 268
      classes/summary.php
  4. 22
      export.php
  5. 11
      lang/en/attendance.php
  6. 5
      lib.php
  7. 211
      locallib.php
  8. 4
      preferences.php
  9. 101
      renderables.php
  10. 119
      renderer.php
  11. 88
      renderhelpers.php
  12. 8
      sessions.php
  13. 14
      styles.css
  14. 240
      tests/behat/report.feature

4
classes/page_with_filter_controls.php

@ -124,6 +124,10 @@ class mod_attendance_page_with_filter_controls {
$this->startdate = 0; $this->startdate = 0;
$this->enddate = 0; $this->enddate = 0;
break; break;
case ATT_VIEW_SUMMARY:
$this->startdate = 1;
$this->enddate = 1;
break;
} }
} }

142
classes/structure.php

@ -61,10 +61,6 @@ class mod_attendance_structure {
// Array by sessionid. // Array by sessionid.
private $sessioninfo = array(); private $sessioninfo = array();
// Arrays by userid.
private $usertakensesscount = array();
private $userstatusesstat = array();
/** /**
* Initializes the attendance API instance using the data from DB * Initializes the attendance API instance using the data from DB
* *
@ -233,6 +229,7 @@ class mod_attendance_structure {
'edate' => $this->pageparams->enddate, 'edate' => $this->pageparams->enddate,
'cgroup' => $this->pageparams->get_current_sesstype()); 'cgroup' => $this->pageparams->get_current_sesstype());
$sessions = $DB->get_records_select('attendance_sessions', $where, $params, 'sessdate asc'); $sessions = $DB->get_records_select('attendance_sessions', $where, $params, 'sessdate asc');
$statussetmaxpoints = attendance_get_statusset_maxpoints($this->get_statuses(true, true));
foreach ($sessions as $sess) { foreach ($sessions as $sess) {
if (empty($sess->description)) { if (empty($sess->description)) {
$sess->description = get_string('nodescription', 'attendance'); $sess->description = get_string('nodescription', 'attendance');
@ -240,6 +237,7 @@ class mod_attendance_structure {
$sess->description = file_rewrite_pluginfile_urls($sess->description, $sess->description = file_rewrite_pluginfile_urls($sess->description,
'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id); 'pluginfile.php', $this->context->id, 'mod_attendance', 'session', $sess->id);
} }
$sess->maxpoints = $statussetmaxpoints[$sess->statusset];
} }
return $sessions; return $sessions;
@ -746,138 +744,8 @@ class mod_attendance_structure {
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');
} }
public function get_user_stat($userid) {
$ret = array();
$ret['completed'] = $this->get_user_taken_sessions_count($userid);
$ret['statuses'] = $this->get_user_statuses_stat($userid);
return $ret;
}
public function get_user_taken_sessions_count($userid) {
if (!array_key_exists($userid, $this->usertakensesscount)) {
if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) {
$this->usertakensesscount[$userid] = attendance_get_user_taken_sessions_count($this->id, $this->course->startdate,
$userid, $this->cm, $this->pageparams->startdate, $this->pageparams->enddate);
} else {
$this->usertakensesscount[$userid] = attendance_get_user_taken_sessions_count($this->id, $this->course->startdate,
$userid, $this->cm);
}
}
return $this->usertakensesscount[$userid];
}
/**
*
* @param type $userid
* @param type $filters - An array things to filter by. For now only enddate is valid.
* @return type
*/
public function get_user_statuses_stat($userid, array $filters = null) {
global $DB;
$params = array(
'aid' => $this->id,
'cstartdate' => $this->course->startdate,
'uid' => $userid);
$processedfilters = array();
// We test for any valid filters sent.
if (isset($filters['enddate'])) {
$processedfilters[] = 'ats.sessdate <= :enddate';
$params['enddate'] = $filters['enddate'];
}
// Make the filter array into a SQL string.
if (!empty($processedfilters)) {
$processedfilters = ' AND '.implode(' AND ', $processedfilters);
} else {
$processedfilters = '';
}
$period = '';
if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) {
$period = ' AND ats.sessdate >= :sdate AND ats.sessdate < :edate ';
$params['sdate'] = $this->pageparams->startdate;
$params['edate'] = $this->pageparams->enddate;
}
if ($this->get_group_mode()) {
$qry = "SELECT al.statusid, count(al.statusid) AS stcnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats ON al.sessionid = ats.id
LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid AND
(ats.groupid = 0 or gm.id is NOT NULL)".$period.$processedfilters."
GROUP BY al.statusid";
} else {
$qry = "SELECT al.statusid, count(al.statusid) AS stcnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats
ON al.sessionid = ats.id
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid".$period.$processedfilters."
GROUP BY al.statusid";
}
// We do not want to cache, or use a cached version of the results when a filter is set.
if ($filters !== null) {
return $DB->get_records_sql($qry, $params);
} else if (!array_key_exists($userid, $this->userstatusesstat)) {
// Not filtered so if we do not already have them do the query.
$this->userstatusesstat[$userid] = $DB->get_records_sql($qry, $params);
}
// Return the cached stats.
return $this->userstatusesstat[$userid];
}
/**
*
* @param type $userid
* @param type $filters - An array things to filter by. For now only enddate is valid.
* @return type
*/
public function get_user_grade($userid, array $filters = null) {
return attendance_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses(true, true));
}
// For getting sessions count implemented simplest method - taken sessions.
// It can have error if users don't have attendance info for some sessions.
// In the future we can implement another methods:
// * all sessions between user start enrolment date and now;
// * all sessions between user start and end enrolment date.
// While implementing those methods we need recalculate grades of all users
// on session adding.
public function get_user_max_grade($userid) {
return attendance_get_user_max_grade($this->get_user_taken_sessions_count($userid), $this->get_statuses(true, true));
}
public function update_users_grade($userids) { public function update_users_grade($userids) {
global $DB; attendance_update_users_grade($this, $userids);
$grades = array();
if ($this->grade < 0) {
$dbparams = array('id' => -($this->grade));
$this->scale = $DB->get_record('scale', $dbparams);
$scalearray = explode(',', $this->scale->scale);
$attendancegrade = count($scalearray);
} else {
$attendancegrade = $this->grade;
}
foreach ($userids as $userid) {
$grades[$userid] = new stdClass();
$grades[$userid]->userid = $userid;
$grades[$userid]->rawgrade = attendance_calc_user_grade_fraction($this->get_user_grade($userid),
$this->get_user_max_grade($userid)) * $attendancegrade;
}
return grade_update('mod/attendance', $this->course->id, 'mod', 'attendance',
$this->id, 0, $grades);
} }
public function get_user_filtered_sessions_log($userid) { public function get_user_filtered_sessions_log($userid) {
@ -950,7 +818,7 @@ class mod_attendance_structure {
WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL) WHERE $where AND (ats.groupid = 0 or gm.id is NOT NULL)
ORDER BY ats.sessdate ASC"; ORDER BY ats.sessdate ASC";
} else { } else {
$sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
al.statusid, al.remarks, ats.studentscanmark al.statusid, al.remarks, ats.studentscanmark
FROM {attendance_sessions} ats FROM {attendance_sessions} ats
RIGHT JOIN {attendance_log} al RIGHT JOIN {attendance_log} al
@ -981,7 +849,7 @@ class mod_attendance_structure {
$where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql"; $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql";
} }
$sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
al.statusid, al.remarks, ats.studentscanmark al.statusid, al.remarks, ats.studentscanmark
FROM {attendance_sessions} ats FROM {attendance_sessions} ats
LEFT JOIN {attendance_log} al LEFT JOIN {attendance_log} al

268
classes/summary.php

@ -0,0 +1,268 @@
<?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/>.
/**
* Class that computes summary of users points
*
* @package mod_attendance
* @copyright 2016 Antonio Carlos Mariani http://antonio.c.mariani@gmail.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot . '/mod/attendance/locallib.php');
class mod_attendance_summary {
/** @var int attendance instance identifier */
private $attendanceid;
/** @var stdclass course course data*/
private $course;
/** @var int groupmode*/
private $groupmode;
/** @var array userspoints (userid, numtakensessions, points, maxpoints) */
private $userspoints;
/** @var array pointsbygroup (groupid, numsessions, maxpoints) */
private $maxpointsbygroupsessions;
/**
* Initializes the class
*
* @param int attendance instance identifier
* @param array userids user instances identifier
* @param int $startdate Attendance sessions startdate
* @param int $enddate Attendance sessions enddate
*/
public function __construct($attendanceid, $userids=array(), $startdate = '', $enddate = '') {
$this->attendanceid = $attendanceid;
$this->compute_users_points($userids, $startdate, $enddate);
}
/**
* Returns true if the user has some session with points
*
* @param int userid User instance id
*
* @return boolean
*/
public function has_taken_sessions($userid) {
return isset($this->userspoints[$userid]);
}
/**
* Returns true if the corresponding attendance instance is currently configure to work with grades (points)
*
* @return boolean
*/
public function with_groups() {
return $this->groupmode > 0;
}
/**
* Returns the groupmode of the corresponding attendance instance
*
* @return int
*/
public function get_groupmode() {
return $this->groupmode;
}
/**
* Returns the percentages of each user related to the taken sessions
*
* @return array
*/
public function get_user_taken_sessions_percentages() {
$percentages = array();
foreach ($this->userspoints as $userid => $userpoints) {
$percentages[$userid] = attendance_calc_fraction($userpoints->points, $userpoints->maxpoints);
}
return $percentages;
}
/**
* Returns a summary of the points assigned to the user related to the taken sessions
*
* @param int userid User instance id
*
* @return array
*/
public function get_taken_sessions_summary_for($userid) {
$usersummary = new stdClass();
if ($this->has_taken_sessions($userid)) {
$usersummary->numtakensessions = $this->userspoints[$userid]->numtakensessions;
$usersummary->takensessionspoints = $this->userspoints[$userid]->points;
$usersummary->takensessionsmaxpoints = $this->userspoints[$userid]->maxpoints;
} else {
$usersummary->numtakensessions = 0;
$usersummary->takensessionspoints = 0;
$usersummary->takensessionsmaxpoints = 0;
}
$usersummary->takensessionspercentage = attendance_calc_fraction($usersummary->takensessionspoints,
$usersummary->takensessionsmaxpoints);
return $usersummary;
}
/**
* Returns a summary of the points assigned to the user, both related to taken sessions and related to all sessions
*
* @param int userid User instance id
*
* @return array
*/
public function get_all_sessions_summary_for($userid) {
$usersummary = $this->get_taken_sessions_summary_for($userid);
if (!isset($this->maxpointsbygroupsessions)) {
$this->compute_maxpoints_by_group_session();
}
$usersummary->numallsessions = $this->maxpointsbygroupsessions[0]->numsessions;
$usersummary->allsessionsmaxpoints = $this->maxpointsbygroupsessions[0]->maxpoints;
if ($this->with_groups()) {
$groupids = array_keys(groups_get_all_groups($this->course->id, $userid));
foreach ($groupids as $gid) {
if (isset($this->maxpointsbygroupsessions[$gid])) {
$usersummary->numallsessions += $this->maxpointsbygroupsessions[$gid]->numsessions;
$usersummary->allsessionsmaxpoints += $this->maxpointsbygroupsessions[$gid]->maxpoints;
}
}
}
$usersummary->allsessionspercentage = attendance_calc_fraction($usersummary->takensessionspoints,
$usersummary->allsessionsmaxpoints);
$deltapoints = $usersummary->allsessionsmaxpoints - $usersummary->takensessionsmaxpoints;
$usersummary->maxpossiblepoints = $usersummary->takensessionspoints + $deltapoints;
$usersummary->maxpossiblepercentage = attendance_calc_fraction($usersummary->maxpossiblepoints,
$usersummary->allsessionsmaxpoints);
return $usersummary;
}
/**
* Computes the summary of points for the users that have some taken session
*
* @param array userids user instances identifier
* @param int $startdate Attendance sessions startdate
* @param int $enddate Attendance sessions enddate
* @return (userid, numtakensessions, points, maxpoints)
*/
private function compute_users_points($userids=array(), $startdate = '', $enddate = '') {
global $DB;
list($this->course, $cm) = get_course_and_cm_from_instance($this->attendanceid, 'attendance');
$this->groupmode = $cm->effectivegroupmode;
$params = array(
'attid' => $this->attendanceid,
'attid2' => $this->attendanceid,
'cstartdate' => $this->course->startdate,
);
$where = '';
if (!empty($userids)) {
list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$where .= ' AND atl.studentid ' . $insql;
$params = array_merge($params, $inparams);
}
if (!empty($startdate)) {
$where .= ' AND ats.sessdate >= :startdate';
$params['startdate'] = $startdate;
}
if (!empty($enddate)) {
$where .= ' AND ats.sessdate < :enddate ';
$params['enddate'] = $enddate;
}
$joingroup = '';
if ($this->with_groups()) {
$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)';
} else {
$where .= ' AND ats.groupid = 0';
}
$sql = " SELECT 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_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 setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE attendanceid = :attid2
AND deleted = 0
AND visible = 1
GROUP BY setnumber) stm
ON (stm.setnumber = ats.statusset)
{$joingroup}
WHERE ats.attendanceid = :attid
AND ats.sessdate >= :cstartdate
AND ats.lasttakenby != 0
{$where}
GROUP BY atl.studentid";
$this->userspoints = $DB->get_records_sql($sql, $params);
}
/**
* Computes and store the maximum points possible for each group session
*
* @return null
*/
private function compute_maxpoints_by_group_session() {
global $DB;
$params = array(
'attid' => $this->attendanceid,
'attid2' => $this->attendanceid,
'cstartdate' => $this->course->startdate,
);
$where = '';
if (!$this->with_groups()) {
$where = 'AND sess.groupid = 0';
}
$sql = "SELECT sess.groupid, COUNT(*) AS numsessions, SUM(stamax.maxgrade) AS maxpoints
FROM {attendance_sessions} sess
JOIN (SELECT setnumber, MAX(grade) AS maxgrade
FROM {attendance_statuses}
WHERE attendanceid = :attid2
AND deleted = 0
AND visible = 1
GROUP BY setnumber) stamax
ON (stamax.setnumber = sess.statusset)
WHERE sess.attendanceid = :attid
AND sess.sessdate >= :cstartdate
{$where}
GROUP BY sess.groupid";
$this->maxpointsbygroupsessions = $DB->get_records_sql($sql, $params);
if (!isset($this->maxpointsbygroupsessions[0])) {
$gpoints = new stdClass();
$gpoints->numsessions = 0;
$gpoints->maxpoints = 0;
$this->maxpointsbygroupsessions[0] = $gpoints;
}
}
}

22
export.php

@ -123,10 +123,9 @@ if ($formdata = $mform->get_data()) {
} else { } else {
print_error('sessionsnotfound', 'attendance', $att->url_manage()); print_error('sessionsnotfound', 'attendance', $att->url_manage());
} }
if ($reportdata->gradable) { $data->tabhead[] = get_string('takensessions', 'attendance');
$data->tabhead[] = get_string('grade'); $data->tabhead[] = get_string('points', 'attendance');
$data->tabhead[] = get_string('percentage', 'attendance'); $data->tabhead[] = get_string('percentage', 'attendance');
}
$i = 0; $i = 0;
$data->table = array(); $data->table = array();
@ -158,16 +157,13 @@ if ($formdata = $mform->get_data()) {
} }
$cellsgenerator = new user_sessions_cells_text_generator($reportdata, $user); $cellsgenerator = new user_sessions_cells_text_generator($reportdata, $user);
$data->table[$i] = array_merge($data->table[$i], $cellsgenerator->get_cells(isset($formdata->includeremarks))); $data->table[$i] = array_merge($data->table[$i], $cellsgenerator->get_cells(isset($formdata->includeremarks)));
if ($reportdata->gradable) {
$data->table[$i][] = format_float($reportdata->grades[$user->id]).' / '. $usersummary = $reportdata->summary->get_taken_sessions_summary_for($user->id);
format_float($reportdata->maxgrades[$user->id]); $data->table[$i][] = $usersummary->numtakensessions;
if ($reportdata->maxgrades[$user->id]) { $data->table[$i][] = format_float($usersummary->takensessionspoints, 1, true, true) . ' / ' .
$percent = $reportdata->grades[$user->id] * 100.0 / $reportdata->maxgrades[$user->id]; format_float($usersummary->takensessionsmaxpoints, 1, true, true);
} else { $data->table[$i][] = format_float($usersummary->takensessionspercentage * 100);
$percent = 0.0;
}
$data->table[$i][] = $percent;
}
$i++; $i++;
} }

11
lang/en/attendance.php

@ -133,6 +133,8 @@ $string['indetail'] = 'In detail...';
$string['invalidsessionenddate'] = 'This date can not be earlier than the session date'; $string['invalidsessionenddate'] = 'This date can not be earlier than the session date';
$string['invalidaction'] = 'You must select an action'; $string['invalidaction'] = 'You must select an action';
$string['jumpto'] = 'Jump to'; $string['jumpto'] = 'Jump to';
$string['maxpossiblepoints'] = 'Maximum possible points';
$string['maxpossiblepercentage'] = 'Maximum possible percentage';
$string['mergeuser'] = 'Merge user'; $string['mergeuser'] = 'Merge user';
$string['modulename'] = 'Attendance'; $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. $string['modulename_help'] = 'The attendance activity module enables a teacher to take attendance during class and students to view their own attendance record.
@ -165,6 +167,8 @@ $string['olddate'] = 'Old date';
$string['onlyselectedusers'] = 'Export specific users'; $string['onlyselectedusers'] = 'Export specific users';
$string['participant'] = 'Participant'; $string['participant'] = 'Participant';
$string['percentage'] = 'Percentage'; $string['percentage'] = 'Percentage';
$string['percentagesessionscompleted'] = 'Percentage over taked sessions';
$string['percentageallsessions'] = 'Percentage over all sessions';
$string['pluginname'] = 'Attendance'; $string['pluginname'] = 'Attendance';
$string['pluginadministration'] = 'Attendance administration'; $string['pluginadministration'] = 'Attendance administration';
$string['remark'] = 'Remark for: {$a}'; $string['remark'] = 'Remark for: {$a}';
@ -221,7 +225,8 @@ $string['sessiondays'] = 'Session Days';
$string['sessiondeleted'] = 'Session successfully deleted'; $string['sessiondeleted'] = 'Session successfully deleted';
$string['sessionexist'] = 'Session not added (already exists)!'; $string['sessionexist'] = 'Session not added (already exists)!';
$string['sessions'] = 'Sessions'; $string['sessions'] = 'Sessions';
$string['sessionscompleted'] = 'Sessions completed'; $string['sessionscompleted'] = 'Taked sessions';
$string['sessionstotal'] = 'Total number of sessions';
$string['sessionsids'] = 'IDs of sessions: '; $string['sessionsids'] = 'IDs of sessions: ';
$string['sessiongenerated'] = 'One session was successfully generated'; $string['sessiongenerated'] = 'One session was successfully generated';
$string['sessionsgenerated'] = '{$a} sessions were successfully generated'; $string['sessionsgenerated'] = '{$a} sessions were successfully generated';
@ -255,7 +260,9 @@ $string['strftimedmyw'] = '%d.%m.%y&nbsp;(%a)';
$string['strftimehm'] = '%H:%M'; // Line added to allow display of time. $string['strftimehm'] = '%H:%M'; // Line added to allow display of time.
$string['strftimeshortdate'] = '%d.%m.%Y'; $string['strftimeshortdate'] = '%d.%m.%Y';
$string['studentid'] = 'Student ID'; $string['studentid'] = 'Student ID';
$string['summary'] = 'Summary';
$string['takeattendance'] = 'Take attendance'; $string['takeattendance'] = 'Take attendance';
$string['takensessions'] = 'Taken sessions';
$string['tempaddform'] = 'Add temporary user'; $string['tempaddform'] = 'Add temporary user';
$string['tempexists'] = 'There is already a temporary user with this email address'; $string['tempexists'] = 'There is already a temporary user with this email address';
$string['tempusers'] = 'Temporary users'; $string['tempusers'] = 'Temporary users';
@ -301,6 +308,8 @@ $string['submitattendance'] = 'Submit attendance';
$string['attendancenotset'] = 'You must set your attendance'; $string['attendancenotset'] = 'You must set your attendance';
$string['export'] = 'Export'; $string['export'] = 'Export';
$string['points'] = 'Points'; $string['points'] = 'Points';
$string['pointssessionscompleted'] = 'Points over taked sessions';
$string['pointsallsessions'] = 'Points over all sessions';
$string['unknowngroup'] = 'Unknown group'; $string['unknowngroup'] = 'Unknown group';
$string['notmember'] = 'not&nbsp;member'; $string['notmember'] = 'not&nbsp;member';

5
lib.php

@ -250,7 +250,7 @@ function attendance_user_complete($course, $user, $mod, $attendance) {
require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->libdir.'/gradelib.php');
if (has_capability('mod/attendance:canbelisted', $mod->context, $user->id)) { if (has_capability('mod/attendance:canbelisted', $mod->context, $user->id)) {
echo construct_full_user_stat_html_table($attendance, $course, $user, $mod); echo construct_full_user_stat_html_table($attendance, $user);
} }
} }
@ -276,7 +276,7 @@ function attendance_grade_item_update($attendance, $grades=null) {
if (!isset($attendance->courseid)) { if (!isset($attendance->courseid)) {
$attendance->courseid = $attendance->course; $attendance->courseid = $attendance->course;
} }
if (! $course = $DB->get_record('course', array('id' => $attendance->course))) { if (!$DB->get_record('course', array('id' => $attendance->course))) {
error("Course is misconfigured"); error("Course is misconfigured");
} }
@ -284,7 +284,6 @@ function attendance_grade_item_update($attendance, $grades=null) {
$params = array('itemname' => $attendance->name, 'idnumber' => $attendance->cmidnumber); $params = array('itemname' => $attendance->name, 'idnumber' => $attendance->cmidnumber);
} else { } else {
// MDL-14303. // MDL-14303.
$cm = get_coursemodule_from_instance('attendance', $attendance->id);
$params = array('itemname' => $attendance->name/*, 'idnumber'=>$attendance->id*/); $params = array('itemname' => $attendance->name/*, 'idnumber'=>$attendance->id*/);
} }

211
locallib.php

@ -33,6 +33,7 @@ define('ATT_VIEW_MONTHS', 3);
define('ATT_VIEW_ALLPAST', 4); define('ATT_VIEW_ALLPAST', 4);
define('ATT_VIEW_ALL', 5); define('ATT_VIEW_ALL', 5);
define('ATT_VIEW_NOTPRESENT', 6); define('ATT_VIEW_NOTPRESENT', 6);
define('ATT_VIEW_SUMMARY', 7);
define('ATT_SORT_LASTNAME', 1); define('ATT_SORT_LASTNAME', 1);
define('ATT_SORT_FIRSTNAME', 2); define('ATT_SORT_FIRSTNAME', 2);
@ -88,86 +89,6 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) {
return $statusname; return $statusname;
} }
function attendance_get_user_taken_sessions_count($attid, $coursestartdate, $userid, $coursemodule, $startdate = '', $enddate = '') {
global $DB, $COURSE;
$groupmode = groups_get_activity_groupmode($coursemodule, $COURSE);
if (!empty($groupmode)) {
$qry = "SELECT count(*) as cnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats ON al.sessionid = ats.id
LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid AND
(ats.groupid = 0 or gm.id is NOT NULL)";
} else {
$qry = "SELECT count(*) as cnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats
ON al.sessionid = ats.id
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid";
}
$params = array(
'aid' => $attid,
'cstartdate' => $coursestartdate,
'uid' => $userid);
if (!empty($startdate) && !empty($enddate)) {
$qry .= ' AND sessdate >= :sdate AND sessdate < :edate ';
$params['sdate'] = $startdate;
$params['edate'] = $enddate;
}
return $DB->count_records_sql($qry, $params);
}
function attendance_get_user_statuses_stat($attid, $coursestartdate, $userid, $coursemodule) {
global $DB, $COURSE;
$groupmode = groups_get_activity_groupmode($coursemodule, $COURSE);
if (!empty($groupmode)) {
$qry = "SELECT al.statusid, count(al.statusid) AS stcnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats ON al.sessionid = ats.id
LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid AND
(ats.groupid = 0 or gm.id is NOT NULL)
GROUP BY al.statusid";
} else {
$qry = "SELECT al.statusid, count(al.statusid) AS stcnt
FROM {attendance_log} al
JOIN {attendance_sessions} ats
ON al.sessionid = ats.id
WHERE ats.attendanceid = :aid AND
ats.sessdate >= :cstartdate AND
al.studentid = :uid
GROUP BY al.statusid";
}
$params = array(
'aid' => $attid,
'cstartdate' => $coursestartdate,
'uid' => $userid);
return $DB->get_records_sql($qry, $params);
}
function attendance_get_user_grade($userstatusesstat, $statuses) {
$sum = 0;
foreach ($userstatusesstat as $stat) {
$sum += $stat->stcnt * $statuses[$stat->statusid]->grade;
}
return $sum;
}
function attendance_get_user_max_grade($sesscount, $statuses) {
reset($statuses);
return current($statuses)->grade * $sesscount;
}
function attendance_get_user_courses_attendances($userid) { function attendance_get_user_courses_attendances($userid) {
global $DB; global $DB;
@ -189,71 +110,18 @@ function attendance_get_user_courses_attendances($userid) {
} }
/** /**
* Used to caclulate usergrade based on rawgrade and max grade. * Used to calculate a fraction based on the part and total values
* *
* @param float $grade - raw grade for user * @param float $part - part of the total value
* @param float $maxgrade - maxgrade for this session. * @param float $total - total value.
* @return float the calculated grade. * @return float the calculated fraction.
*/ */
function attendance_calc_user_grade_fraction($grade, $maxgrade) { function attendance_calc_fraction($part, $total) {
if ($maxgrade == 0) { if ($total == 0) {
return 0; return 0;
} else { } else {
return $grade / $maxgrade; return $part / $total;
}
}
/**
* Update all user grades - used when settings have changed.
*
* @param mod_attendance_structure $attendance - Full attendance class.
* @param stdclass $coursemodule - full coursemodule record
* @return float the calculated grade.
*/
function attendance_update_all_users_grades(mod_attendance_structure $attendance, $coursemodule) {
global $DB;
$grades = array();
$course = $attendance->course;
$userids = array_keys(get_enrolled_users($attendance->context, 'mod/attendance:canbelisted', 0, 'u.id'));
$attgrades = grade_get_grades($course->id, 'mod', 'attendance', $attendance->id, $userids);
$usergrades = [];
if (!empty($attgrades->items[0]) and !empty($attgrades->items[0]->grades)) {
$usergrades = $attgrades->items[0]->grades;
}
$statuses = attendance_get_statuses($attendance->id);
if ($attendance->grade < 0) {
$dbparams = array('id' => -($attendance->grade));
$scale = $DB->get_record('scale', $dbparams);
$scalearray = explode(',', $scale->scale);
$gradebookmaxgrade = count($scalearray);
} else {
$gradebookmaxgrade = $attendance->grade;
}
foreach ($usergrades as $userid => $existinggrade) {
if (is_null($existinggrade->grade)) {
// Don't update grades where one doesn't exist yet.
continue;
}
$grade = new stdClass;
$grade->userid = $userid;
$userstatusesstat = attendance_get_user_statuses_stat($attendance->id, $course->startdate, $userid, $coursemodule);
$usertakensesscount = attendance_get_user_taken_sessions_count($attendance->id, $course->startdate, $userid, $coursemodule);
$usergrade = attendance_get_user_grade($userstatusesstat, $statuses);
$usermaxgrade = attendance_get_user_max_grade($usertakensesscount, $statuses);
$grade->rawgrade = attendance_calc_user_grade_fraction($usergrade, $usermaxgrade) * $gradebookmaxgrade;
$grades[$userid] = $grade;
}
if (!empty($grades)) {
$result = grade_update('mod/attendance', $course->id, 'mod', 'attendance',
$attendance->id, 0, $grades);
} else {
$result = true;
} }
return $result;
} }
/** /**
@ -308,3 +176,66 @@ function attendance_get_max_statusset($attendanceid) {
} }
return 0; return 0;
} }
/**
* Returns the maxpoints for each statusset
*
* @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
*
* @param mixed 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);
}

4
preferences.php

@ -122,9 +122,7 @@ switch ($att->pageparams->action) {
$status = $statuses[$id]; $status = $statuses[$id];
$errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null); $errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null);
} }
if ($att->grade > 0) { attendance_update_users_grade($att);
attendance_update_all_users_grades($att, $cm);
}
break; break;
} }

101
renderables.php

@ -128,7 +128,7 @@ class attendance_filter_controls implements renderable {
private $urlpath; private $urlpath;
private $urlparams; private $urlparams;
private $att; public $att;
public function __construct(mod_attendance_structure $att, $report = false) { public function __construct(mod_attendance_structure $att, $report = false) {
global $PAGE; global $PAGE;
@ -138,7 +138,7 @@ class attendance_filter_controls implements renderable {
$this->cm = $att->cm; $this->cm = $att->cm;
// This is a report control only if $reports is true and the attendance block can be graded. // This is a report control only if $reports is true and the attendance block can be graded.
$this->reportcontrol = $report && ($att->grade > 0); $this->reportcontrol = $report;
$this->curdate = $att->pageparams->curdate; $this->curdate = $att->pageparams->curdate;
@ -336,17 +336,9 @@ class attendance_user_data implements renderable {
public $pageparams; public $pageparams;
public $stat;
public $statuses; public $statuses;
public $gradable; public $summary;
public $grade;
public $maxgrade;
public $decimalpoints;
public $filtercontrols; public $filtercontrols;
@ -360,26 +352,15 @@ class attendance_user_data implements renderable {
private $urlparams; private $urlparams;
public function __construct(mod_attendance_structure $att, $userid) { public function __construct(mod_attendance_structure $att, $userid) {
global $CFG;
$this->user = $att->get_user($userid); $this->user = $att->get_user($userid);
$this->pageparams = $att->pageparams; $this->pageparams = $att->pageparams;
if (!$this->decimalpoints = grade_get_setting($att->course->id, 'decimalpoints')) {
$this->decimalpoints = $CFG->grade_decimalpoints;
}
if ($this->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) { if ($this->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) {
$this->statuses = $att->get_statuses(true, true); $this->statuses = $att->get_statuses(true, true);
$this->stat = $att->get_user_stat($userid); $this->summary = new mod_attendance_summary($att->id, array($userid), $att->pageparams->startdate,
$att->pageparams->enddate);
$this->gradable = $att->grade > 0;
if ($this->gradable) {
$this->grade = $att->get_user_grade($userid);
$this->maxgrade = $att->get_user_max_grade($userid);
}
$this->filtercontrols = new attendance_filter_controls($att); $this->filtercontrols = new attendance_filter_controls($att);
@ -389,40 +370,15 @@ class attendance_user_data implements renderable {
} else { } else {
$this->coursesatts = attendance_get_user_courses_attendances($userid); $this->coursesatts = attendance_get_user_courses_attendances($userid);
$this->statuses = array(); $this->statuses = array();
$this->stat = array(); $this->summary = array();
$this->gradable = array();
$this->grade = array();
$this->maxgrade = array();
foreach ($this->coursesatts as $atid => $ca) { foreach ($this->coursesatts as $atid => $ca) {
// Check to make sure the user can view this cm. // Check to make sure the user can view this cm.
if (!get_fast_modinfo($ca->courseid)->instances['attendance'][$ca->attid]->uservisible) { if (!get_fast_modinfo($ca->courseid)->instances['attendance'][$ca->attid]->uservisible) {
unset($this->courseatts[$atid]); unset($this->courseatts[$atid]);
continue; continue;
} }
$statuses = attendance_get_statuses($ca->attid); $this->statuses[$ca->attid] = attendance_get_statuses($ca->attid);
$usertakensessionscount = attendance_get_user_taken_sessions_count($ca->attid, $ca->coursestartdate, $userid, $att->cm); $this->summary[$ca->attid] = new mod_attendance_summary($ca->attid, array($userid));
$userstatusesstat = attendance_get_user_statuses_stat($ca->attid, $ca->coursestartdate, $userid, $att->cm);
$this->statuses[$ca->attid] = $statuses;
$this->stat[$ca->attid]['completed'] = $usertakensessionscount;
$this->stat[$ca->attid]['statuses'] = $userstatusesstat;
$this->gradable[$ca->attid] = $ca->attgrade > 0;
if ($this->gradable[$ca->attid]) {
$this->grade[$ca->attid] = attendance_get_user_grade($userstatusesstat, $statuses);
// For getting sessions count implemented simplest method - taken sessions.
// It can have error if users don't have attendance info for some sessions.
// In the future we can implement another methods:
// * all sessions between user start enrolment date and now;
// * all sessions between user start and end enrolment date.
$this->maxgrade[$ca->attid] = attendance_get_user_max_grade($usertakensessionscount, $statuses);
} else {
// For more comfortable and universal work with arrays.
$this->grade[$ca->attid] = null;
$this->maxgrade[$ca->attid] = null;
}
} }
} }
$this->urlpath = $att->url_view()->out_omit_querystring(); $this->urlpath = $att->url_view()->out_omit_querystring();
@ -449,25 +405,15 @@ class attendance_report_data implements renderable {
// Includes disablrd/deleted statuses. // Includes disablrd/deleted statuses.
public $allstatuses; public $allstatuses;
public $gradable;
public $decimalpoints;
public $usersgroups = array(); public $usersgroups = array();
public $sessionslog = array(); public $sessionslog = array();
public $usersstats = array(); public $summary = array();
public $grades = array();
public $maxgrades = array();
public $att; public $att;
public function __construct(mod_attendance_structure $att) { public function __construct(mod_attendance_structure $att) {
global $CFG;
$currenttime = time(); $currenttime = time();
if ($att->pageparams->view == ATT_VIEW_NOTPRESENT) { if ($att->pageparams->view == ATT_VIEW_NOTPRESENT) {
$att->pageparams->enddate = $currenttime; $att->pageparams->enddate = $currenttime;
@ -492,32 +438,21 @@ class attendance_report_data implements renderable {
$this->statuses = $att->get_statuses(true, true); $this->statuses = $att->get_statuses(true, true);
$this->allstatuses = $att->get_statuses(false, true); $this->allstatuses = $att->get_statuses(false, true);
$this->gradable = $att->grade > 0; if ($att->pageparams->view == ATT_VIEW_SUMMARY) {
$this->summary = new mod_attendance_summary($att->id);
if (!$this->decimalpoints = grade_get_setting($att->course->id, 'decimalpoints')) { } else {
$this->decimalpoints = $CFG->grade_decimalpoints; $this->summary = new mod_attendance_summary($att->id, array_keys($this->users),
$att->pageparams->startdate, $att->pageparams->enddate);
} }
$maxgrade = attendance_get_user_max_grade(count($this->sessions), $this->statuses);
foreach ($this->users as $key => $user) { foreach ($this->users as $key => $user) {
$grade = 0; $usersummary = $this->summary->get_taken_sessions_summary_for($user->id);
if ($this->gradable) { if ($att->pageparams->view != ATT_VIEW_NOTPRESENT ||
$grade = $att->get_user_grade($user->id, array('enddate' => $currenttime)); $usersummary->takensessionspoints < $usersummary->takensessionsmaxpoints ||
$totalgrade = $att->get_user_grade($user->id); $usersummary->takensessionsmaxpoints == 0) {
}
if ($att->pageparams->view != ATT_VIEW_NOTPRESENT || $grade < $maxgrade) {
$this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id); $this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id);
$this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id); $this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id);
$this->usersstats[$user->id] = $att->get_user_statuses_stat($user->id);
if ($this->gradable) {
$this->grades[$user->id] = $totalgrade;
$this->maxgrades[$user->id] = $att->get_user_max_grade($user->id);;
}
} else { } else {
unset($this->users[$key]); unset($this->users[$key]);
} }

119
renderer.php

@ -176,12 +176,15 @@ class mod_attendance_renderer extends plugin_renderer_base {
protected function render_view_controls(attendance_filter_controls $fcontrols) { protected function render_view_controls(attendance_filter_controls $fcontrols) {
$views[ATT_VIEW_ALL] = get_string('all', 'attendance'); $views[ATT_VIEW_ALL] = get_string('all', 'attendance');
$views[ATT_VIEW_ALLPAST] = get_string('allpast', 'attendance'); $views[ATT_VIEW_ALLPAST] = get_string('allpast', 'attendance');
if ($fcontrols->reportcontrol) { if ($fcontrols->reportcontrol && $fcontrols->att->grade > 0) {
$views[ATT_VIEW_NOTPRESENT] = get_string('lowgrade', 'attendance'); $views[ATT_VIEW_NOTPRESENT] = get_string('lowgrade', 'attendance');
} }
$views[ATT_VIEW_MONTHS] = get_string('months', 'attendance'); $views[ATT_VIEW_MONTHS] = get_string('months', 'attendance');
$views[ATT_VIEW_WEEKS] = get_string('weeks', 'attendance'); $views[ATT_VIEW_WEEKS] = get_string('weeks', 'attendance');
$views[ATT_VIEW_DAYS] = get_string('days', 'attendance'); $views[ATT_VIEW_DAYS] = get_string('days', 'attendance');
if ($fcontrols->reportcontrol) {
$views[ATT_VIEW_SUMMARY] = get_string('summary', 'attendance');
}
$viewcontrols = ''; $viewcontrols = '';
foreach ($views as $key => $sview) { foreach ($views as $key => $sview) {
if ($key != $fcontrols->pageparams->view) { if ($key != $fcontrols->pageparams->view) {
@ -299,8 +302,6 @@ class mod_attendance_renderer extends plugin_renderer_base {
} }
protected function render_sess_manage_control(attendance_manage_data $sessdata) { protected function render_sess_manage_control(attendance_manage_data $sessdata) {
global $OUTPUT;
$table = new html_table(); $table = new html_table();
$table->attributes['class'] = ' '; $table->attributes['class'] = ' ';
$table->width = '100%'; $table->width = '100%';
@ -735,8 +736,8 @@ class mod_attendance_renderer extends plugin_renderer_base {
if ($userdata->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) { if ($userdata->pageparams->mode == mod_attendance_view_page_params::MODE_THIS_COURSE) {
$o .= html_writer::empty_tag('hr'); $o .= html_writer::empty_tag('hr');
$o .= construct_user_data_stat($userdata->stat, $userdata->statuses, $o .= construct_user_data_stat($userdata->summary->get_all_sessions_summary_for($userdata->user->id),
$userdata->gradable, $userdata->grade, $userdata->maxgrade, $userdata->decimalpoints); $userdata->pageparams->view);
$o .= $this->render_attendance_filter_controls($userdata->filtercontrols); $o .= $this->render_attendance_filter_controls($userdata->filtercontrols);
@ -752,9 +753,12 @@ class mod_attendance_renderer extends plugin_renderer_base {
} }
$o .= html_writer::tag('h4', $ca->attname); $o .= html_writer::tag('h4', $ca->attname);
$o .= construct_user_data_stat($userdata->stat[$ca->attid], $userdata->statuses[$ca->attid], if (isset($userdata->summary[$ca->attid])) {
$userdata->gradable[$ca->attid], $userdata->grade[$ca->attid], $usersummary = $userdata->summary[$ca->attid]->get_all_sessions_summary_for($userdata->user->id);
$userdata->maxgrade[$ca->attid], $userdata->decimalpoints); } else {
$usersummary = null;
}
$o .= construct_user_data_stat($usersummary, ATT_VIEW_ALL);
} }
} }
@ -771,10 +775,13 @@ class mod_attendance_renderer extends plugin_renderer_base {
get_string('time'), get_string('time'),
get_string('description', 'attendance'), get_string('description', 'attendance'),
get_string('status', 'attendance'), get_string('status', 'attendance'),
get_string('points', 'attendance'),
get_string('remarks', 'attendance') get_string('remarks', 'attendance')
); );
$table->align = array('', '', '', 'left', 'left', 'center', 'left', 'center'); $table->align = array('', '', '', 'left', 'left', 'center', 'center', 'center');
$table->size = array('1px', '1px', '1px', '1px', '*', '1px', '1px', '*'); $table->size = array('1px', '1px', '1px', '1px', '*', '*', '1px', '*');
$statussetmaxpoints = attendance_get_statusset_maxpoints($userdata->statuses);
$i = 0; $i = 0;
foreach ($userdata->sessionslog as $sess) { foreach ($userdata->sessionslog as $sess) {
@ -793,7 +800,10 @@ class mod_attendance_renderer extends plugin_renderer_base {
$row->cells[] = $this->construct_time($sess->sessdate, $sess->duration); $row->cells[] = $this->construct_time($sess->sessdate, $sess->duration);
$row->cells[] = $sess->description; $row->cells[] = $sess->description;
if (isset($sess->statusid)) { if (isset($sess->statusid)) {
$row->cells[] = $userdata->statuses[$sess->statusid]->description; $status = $userdata->statuses[$sess->statusid];
$row->cells[] = $status->description;
$row->cells[] = format_float($status->grade, 1, true, true) . ' / ' .
format_float($statussetmaxpoints[$status->setnumber], 1, true, true);
$row->cells[] = $sess->remarks; $row->cells[] = $sess->remarks;
} else if ($sess->sessdate < $userdata->user->enrolmentstart) { } else if ($sess->sessdate < $userdata->user->enrolmentstart) {
$cell = new html_table_cell(get_string('enrolmentstart', 'attendance', $cell = new html_table_cell(get_string('enrolmentstart', 'attendance',
@ -815,6 +825,7 @@ class mod_attendance_renderer extends plugin_renderer_base {
$row->cells[] = $cell; $row->cells[] = $cell;
} else { // Student cannot mark their own attendace. } else { // Student cannot mark their own attendace.
$row->cells[] = '?'; $row->cells[] = '?';
$row->cells[] = '? / ' . format_float($statussetmaxpoints[$sess->statusset], 1, true, true);
$row->cells[] = ''; $row->cells[] = '';
} }
} }
@ -843,6 +854,9 @@ class mod_attendance_renderer extends plugin_renderer_base {
$table = new html_table(); $table = new html_table();
$table->attributes['class'] = 'generaltable attwidth'; $table->attributes['class'] = 'generaltable attwidth';
if ($reportdata->pageparams->view == ATT_VIEW_SUMMARY) {
$table->attributes['class'] .= ' summaryreport';
}
// User picture. // User picture.
$table->head[] = ''; $table->head[] = '';
@ -882,15 +896,36 @@ class mod_attendance_renderer extends plugin_renderer_base {
$table->size[] = '1px'; $table->size[] = '1px';
} }
foreach ($reportdata->statuses as $status) { $table->head[] = get_string('takensessions', 'attendance');
$table->head[] = $status->acronym; $table->align[] = 'center';
$table->size[] = '1px';
$table->head[] = get_string('points', 'attendance');
$table->align[] = 'center'; $table->align[] = 'center';
$table->size[] = '1px'; $table->size[] = '1px';
$sessionstats[$status->id] = 0;
}
if ($reportdata->gradable) { $table->head[] = get_string('percentage', 'attendance');
$table->head[] = get_string('grade'); $table->align[] = 'center';
$table->size[] = '1px';
if ($reportdata->pageparams->view == ATT_VIEW_SUMMARY) {
$table->head[] = get_string('sessionstotal', 'attendance');
$table->align[] = 'center';
$table->size[] = '1px';
$table->head[] = get_string('pointsallsessions', 'attendance');
$table->align[] = 'center';
$table->size[] = '1px';
$table->head[] = get_string('percentageallsessions', 'attendance');
$table->align[] = 'center';
$table->size[] = '1px';
$table->head[] = get_string('maxpossiblepoints', 'attendance');
$table->align[] = 'center';
$table->size[] = '1px';
$table->head[] = get_string('maxpossiblepercentage', 'attendance');
$table->align[] = 'center'; $table->align[] = 'center';
$table->size[] = '1px'; $table->size[] = '1px';
} }
@ -910,17 +945,25 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cellsgenerator = new user_sessions_cells_html_generator($reportdata, $user); $cellsgenerator = new user_sessions_cells_html_generator($reportdata, $user);
$row->cells = array_merge($row->cells, $cellsgenerator->get_cells(true)); $row->cells = array_merge($row->cells, $cellsgenerator->get_cells(true));
foreach ($reportdata->statuses as $status) { if ($reportdata->pageparams->view == ATT_VIEW_SUMMARY) {
if (array_key_exists($status->id, $reportdata->usersstats[$user->id])) { $usersummary = $reportdata->summary->get_all_sessions_summary_for($user->id);
$row->cells[] = $reportdata->usersstats[$user->id][$status->id]->stcnt;
} else { } else {
// No attendance data for this $status => no statistic for this status. $usersummary = $reportdata->summary->get_taken_sessions_summary_for($user->id);
$row->cells[] = 0;
}
} }
$row->cells[] = $usersummary->numtakensessions;
$row->cells[] = format_float($usersummary->takensessionspoints, 1, true, true) . ' / ' .
format_float($usersummary->takensessionsmaxpoints, 1, true, true);
$row->cells[] = format_float($usersummary->takensessionspercentage * 100) . '%';
if ($reportdata->pageparams->view == ATT_VIEW_SUMMARY) {
$row->cells[] = $usersummary->numallsessions;
$row->cells[] = format_float($usersummary->takensessionspoints, 1, true, true) . ' / ' .
format_float($usersummary->allsessionsmaxpoints, 1, true, true);
$row->cells[] = format_float($usersummary->allsessionspercentage * 100) . '%';
if ($reportdata->gradable) { $row->cells[] = format_float($usersummary->maxpossiblepoints, 1, true, true) . ' / ' .
$row->cells[] = format_float($reportdata->grades[$user->id]).' / '.format_float($reportdata->maxgrades[$user->id]); format_float($usersummary->allsessionsmaxpoints, 1, true, true);
$row->cells[] = format_float($usersummary->maxpossiblepercentage * 100) . '%';
} }
if ($bulkmessagecapability) { // Create the checkbox for bulk messaging. if ($bulkmessagecapability) { // Create the checkbox for bulk messaging.
@ -936,22 +979,30 @@ class mod_attendance_renderer extends plugin_renderer_base {
$statrow->cells[] = ''; $statrow->cells[] = '';
$statrow->cells[] = get_string('summary'); $statrow->cells[] = get_string('summary');
foreach ($reportdata->sessions as $sess) { foreach ($reportdata->sessions as $sess) {
foreach ($reportdata->users as $user) { $sessionstats = array();
foreach ($reportdata->statuses as $status) { foreach ($reportdata->statuses as $status) {
if (!empty($reportdata->sessionslog[$user->id][$sess->id])) { if ($status->setnumber == $sess->statusset) {
if ($reportdata->sessionslog[$user->id][$sess->id]->statusid == $status->id) { $status->count = 0;
$sessionstats[$status->id]++; $sessionstats[$status->id] = $status;
}
} }
foreach ($reportdata->users as $user) {
if (!empty($reportdata->sessionslog[$user->id][$sess->id])) {
$statusid = $reportdata->sessionslog[$user->id][$sess->id]->statusid;
if (isset($sessionstats[$statusid]->count)) {
$sessionstats[$statusid]->count++;
} }
} }
} }
$statsoutput = '<br/>'; $statsoutput = '<br/>';
foreach ($reportdata->statuses as $status) { foreach ($sessionstats as $status) {
$statsoutput .= "$status->description:".$sessionstats[$status->id]." <br/>"; $statsoutput .= "$status->description: {$status->count}<br/>";
} }
$statrow->cells[] = $statsoutput; $cell = new html_table_cell($statsoutput);
$cell->style = 'white-space:nowrap;';
$statrow->cells[] = $cell;
} }
$table->data[] = $statrow; $table->data[] = $statrow;
@ -1008,7 +1059,7 @@ class mod_attendance_renderer extends plugin_renderer_base {
$table->head = array('#', $table->head = array('#',
get_string('acronym', 'attendance'), get_string('acronym', 'attendance'),
get_string('description'), get_string('description'),
get_string('grade'), get_string('points', 'attendance'),
get_string('action')); get_string('action'));
$table->align = array('center', 'center', 'center', 'center', 'center', 'center'); $table->align = array('center', 'center', 'center', 'center', 'center', 'center');

88
renderhelpers.php

@ -49,7 +49,10 @@ class user_sessions_cells_generator {
if (array_key_exists($sess->id, $this->reportdata->sessionslog[$this->user->id])) { if (array_key_exists($sess->id, $this->reportdata->sessionslog[$this->user->id])) {
$statusid = $this->reportdata->sessionslog[$this->user->id][$sess->id]->statusid; $statusid = $this->reportdata->sessionslog[$this->user->id][$sess->id]->statusid;
if (array_key_exists($statusid, $this->reportdata->statuses)) { if (array_key_exists($statusid, $this->reportdata->statuses)) {
$this->construct_existing_status_cell($this->reportdata->statuses[$statusid]->acronym); $points = format_float($this->reportdata->statuses[$statusid]->grade, 1, true, true);
$maxpoints = format_float($sess->maxpoints, 1, true, true);
$this->construct_existing_status_cell($this->reportdata->statuses[$statusid]->acronym .
" ({$points}/{$maxpoints})");
} else { } else {
$this->construct_hidden_status_cell($this->reportdata->allstatuses[$statusid]->acronym); $this->construct_hidden_status_cell($this->reportdata->allstatuses[$statusid]->acronym);
} }
@ -228,65 +231,66 @@ function construct_session_full_date_time($datetime, $duration) {
return $sessinfo; return $sessinfo;
} }
function construct_user_data_stat($stat, $statuses, $gradable, $grade, $maxgrade, $decimalpoints) { function construct_user_data_stat($usersummary, $view) {
global $OUTPUT;
$stattable = new html_table(); $stattable = new html_table();
$stattable->attributes['class'] = 'attlist'; $stattable->attributes['class'] = 'attlist';
$row = new html_table_row(); $row = new html_table_row();
$row->attributes['class'] = 'normal';
$row->cells[] = get_string('sessionscompleted', 'attendance') . ':'; $row->cells[] = get_string('sessionscompleted', 'attendance') . ':';
$row->cells[] = $stat['completed']; $row->cells[] = $usersummary->numtakensessions;
$stattable->data[] = $row; $stattable->data[] = $row;
foreach ($statuses as $st) {
$row = new html_table_row(); $row = new html_table_row();
$row->cells[] = $st->description . ':'; $row->attributes['class'] = 'normal';
$row->cells[] = array_key_exists($st->id, $stat['statuses']) ? $stat['statuses'][$st->id]->stcnt : 0; $row->cells[] = get_string('pointssessionscompleted', 'attendance') . ':';
$row->cells[] = format_float($usersummary->takensessionspoints, 1, true, true) . ' / ' .
format_float($usersummary->takensessionsmaxpoints, 1, true, true);
$stattable->data[] = $row;
$row = new html_table_row();
$row->attributes['class'] = 'normal';
$row->cells[] = get_string('percentagesessionscompleted', 'attendance') . ':';
$row->cells[] = format_float($usersummary->takensessionspercentage * 100) . '%';
$stattable->data[] = $row; $stattable->data[] = $row;
}
if ($gradable) { if ($view == ATT_VIEW_ALL) {
$row = new html_table_row(); $row = new html_table_row();
$row->cells[] = get_string('attendancegrade', 'attendance') . $row->attributes['class'] = 'highlight';
$OUTPUT->help_icon('gradebookexplanation', 'attendance') . ':'; $row->cells[] = get_string('sessionstotal', 'attendance') . ':';
$row->cells[] = format_float($grade) . ' / ' . format_float($maxgrade); $row->cells[] = $usersummary->numallsessions;
$stattable->data[] = $row; $stattable->data[] = $row;
$row = new html_table_row(); $row = new html_table_row();
$row->cells[] = get_string('attendancepercent', 'attendance') . ':'; $row->attributes['class'] = 'highlight';
if ($maxgrade == 0) { $row->cells[] = get_string('pointsallsessions', 'attendance') . ':';
$percent = 0; $row->cells[] = format_float($usersummary->takensessionspoints, 1, true, true) . ' / ' .
} else { format_float($usersummary->allsessionsmaxpoints, 1, true, true);
$percent = $grade / $maxgrade * 100;
}
$row->cells[] = format_float(sprintf("%0.{$decimalpoints}f", $percent));
$stattable->data[] = $row; $stattable->data[] = $row;
}
return html_writer::table($stattable); $row = new html_table_row();
} $row->attributes['class'] = 'highlight';
$row->cells[] = get_string('percentageallsessions', 'attendance') . ':';
$row->cells[] = format_float($usersummary->allsessionspercentage * 100) . '%';
$stattable->data[] = $row;
$row = new html_table_row();
$row->attributes['class'] = 'normal';
$row->cells[] = get_string('maxpossiblepoints', 'attendance') . ':';
$row->cells[] = format_float($usersummary->maxpossiblepoints, 1, true, true) . ' / ' .
format_float($usersummary->allsessionsmaxpoints, 1, true, true);
$stattable->data[] = $row;
function construct_full_user_stat_html_table($attendance, $course, $user, $coursemodule) { $row = new html_table_row();
global $CFG; $row->attributes['class'] = 'normal';
$gradeable = $attendance->grade > 0; $row->cells[] = get_string('maxpossiblepercentage', 'attendance') . ':';
$statuses = attendance_get_statuses($attendance->id); $row->cells[] = format_float($usersummary->maxpossiblepercentage * 100) . '%';
$userstatusesstat = attendance_get_user_statuses_stat($attendance->id, $course->startdate, $user->id, $coursemodule); $stattable->data[] = $row;
$stat['completed'] = attendance_get_user_taken_sessions_count($attendance->id, $course->startdate, $user->id, $coursemodule);
$stat['statuses'] = $userstatusesstat;
if ($gradeable) {
$grade = attendance_get_user_grade($userstatusesstat, $statuses);
$maxgrade = attendance_get_user_max_grade(attendance_get_user_taken_sessions_count($attendance->id, $course->startdate,
$user->id, $coursemodule), $statuses);
if (!$decimalpoints = grade_get_setting($course->id, 'decimalpoints')) {
$decimalpoints = $CFG->grade_decimalpoints;
} }
} else {
$grade = 0; return html_writer::table($stattable);
$maxgrade = 0;
$decimalpoints = 0;
} }
return construct_user_data_stat($stat, $statuses, function construct_full_user_stat_html_table($attendance, $user) {
$gradeable, $grade, $maxgrade, $decimalpoints); $summary = new mod_attendance_summary($attendance->id, $user->id);
return construct_user_data_stat($summary->get_all_sessions_summary_for($user->id), ATT_VIEW_ALL);
} }

8
sessions.php

@ -113,9 +113,7 @@ switch ($att->pageparams->action) {
if (isset($confirm) && confirm_sesskey()) { if (isset($confirm) && confirm_sesskey()) {
$att->delete_sessions(array($sessionid)); $att->delete_sessions(array($sessionid));
if ($att->grade > 0) { attendance_update_users_grade($att);
attendance_update_all_users_grades($att, $cm);
}
redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance'));
} }
@ -142,9 +140,7 @@ switch ($att->pageparams->action) {
$sessionsids = explode('_', $sessionsids); $sessionsids = explode('_', $sessionsids);
$att->delete_sessions($sessionsids); $att->delete_sessions($sessionsids);
if ($att->grade > 0) { attendance_update_users_grade($att);
attendance_update_all_users_grades($att, $cm);
}
redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance'));
} }
$sessid = optional_param_array('sessid', '', PARAM_SEQUENCE); $sessid = optional_param_array('sessid', '', PARAM_SEQUENCE);

14
styles.css

@ -17,7 +17,7 @@
} }
.path-mod-attendance .attfiltercontrols { .path-mod-attendance .attfiltercontrols {
margin-bottom: 10px; margin-bottom: 10px !important;
margin-right:auto; margin-right:auto;
margin-left:auto; margin-left:auto;
} }
@ -165,3 +165,15 @@
.path-mod-attendance .attendancestatus-A { .path-mod-attendance .attendancestatus-A {
color: red; color: red;
} }
.path-mod-attendance .summaryreport .c5 {
background-color: #EAEAEA;
}
.path-mod-attendance .summaryreport .c6 {
background-color: #EAEAEA;
}
.path-mod-attendance .summaryreport .c7 {
background-color: #EAEAEA;
}

240
tests/behat/report.feature

@ -0,0 +1,240 @@
@javascript @mod @uon @mod_attendance
Feature: Visiting reports
As a teacher I visit the reports
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category |
| Course 1 | C101 | Prove the attendance activity works | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | department | institution |
| student1 | Student | 1 | student1@asd.com | 1234 | computer science | University of Nottingham |
| teacher1 | Teacher | 1 | teacher1@asd.com | 5678 | computer science | University of Nottingham |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C101 | student |
| teacher1 | C101 | editingteacher |
And I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
Then I add a "Attendance" to section "1"
And I press "Save and display"
And I follow "Attendance"
And I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 01 |
| id_sestime_endhour | 02 |
And I click on "id_submitbutton" "button"
And I log out
Scenario: Teacher takes attendance
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Report"
Then "0 / 0" "text" should exist in the "Student 1" "table_row"
And "0.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "-" "text" should exist in the "Student 1" "table_row"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "01:00 - 02:00" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
And "25.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher changes the maximum points in the attendance settings
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "01:00 - 02:00" "table_row"
# Excused
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Attendance"
And I follow "Edit settings"
Then I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 70 |
And I press "Save and display"
When I follow "Report"
Then "1 / 2" "text" should exist in the "Student 1" "table_row"
And "50.0%" "text" should exist in the "Student 1" "table_row"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "35.00" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher take attendance of group session
Given the following "groups" exist:
| course | name | idnumber |
| C101 | Group1 | Group1 |
And the following "group members" exist:
| group | user |
| Group1 | student1 |
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
| id_groupmode | Visible groups |
And I press "Save and display"
When I follow "Attendance"
Then I click on "Take attendance" "link" in the "01:00 - 02:00" "table_row"
# Excused
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
| id_sessiontype_1 | 1 |
| id_groups | Group1 |
And I click on "id_submitbutton" "button"
Then I should see "03:00 - 04:00"
And "Group: Group1" "text" should exist in the "03:00 - 04:00" "table_row"
When I click on "Take attendance" "link" in the "03:00 - 04:00" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Report"
Then "Student 1" row "Points" column of "generaltable" table should contain "3 / 4"
And "Student 1" row "Percentage" column of "generaltable" table should contain "75.0%"
When I follow "Grades" in the user menu
And I follow "Course 1"
Then "37.50" "text" should exist in the "Student 1" "table_row"
And I log out
Scenario: Teacher visit summary report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "01:00 - 02:00" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
Then I should see "03:00 - 04:00"
When I click on "Take attendance" "link" in the "03:00 - 04:00" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I should see "05:00 - 06:00"
When I follow "Report"
And I click on "Summary" "link" in the "All" "table_row"
Then "Student 1" row "Total number of sessions" column of "generaltable" table should contain "3"
And "Student 1" row "Points over all sessions" column of "generaltable" table should contain "3 / 6"
And "Student 1" row "Percentage over all sessions" column of "generaltable" table should contain "50.0%"
And "Student 1" row "Maximum possible points" column of "generaltable" table should contain "5 / 6"
And "Student 1" row "Maximum possible percentage" column of "generaltable" table should contain "83.3%"
And I log out
Scenario: Student visit user report
When I log in as "teacher1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "Edit settings"
And I set the following fields to these values:
| id_grade_modgrade_type | Point |
| id_grade_modgrade_point | 50 |
And I press "Save and display"
When I click on "Take attendance" "link" in the "01:00 - 02:00" "table_row"
# Late
And I click on "td.c3 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 03 |
| id_sestime_endhour | 04 |
And I click on "id_submitbutton" "button"
When I click on "Take attendance" "link" in the "03:00 - 04:00" "table_row"
# Present
And I click on "td.c2 input" "css_element" in the "Student 1" "table_row"
And I press "Save attendance"
When I follow "Add session"
And I set the following fields to these values:
| id_sestime_starthour | 05 |
| id_sestime_endhour | 06 |
And I click on "id_submitbutton" "button"
Then I log out
When I log in as "student1"
And I follow "Course 1"
And I follow "Attendance"
And I follow "All"
Then "2" "text" should exist in the "Taked sessions" "table_row"
And "3 / 4" "text" should exist in the "Points over taked sessions:" "table_row"
And "75.0%" "text" should exist in the "Percentage over taked sessions:" "table_row"
And "3" "text" should exist in the "Total number of sessions:" "table_row"
And "3 / 6" "text" should exist in the "Points over all sessions:" "table_row"
And "50.0%" "text" should exist in the "Percentage over all sessions:" "table_row"
And "5 / 6" "text" should exist in the "Maximum possible points:" "table_row"
And "83.3%" "text" should exist in the "Maximum possible percentage:" "table_row"
And I log out
Loading…
Cancel
Save