Concentrate grades update method in one place
authorAntonio Carlos Mariani <antonio.c.mariani@ufsc.br>
Mon, 2 May 2016 21:06:28 +0000 (18:06 -0300)
committerAntonio Carlos Mariani <antonio.c.mariani@ufsc.br>
Wed, 4 May 2016 19:51:36 +0000 (16:51 -0300)
classes/structure.php
classes/summary.php [new file with mode: 0644]
locallib.php
preferences.php
sessions.php

index 67d67ff..6f4ff8a 100644 (file)
@@ -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 (file)
index 0000000..9f7279e
--- /dev/null
@@ -0,0 +1,249 @@
+<?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
+ */
+
+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;
+        }
+    }
+}
index 9cd860e..9082ec8 100644 (file)
@@ -189,74 +189,21 @@ 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.
  *
  * @param integer $statusid
@@ -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);
+}
index 061912a..194dd54 100644 (file)
@@ -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;
 }
 
index dc790f8..f879a1a 100644 (file)
@@ -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);