Allow an attendance instance to have multiple status sets
authorDavo Smith <git@davosmith.co.uk>
Thu, 25 Jun 2015 10:20:53 +0000 (11:20 +0100)
committerDavo Smith <git@davosmith.co.uk>
Fri, 26 Jun 2015 07:14:33 +0000 (08:14 +0100)
The set to use can be selected per session

13 files changed:
add_form.php
db/install.xml
db/upgrade.php
lang/en/attendance.php
lib.php
locallib.php
preferences.php
renderables.php
renderer.php
sessions.php
tests/behat/extra_features.feature
update_form.php
version.php

index 9786f40..f216caa 100644 (file)
@@ -148,6 +148,19 @@ class mod_attendance_add_form extends moodleform {
         $mform->addGroup($periodgroup, 'periodgroup', get_string('period', 'attendance'), array(' '), false);
         $mform->disabledIf('periodgroup', 'addmultiply', 'notchecked');
 
+        // Select which status set to use:
+        $maxstatusset = attendance_get_max_statusset($this->_customdata['att']->id);
+        if ($maxstatusset > 0) {
+            $opts = array();
+            for ($i=0; $i<=$maxstatusset; $i++) {
+                $opts[$i] = att_get_setname($this->_customdata['att']->id, $i);
+            }
+            $mform->addElement('select', 'statusset', get_string('usestatusset', 'mod_attendance'), $opts);
+        } else {
+            $mform->addElement('hidden', 'statusset', 0);
+            $mform->setType('statusset', PARAM_INT);
+        }
+
         $mform->addElement('editor', 'sdescription', get_string('description', 'attendance'),
                            null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$modcontext));
         $mform->setType('sdescription', PARAM_RAW);
index 65e43d5..b1fc454 100644 (file)
@@ -31,6 +31,7 @@
         <FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="studentscanmark" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false"/>
+        <FIELD NAME="statusset" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Which set of statuses to use"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance_sessions"/>
@@ -70,6 +71,7 @@
         <FIELD NAME="grade" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
         <FIELD NAME="deleted" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
+        <FIELD NAME="setnumber" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Allows different sets of statuses to be allocated to different sessions"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance_settings"/>
index 8ec0802..75bf87e 100644 (file)
@@ -121,5 +121,29 @@ function xmldb_attendance_upgrade($oldversion=0) {
         upgrade_mod_savepoint(true, 2015040501, 'attendance');
     }
 
+    if ($oldversion < 2015040502) {
+
+        // Define field setnumber to be added to attendance_statuses.
+        $table = new xmldb_table('attendance_statuses');
+        $field = new xmldb_field('setnumber', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0', 'deleted');
+
+        // Conditionally launch add field setnumber.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Define field statusset to be added to attendance_sessions.
+        $table = new xmldb_table('attendance_sessions');
+        $field = new xmldb_field('statusset', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0', 'descriptionformat');
+
+        // Conditionally launch add field statusset.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Attendance savepoint reached.
+        upgrade_mod_savepoint(true, 2015040502, 'attendance');
+    }
+
     return $result;
 }
index 5ac7897..6bf3481 100644 (file)
@@ -143,6 +143,7 @@ $string['moreattendance'] = 'Attendance has been successfully taken for this pag
 $string['myvariables'] = 'My Variables';
 $string['newdate'] = 'New date';
 $string['newduration'] = 'New duration';
+$string['newstatusset'] = 'New set of statuses';
 $string['noattforuser'] = 'No attendance records exist for the user';
 $string['nodescription'] = 'Regular class session';
 $string['noguest'] = 'Guest can\'t see attendance';
@@ -240,6 +241,7 @@ $string['startofperiod'] = 'Start of period';
 $string['status'] = 'Status';
 $string['statuses'] = 'Statuses';
 $string['statusdeleted'] = 'Status deleted';
+$string['statusset'] = 'Status set {$a}';
 $string['strftimedm'] = '%d.%m';
 $string['strftimedmy'] = '%d.%m.%Y';
 $string['strftimedmyhm'] = '%d.%m.%Y %H.%M'; // Line added to allow multiple sessions in the same day.
@@ -263,6 +265,7 @@ $string['tempusermerge'] = 'Merge temporary user';
 $string['tuseremail'] = 'Email';
 $string['tusername'] = 'Full name';
 $string['update'] = 'Update';
+$string['usestatusset'] = 'Use status set';
 $string['userexists'] = 'There is already a real user with this email address';
 $string['variable'] = 'variable';
 $string['variablesupdated'] = 'Variables successfully updated';
diff --git a/lib.php b/lib.php
index 134288b..ab61452 100644 (file)
--- a/lib.php
+++ b/lib.php
@@ -406,3 +406,15 @@ function attendance_pluginfile($course, $cm, $context, $filearea, $args, $forced
     }
     send_stored_file($file, 0, 0, true);
 }
