From 0129310e21a3f9487458bff03461c64208042a93 Mon Sep 17 00:00:00 2001 From: Antonio Carlos Mariani Date: Mon, 2 May 2016 18:06:28 -0300 Subject: [PATCH] Concentrate grades update method in one place --- classes/structure.php | 22 +--- classes/summary.php | 249 ++++++++++++++++++++++++++++++++++++++++++ locallib.php | 114 +++++++++---------- preferences.php | 4 +- sessions.php | 8 +- 5 files changed, 307 insertions(+), 90 deletions(-) create mode 100644 classes/summary.php diff --git a/classes/structure.php b/classes/structure.php index 67d67ff..6f4ff8a 100644 --- a/classes/structure.php +++ b/classes/structure.php @@ -857,27 +857,7 @@ class mod_attendance_structure { } public function update_users_grade($userids) { - global $DB; - $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); + attendance_update_users_grade($this, $userids); } public function get_user_filtered_sessions_log($userid) { diff --git a/classes/summary.php b/classes/summary.php new file mode 100644 index 0000000..9f7279e --- /dev/null +++ b/classes/summary.php @@ -0,0 +1,249 @@ +. + +/** + * 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 + */ + +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 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 userid, COUNT(*) AS numtakensessions, SUM(grade) AS points, SUM(maxgrade) AS maxpoints + FROM (SELECT atl.studentid AS userid, ats.id AS sessionid, stg.grade, stm.maxgrade + 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} + ) sess + GROUP BY userid"; + $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; + } + } +} diff --git a/locallib.php b/locallib.php index 9cd860e..9082ec8 100644 --- a/locallib.php +++ b/locallib.php @@ -189,73 +189,20 @@ 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 $maxgrade - maxgrade for this session. - * @return float the calculated grade. + * @param float $part - part of the total value + * @param float $total - total value. + * @return float the calculated fraction. */ -function attendance_calc_user_grade_fraction($grade, $maxgrade) { - if ($maxgrade == 0) { +function attendance_calc_fraction($part, $total) { + if ($total == 0) { return 0; } 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; -} - /** * Check to see if statusid in use to help prevent deletion etc. * @@ -308,3 +255,50 @@ function attendance_get_max_statusset($attendanceid) { } return 0; } + +/** + * 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); +} diff --git a/preferences.php b/preferences.php index 061912a..194dd54 100644 --- a/preferences.php +++ b/preferences.php @@ -122,9 +122,7 @@ switch ($att->pageparams->action) { $status = $statuses[$id]; $errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null); } - if ($att->grade > 0) { - attendance_update_all_users_grades($att, $cm); - } + attendance_update_users_grade($att); break; } diff --git a/sessions.php b/sessions.php index dc790f8..f879a1a 100644 --- a/sessions.php +++ b/sessions.php @@ -113,9 +113,7 @@ switch ($att->pageparams->action) { if (isset($confirm) && confirm_sesskey()) { $att->delete_sessions(array($sessionid)); - if ($att->grade > 0) { - attendance_update_all_users_grades($att, $cm); - } + attendance_update_users_grade($att); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); } @@ -142,9 +140,7 @@ switch ($att->pageparams->action) { $sessionsids = explode('_', $sessionsids); $att->delete_sessions($sessionsids); - if ($att->grade > 0) { - attendance_update_all_users_grades($att, $cm); - } + attendance_update_users_grade($att); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); } $sessid = optional_param_array('sessid', '', PARAM_SEQUENCE);