From 4302b218402dbeeada060195b7634af572d14ea3 Mon Sep 17 00:00:00 2001 From: Davo Smith Date: Thu, 25 Jun 2015 11:20:53 +0100 Subject: [PATCH] Allow an attendance instance to have multiple status sets The set to use can be selected per session --- add_form.php | 13 +++++ db/install.xml | 2 + db/upgrade.php | 24 +++++++++ lang/en/attendance.php | 3 ++ lib.php | 12 +++++ locallib.php | 79 ++++++++++++++++++++++++++---- preferences.php | 12 +++++ renderables.php | 36 ++++++++++++-- renderer.php | 27 ++++++++++ sessions.php | 4 +- tests/behat/extra_features.feature | 50 +++++++++++++++++++ update_form.php | 6 +++ version.php | 2 +- 13 files changed, 256 insertions(+), 14 deletions(-) diff --git a/add_form.php b/add_form.php index 9786f40..f216caa 100644 --- a/add_form.php +++ b/add_form.php @@ -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); diff --git a/db/install.xml b/db/install.xml index 65e43d5..b1fc454 100644 --- a/db/install.xml +++ b/db/install.xml @@ -31,6 +31,7 @@ + @@ -70,6 +71,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 8ec0802..75bf87e 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -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; } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 5ac7897..6bf3481 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -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 --- 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; +} diff --git a/locallib.php b/locallib.php index 09c8b89..122159b 100644 --- a/locallib.php +++ b/locallib.php @@ -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); diff --git a/preferences.php b/preferences.php index ce30721..210734e 100644 --- a/preferences.php +++ b/preferences.php @@ -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(); diff --git a/renderables.php b/renderables.php index ac9b37e..047a461 100644 --- a/renderables.php +++ b/renderables.php @@ -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); diff --git a/renderer.php b/renderer.php index 46b4617..2aad1e8 100644 --- a/renderer.php +++ b/renderer.php @@ -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'); diff --git a/sessions.php b/sessions.php index 0faaeb2..2b93633 100644 --- a/sessions.php +++ b/sessions.php @@ -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); } diff --git a/tests/behat/extra_features.feature b/tests/behat/extra_features.feature index 6c03d2b..ffab425 100644 --- a/tests/behat/extra_features.feature +++ b/tests/behat/extra_features.feature @@ -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 + diff --git a/update_form.php b/update_form.php index 980c9ce..2b03195 100644 --- a/update_form.php +++ b/update_form.php @@ -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); diff --git a/version.php b/version.php index 9f2dff3..7872ebf 100644 --- a/version.php +++ b/version.php @@ -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;