+
+// Count the number of status sets that exist for this instance.
+function attendance_get_max_statusset($attendanceid) {
+    global $DB;
+
+    $max = $DB->get_field_sql('SELECT MAX(setnumber) FROM {attendance_statuses} WHERE attendanceid = ? AND deleted = 0',
+                              array($attendanceid));
+    if ($max) {
+        return $max;
+    }
+    return 0;
+}
index 09c8b89..122159b 100644 (file)
@@ -524,6 +524,8 @@ class att_preferences_page_params {
 
     public $statusid;
 
+    public $statusset;
+
     public function get_significant_params() {
         $params = array();
 
@@ -533,6 +535,9 @@ class att_preferences_page_params {
         if (isset($this->statusid)) {
             $params['statusid'] = $this->statusid;
         }
+        if (isset($this->statusset)) {
+            $params['statusset'] = $this->statusset;
+        }
 
         return $params;
     }
@@ -571,6 +576,7 @@ class attendance {
     private $groupmode;
 
     private $statuses;
+    private $allstatuses; // Cache list of all statuses (not just one used by current session).
 
     // Array by sessionid.
     private $sessioninfo = array();
@@ -815,6 +821,10 @@ class attendance {
      * @return moodle_url of attsettings.php for attendance instance
      */
     public function url_preferences($params=array()) {
+        // Add the statusset params.
+        if (isset($this->pageparams->statusset) && !isset($params['statusset'])) {
+            $params['statusset'] = $this->pageparams->statusset;
+        }
         $params = array_merge(array('id' => $this->cm->id), $params);
         return new moodle_url('/mod/attendance/preferences.php', $params);
     }
@@ -1177,11 +1187,24 @@ class attendance {
         return $user;
     }
 
-    public function get_statuses($onlyvisible = true) {
+    public function get_statuses($onlyvisible = true, $allsets = false) {
         if (!isset($this->statuses)) {
-            $this->statuses = att_get_statuses($this->id, $onlyvisible);
+            // Get the statuses for the current set only.
+            $statusset = 0;
+            if (isset($this->pageparams->statusset)) {
+                $statusset = $this->pageparams->statusset;
+            } else if (isset($this->pageparams->sessionid)) {
+                $sessioninfo = $this->get_session_info($this->pageparams->sessionid);
+                $statusset = $sessioninfo->statusset;
+            }
+            $this->statuses = att_get_statuses($this->id, $onlyvisible, $statusset);
+            $this->allstatuses = att_get_statuses($this->id, $onlyvisible);
         }
 
+        // Return all sets, if requested.
+        if ($allsets) {
+            return $this->allstatuses;
+        }
         return $this->statuses;
     }
 
@@ -1320,7 +1343,7 @@ class attendance {
      * @return type
      */
     public function get_user_grade($userid, array $filters = null) {
-        return att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses());
+        return att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses(true, true));
     }
 
     // For getting sessions count implemented simplest method - taken sessions.
@@ -1331,7 +1354,7 @@ class attendance {
     // While implementing those methods we need recalculate grades of all users
     // on session adding.
     public function get_user_max_grade($userid) {
-        return att_get_user_max_grade($this->get_user_taken_sessions_count($userid), $this->get_statuses());
+        return att_get_user_max_grade($this->get_user_taken_sessions_count($userid), $this->get_statuses(true, true));
     }
 
     public function update_users_grade($userids) {
@@ -1542,6 +1565,7 @@ class attendance {
             $rec->acronym = $acronym;
             $rec->description = $description;
             $rec->grade = $grade;
+            $rec->setnumber = $this->pageparams->statusset; // Save which set it is part of.
             $rec->deleted = 0;
             $rec->visible = 1;
             $id = $DB->insert_record('attendance_statuses', $rec);
@@ -1629,20 +1653,57 @@ class attendance {
 }
 
 
-function att_get_statuses($attid, $onlyvisible=true) {
+function att_get_statuses($attid, $onlyvisible=true, $statusset = -1) {
     global $DB;
 
+    // Set selector.
+    $params = array('aid' => $attid);
+    $setsql = '';
+    if ($statusset >= 0) {
+        $params['statusset'] = $statusset;
+        $setsql = ' AND setnumber = :statusset ';
+    }
+
     if ($onlyvisible) {
-        $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND visible = 1 AND deleted = 0",
-                                            array('aid' => $attid), 'grade DESC');
+        $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND visible = 1 AND deleted = 0 $setsql",
+                                            $params, 'setnumber ASC, grade DESC');
     } else {
-        $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND deleted = 0",
-                                            array('aid' => $attid), 'grade DESC');
+        $statuses = $DB->get_records_select('attendance_statuses', "attendanceid = :aid AND deleted = 0 $setsql",
+                                            $params, 'setnumber ASC, grade DESC');
     }
 
     return $statuses;
 }
 
+/**
+ * Get the name of the status set.
+ *
+ * @param int $attid
+ * @param int $statusset
+ * @param bool $includevalues
+ * @return string
+ */
+function att_get_setname($attid, $statusset, $includevalues = true) {
+    $statusname = get_string('statusset', 'mod_attendance', $statusset + 1);
+    if ($includevalues) {
+        $statuses = att_get_statuses($attid, true, $statusset);
+        $statusesout = array();
+        foreach ($statuses as $status) {
+            $statusesout[] = $status->acronym;
+        }
+        if ($statusesout) {
+            if (count($statusesout) > 6) {
+                $statusesout = array_slice($statusesout, 0, 6);
+                $statusesout[] = '&helip;';
+            }
+            $statusesout = implode(' ', $statusesout);
+            $statusname .= ' ('.$statusesout.')';
+        }
+    }
+
+    return $statusname;
+}
+
 function att_get_user_taken_sessions_count($attid, $coursestartdate, $userid, $coursemodule, $startdate = '', $enddate = '') {
     global $DB, $COURSE;
     $groupmode = groups_get_activity_groupmode($coursemodule, $COURSE);
index ce30721..210734e 100644 (file)
@@ -30,6 +30,7 @@ $pageparams = new att_preferences_page_params();
 $id                         = required_param('id', PARAM_INT);
 $pageparams->action         = optional_param('action', null, PARAM_INT);
 $pageparams->statusid       = optional_param('statusid', null, PARAM_INT);
+$pageparams->statusset      = optional_param('statusset', 0, PARAM_INT); // Set of statuses to view.
 
 $cm             = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST);
 $course         = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
@@ -37,6 +38,12 @@ $att            = $DB->get_record('attendance', array('id' => $cm->instance), '*
 
 require_login($course, true, $cm);
 
+// Make sure the statusset is valid.
+$maxstatusset = attendance_get_max_statusset($att->id);
+if ($pageparams->statusset > $maxstatusset + 1) {
+    $pageparams->statusset = $maxstatusset + 1;
+}
+
 $att = new attendance($att, $cm, $course, $PAGE->context, $pageparams);
 
 $att->perm->require_change_preferences_capability();
@@ -55,6 +62,9 @@ switch ($att->pageparams->action) {
         $newgrade           = optional_param('newgrade', 0, PARAM_INT);
 
         $att->add_status($newacronym, $newdescription, $newgrade);
+        if ($pageparams->statusset > $maxstatusset) {
+            $maxstatusset = $pageparams->statusset; // Make sure the new maximum is shown without a page refresh.
+        }
         break;
     case att_preferences_page_params::ACTION_DELETE:
         if (att_has_logs_for_status($att->pageparams->statusid)) {
@@ -109,12 +119,14 @@ switch ($att->pageparams->action) {
 $output = $PAGE->get_renderer('mod_attendance');
 $tabs = new attendance_tabs($att, attendance_tabs::TAB_PREFERENCES);
 $prefdata = new attendance_preferences_data($att);
+$setselector = new attendance_set_selector($att, $maxstatusset);
 
 // Output starts here.
 
 echo $output->header();
 echo $output->heading(get_string('attendanceforthecourse', 'attendance').' :: ' .$course->fullname);
 echo $output->render($tabs);
+echo $output->render($setselector);
 echo $output->render($prefdata);
 
 echo $output->footer();
index ac9b37e..047a461 100644 (file)
@@ -369,7 +369,7 @@ class attendance_user_data implements renderable {
         }
 
         if ($this->pageparams->mode == att_view_page_params::MODE_THIS_COURSE) {
-            $this->statuses = $att->get_statuses();
+            $this->statuses = $att->get_statuses(true, true);
 
             $this->stat = $att->get_user_stat($userid);
 
@@ -482,8 +482,8 @@ class attendance_report_data implements renderable {
 
         $this->sessions = $att->get_filtered_sessions();
 
-        $this->statuses = $att->get_statuses();
-        $this->allstatuses = $att->get_statuses(false);
+        $this->statuses = $att->get_statuses(true, true);
+        $this->allstatuses = $att->get_statuses(false, true);
 
         $this->gradable = $att->grade > 0;
 
@@ -559,6 +559,36 @@ class attendance_preferences_data implements renderable {
     }
 }
 
+// Output a selector to change between status sets.
+class attendance_set_selector implements renderable {
+    public $maxstatusset;
+
+    private $att;
+
+    public function __construct(attendance $att, $maxstatusset) {
+        $this->att = $att;
+        $this->maxstatusset = $maxstatusset;
+    }
+
+    public function url($statusset) {
+        $params = array();
+        $params['statusset'] = $statusset;
+
+        return $this->att->url_preferences($params);
+    }
+
+    public function get_current_statusset() {
+        if (isset($this->att->pageparams->statusset)) {
+            return $this->att->pageparams->statusset;
+        }
+        return 0;
+    }
+
+    public function get_status_name($statusset) {
+        return att_get_setname($this->att->id, $statusset, true);
+    }
+}
+
 class url_helpers {
     public static function url_take($att, $sessionid, $grouptype) {
         $params = array('sessionid' => $sessionid);
index 46b4617..2aad1e8 100644 (file)
@@ -918,6 +918,33 @@ class mod_attendance_renderer extends plugin_renderer_base {
             
     }
 
+    /**
+     * Output the status set selector.
+     *
+     * @param attendance_set_selector $sel
+     * @return string
+     */
+    protected function render_attendance_set_selector(attendance_set_selector $sel) {
+        $current = $sel->get_current_statusset();
+        $selected = null;
+        $opts = array();
+        for ($i = 0; $i <= $sel->maxstatusset; $i++) {
+            $url = $sel->url($i);
+            $display = $sel->get_status_name($i);
+            $opts[$url->out(false)] = $display;
+            if ($i == $current) {
+                $selected = $url->out(false);
+            }
+        }
+        $newurl = $sel->url($sel->maxstatusset + 1);
+        $opts[$newurl->out(false)] = get_string('newstatusset', 'mod_attendance');
+        if ($current == $sel->maxstatusset + 1) {
+            $selected = $newurl->out(false);
+        }
+
+        return $this->output->url_select($opts, $selected, null);
+    }
+
     protected function render_attendance_preferences_data(attendance_preferences_data $prefdata) {
         $this->page->requires->js('/mod/attendance/module.js');
 
index 0faaeb2..2b93633 100644 (file)
@@ -57,7 +57,7 @@ $PAGE->set_cacheable(true);
 $PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance'));
 $PAGE->navbar->add($att->name);
 
-$formparams = array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context);
+$formparams = array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'att' => $att);
 switch ($att->pageparams->action) {
     case att_sessions_page_params::ACTION_ADD:
         $url = $att->url_sessions(array('action' => att_sessions_page_params::ACTION_ADD));
@@ -230,6 +230,7 @@ function construct_sessions_data_for_add($formdata) {
                     if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
                         $sess->studentscanmark = 1;
                     }
+                    $sess->statusset = $formdata->statusset;
 
                     fill_groupid($formdata, $sessions, $sess);
                 }
@@ -250,6 +251,7 @@ function construct_sessions_data_for_add($formdata) {
         if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
             $sess->studentscanmark = 1; 
         }
+        $sess->statusset = $formdata->statusset;
 
         fill_groupid($formdata, $sessions, $sess);
     }
index 6c03d2b..ffab425 100644 (file)
@@ -110,3 +110,53 @@ Feature: Test the various new features in the attendance module
     And "E" "text" should exist in the "Temporary user 1" "table_row"
     And "A" "text" should exist in the "Student 3" "table_row"
     And I should not see "Temporary user 2"
+
+  Scenario: A teacher can create and use multiple status lists
+    Given I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Test attendance"
+    And I follow "Settings"
+    And I set the field "jump" to "New set of statuses"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/input" to "G"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/input" to "Great"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[4]/input" to "3"
+    And I click on "Add" "button" in the ".lastrow" "css_element"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[2]/input" to "O"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/input" to "OK"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[4]/input" to "2"
+    And I click on "Add" "button" in the ".lastrow" "css_element"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[2]/input" to "B"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[3]/input" to "Bad"
+    And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[4]/input" to "0"
+    And I click on "Add" "button" in the ".lastrow" "css_element"
+    And I click on "Update" "button" in the "#preferencesform" "css_element"
+
+    And I follow "Add"
+    And I set the following fields to these values:
+      | Create multiple sessions | 0                      |
+      | Use status set           | Status set 1 (P L E A) |
+      | id_sessiondate_hour      | 10                     |
+      | id_sessiondate_minute    | 0                      |
+    And I click on "submitbutton" "button"
+    And I follow "Sessions"
+    And I follow "Add"
+    And I set the following fields to these values:
+      | Create multiple sessions | 0                    |
+      | Use status set           | Status set 2 (G O B) |
+      | id_sessiondate_hour      | 11                   |
+      | id_sessiondate_minute    | 0                    |
+    And I click on "submitbutton" "button"
+    And I follow "Sessions"
+
+    When I click on "Take attendance" "link" in the "10:00" "table_row"
+    Then "Set status for all users to «Present»" "link" should exist
+    And "Set status for all users to «Late»" "link" should exist
+    And "Set status for all users to «Excused»" "link" should exist
+    And "Set status for all users to «Absent»" "link" should exist
+
+    When I follow "Sessions"
+    And I click on "Take attendance" "link" in the "11:00" "table_row"
+    Then "Set status for all users to «Great»" "link" should exist
+    And "Set status for all users to «OK»" "link" should exist
+    And "Set status for all users to «Bad»" "link" should exist
+
index 980c9ce..2b03195 100644 (file)
@@ -75,6 +75,12 @@ class mod_attendance_update_form extends moodleform {
         $durselect[] =& $mform->createElement('select', 'minutes', '', $minutes, false, true);
         $mform->addGroup($durselect, 'durtime', get_string('duration', 'attendance'), array(' '), true);
 
+        // Show which status set is in use.
+        $maxstatusset = attendance_get_max_statusset($this->_customdata['att']->id);
+        if ($maxstatusset > 0) {
+            $mform->addElement('static', 'statusset', get_string('usestatusset', 'mod_attendance'),
+                               att_get_setname($this->_customdata['att']->id, $sess->statusset));
+        }
         $mform->addElement('editor', 'sdescription', get_string('description', 'attendance'), null, $defopts);
         $mform->setType('sdescription', PARAM_RAW);
 
index 9f2dff3..7872ebf 100644 (file)
@@ -22,7 +22,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$plugin->version  = 2015040501;
+$plugin->version  = 2015040502;
 $plugin->requires = 2014042900;
 $plugin->release = '2.9.1';
 $plugin->maturity  = MATURITY_STABLE;