Browse Source

UoN Changes to allow students to mark their own attendance on sessions that have been set to allow it.

Adds a Report that lists only students who do not have full attendance.

Adds the Ability to send e-mails to students.
MOODLE_26_STABLE
Neill Magill 11 years ago
parent
commit
5dc2534a87
  1. 4
      add_form.php
  2. 84
      attendance.php
  3. 1
      db/install.xml
  4. 12
      db/upgrade.php
  5. 8
      lang/en/attendance.php
  6. 124
      locallib.php
  7. 38
      renderables.php
  8. 51
      renderer.php
  9. 2
      report.php
  10. 6
      sessions.php
  11. 63
      student_attenance_form.php
  12. 2
      version.php
  13. 2
      view.php

4
add_form.php

@ -103,6 +103,10 @@ class mod_attendance_add_form extends moodleform {
$mform->addElement('checkbox', 'addmultiply', '', get_string('createmultiplesessions', 'attendance'));
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
// Studetns can mark own attendance.
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark','attforblock'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attforblock');
$mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
for ($i=0; $i<=23; $i++) {

84
attendance.php

@ -0,0 +1,84 @@
<?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/>.
/**
* Prints attendance info for particular user
*
* @package mod
* @subpackage attforblock
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__).'/../../config.php');
require_once(dirname(__FILE__).'/locallib.php');
require_once(dirname(__FILE__).'/student_attenance_form.php');
$pageparams = new att_sessions_page_params();
// Check that the required parameters are present.
$id = required_param('sessid', PARAM_INT);
$attendance_session_id = required_param('sessid', PARAM_INT);
$attforsession = $DB->get_record('attendance_sessions', array('id' => $id), '*', MUST_EXIST);
$attforblock = $DB->get_record('attforblock', array('id' => $attforsession->attendanceid), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('attforblock', $attforblock->id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
// Require the user is logged in.
require_login($course, true, $cm);
$pageparams->sessionid = $id;
$att = new attforblock($attforblock, $cm, $course, $PAGE->context, $pageparams);
// Require that a session key is passed to this page.
require_sesskey();
// Create the form.
$mform = new mod_attforblock_student_attendance_form(null,
array('course' => $course, 'cm' => $cm, 'modcontext' => $PAGE->context, 'session' => $attforsession, 'attendance' => $att));
if ($mform->is_cancelled()) {
// The user cancelled the form, so redirect them to the view page.
$url = new moodle_url('/mod/attforblock/view.php', array('id' => $cm->id));
redirect($url);
} else if ($fromform = $mform->get_data()) {
if (!empty($fromform->status)) {
$success = $att->take_from_student($fromform);
$url = new moodle_url('/mod/attforblock/view.php', array('id' => $cm->id));
if ($success) {
// Redirect back to the view page for the block.
redirect($url);
} else {
print_error ('attendance_already_submitted', 'mod_attforblock', $url);
}
}
// The form did not validate correctly so we will set it to display the data they submitted.
$mform->set_data($fromform);
}
$PAGE->set_url($att->url_sessions());
$PAGE->set_title($course->shortname. ": ".$att->name);
$PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(true);
$PAGE->navbar->add($att->name);
$output = $PAGE->get_renderer('mod_attforblock');
echo $output->header();
$mform->display();
echo $output->footer();

1
db/install.xml

@ -30,6 +30,7 @@
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<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"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for attendance_sessions"/>

12
db/upgrade.php

@ -32,7 +32,17 @@ function xmldb_attendance_upgrade($oldversion=0) {
global $CFG, $THEME, $DB;
$dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes.
$result = true;
if ($oldversion < 2013082901) {
$table = new xmldb_table('attendance_sessions');
$field = new xmldb_field('studentscanmark');
$field->set_attributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
upgrade_mod_savepoint(true, 2013082901, 'attendance');
}
// UPGRADES from attforblock are only supported for sites that are running attforblock version 2012120700.
return $result;

8
lang/en/attendance.php

@ -208,3 +208,11 @@ $string['viewmode'] = 'View mode';
$string['week'] = 'week(s)';
$string['weeks'] = 'Weeks';
$string['youcantdo'] = 'You can\'t do anything';
// New strings.
$string['studentscanmark'] = 'Allow students to record own attendance';
$string['studentscanmark_help'] = 'If checked students will be able to change their own attendance status for the session.';
$string['set_by_student'] = 'Self-recorded';
$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';
$string['lowgrade'] = 'Low grade';
$string['submitattendance'] = 'Submit attendance';
$string['attendancenotset'] = 'You must set your attendance';

124
locallib.php

@ -33,6 +33,7 @@ define('ATT_VIEW_WEEKS', 2);
define('ATT_VIEW_MONTHS', 3);
define('ATT_VIEW_ALLPAST', 4);
define('ATT_VIEW_ALL', 5);
define('ATT_VIEW_NOTPRESENT', 6);
define('ATT_SORT_LASTNAME', 1);
define('ATT_SORT_FIRSTNAME', 2);
@ -702,6 +703,8 @@ class attendance {
if ($this->pageparams->startdate && $this->pageparams->enddate) {
$where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate >= :sdate AND sessdate < :edate";
} else if ($this->pageparams->enddate) {
$where = "attendanceid = :aid AND sessdate >= :csdate AND sessdate < :edate";
} else {
$where = "attendanceid = :aid AND sessdate >= :csdate";
}
@ -834,6 +837,57 @@ class attendance {
add_to_log($this->course->id, 'attendance', 'session updated', $url, $info, $this->cm->id);
}
/**
* Used to record attendance submitted by the student.
*
* @global type $DB
* @global type $USER
* @param type $mformdata
* @return boolean
*/
public function take_from_student($mformdata) {
global $DB, $USER;
$statuses = implode(',', array_keys( (array)$this->get_statuses() ));
$now = time();
$record = new stdClass();
$record->studentid = $USER->id;
$record->statusid = $mformdata->status;
$record->statusset = $statuses;
$record->remarks = get_string('set_by_student', 'mod_attforblock');
$record->sessionid = $mformdata->sessid;
$record->timetaken = $now;
$record->takenby = $USER->id;
$dbsesslog = $this->get_session_log($mformdata->sessid);
if (array_key_exists($record->studentid, $dbsesslog)) {
// Already recorded do not save.
return false;
}
else {
$DB->insert_record('attendance_log', $record, false);
}
// Update the session to show that a register has been taken, or staff may overwrite records.
$rec = new object();
$rec->id = $mformdata->sessid;
$rec->lasttaken = $now;
$rec->lasttakenby = $USER->id;
$DB->update_record('attendance_sessions', $rec);
// Update the users grade.
$this->update_users_grade(array($USER->id));
// Log the change.
$params = array(
'sessionid' => $mformdata->sessid);
$url = $this->url_take($params);
$this->log('attendance taked', $url, $USER->firstname.' '.$USER->lastname);
return true;
}
public function take_from_form_data($formdata) {
global $DB, $USER;
// TODO: WARNING - $formdata is unclean - comes from direct $_POST - ideally needs a rewrite but we do some cleaning below.
@ -1016,31 +1070,65 @@ class attendance {
return $this->usertakensesscount[$userid];
}
public function get_user_statuses_stat($userid) {
/**
*
* @global type $DB
* @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;
if (!array_key_exists($userid, $this->userstatusesstat)) {
$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' => $this->id,
'cstartdate' => $this->course->startdate,
'uid' => $userid);
// Need to start setting the parameters here for the filters to work.
$params = array(
'aid' => $this->id,
'cstartdate' => $this->course->startdate,
'uid' => $userid);
$processed_filters = array();
// We test for any valid filters sent.
if (isset($filters['enddate'])) {
$processed_filters[] = 'ats.sessdate <= :enddate';
$params['enddate'] = $filters['enddate'];
}
// Make the filter array into a SQL string.
if (!empty($processed_filters)) {
$processed_filters = 'AND '.implode(' AND ', $processed_filters);
} else {
$processed_filters = '';
}
$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
$processed_filters
GROUP BY al.statusid";
if ($filters !== null) { // We do not want to cache, or use a cached version of the results when a filter is set.
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];
}
public function get_user_grade($userid) {
return att_get_user_grade($this->get_user_statuses_stat($userid), $this->get_statuses());
/**
*
* @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 att_get_user_grade($this->get_user_statuses_stat($userid, $filters), $this->get_statuses());
}
// For getting sessions count implemented simplest method - taken sessions.
@ -1115,13 +1203,13 @@ class attendance {
$where2 = "ats.attendanceid = :aid2 AND ats.sessdate >= :csdate2 AND ats.groupid $gsql";
}
$sql = "SELECT ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks
$sql = "SELECT ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks, ats.studentscanmark
FROM {attendance_sessions} ats
RIGHT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
WHERE $where
UNION
SELECT ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks
SELECT ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks, ats.studentscanmark
FROM {attendance_sessions} ats
LEFT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid2

38
renderables.php

@ -109,19 +109,23 @@ class attendance_filter_controls implements renderable {
public $prevcur;
public $nextcur;
public $curdatetxt;
public $reportcontrol;
private $urlpath;
private $urlparams;
private $att;
public function __construct(attendance $att) {
public function __construct(attendance $att, $report = false) {
global $PAGE;
$this->pageparams = $att->pageparams;
$this->cm = $att->cm;
// This is a report control only if $reports is true and the attendance block can be graded.
$this->reportcontrol = $report && ($att->grade > 0);
$this->curdate = $att->pageparams->curdate;
$date = usergetdate($att->pageparams->curdate);
@ -456,6 +460,12 @@ class attendance_report_data implements renderable {
global $CFG;
$this->perm = $att->perm;
$currenttime = time();
if ($att->pageparams->view == ATT_VIEW_NOTPRESENT) {
$att->pageparams->enddate = $currenttime;
}
$this->pageparams = $att->pageparams;
$this->users = $att->get_users($att->pageparams->group);
@ -473,16 +483,28 @@ class attendance_report_data implements renderable {
$this->decimalpoints = $CFG->grade_decimalpoints;
}
foreach ($this->users as $user) {
$this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id);
$maxgrade = att_get_user_max_grade(count($this->sessions), $this->statuses);
$this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id);
foreach ($this->users as $key => $user) {
$grade = 0;
if ($this->gradable) {
$grade = $att->get_user_grade($user->id, array('enddate' => $currenttime));
$totalgrade = $att->get_user_grade($user->id);
}
$this->usersstats[$user->id] = $att->get_user_statuses_stat($user->id);
if ($att->pageparams->view != ATT_VIEW_NOTPRESENT || $grade < $maxgrade) {
$this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id);
if ($this->gradable) {
$this->grades[$user->id] = $att->get_user_grade($user->id);
$this->maxgrades[$user->id] = $att->get_user_max_grade($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 {
unset($this->users[$key]);
}
}

51
renderer.php

@ -140,6 +140,9 @@ class mod_attendance_renderer extends plugin_renderer_base {
protected function render_view_controls(attendance_filter_controls $fcontrols) {
$views[ATT_VIEW_ALL] = get_string('all', 'attendance');
$views[ATT_VIEW_ALLPAST] = get_string('allpast', 'attendance');
if ($fcontrols->reportcontrol) {
$views[ATT_VIEW_NOTPRESENT] = get_string('lowgrade', 'attforblock');
}
$views[ATT_VIEW_MONTHS] = get_string('months', 'attendance');
$views[ATT_VIEW_WEEKS] = get_string('weeks', 'attendance');
$views[ATT_VIEW_DAYS] = get_string('days', 'attendance');
@ -644,8 +647,17 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cell->colspan = 2;
$row->cells[] = $cell;
} else {
$row->cells[] = '?';
$row->cells[] = '';
if (!empty($sess->studentscanmark)) { // Student can mark their own attendance.
// URL to the page that lets the student modify their attendance.
$url = new moodle_url('/mod/attforblock/attendance.php',
array('sessid' => $sess->id, 'sesskey' => sesskey()));
$cell = new html_table_cell(html_writer::link($url, get_string('submitattendance', 'attforblock')));
$cell->colspan = 2;
$row->cells[] = $cell;
} else { // Student cannot mark their own attendace.
$row->cells[] = '?';
$row->cells[] = '';
}
}
$table->data[] = $row;
@ -661,6 +673,14 @@ class mod_attendance_renderer extends plugin_renderer_base {
}
protected function render_attendance_report_data(attendance_report_data $reportdata) {
global $PAGE;
// Initilise Javascript used to (un)check all checkboxes.
$this->page->requires->js_init_call('M.mod_attforblock.init_manage');
// Check if the user should be able to bulk send messages to other users on the course.
$bulkmessagecapability = has_capability('moodle/course:bulkmessaging', $PAGE->context);
$table = new html_table();
$table->attributes['class'] = 'generaltable attwidth';
@ -701,6 +721,13 @@ class mod_attendance_renderer extends plugin_renderer_base {
$table->size[] = '1px';
}
if ($bulkmessagecapability) { // Display the table header for bulk messaging.
// The checkbox must have an id of cb_selector so that the JavaScript will pick it up.
$table->head[] = html_writer::checkbox('cb_selector', 0, false, '', array('id' => 'cb_selector'));
$table->align[] = 'center';
$table->size[] = '1px';
}
foreach ($reportdata->users as $user) {
$row = new html_table_row();
@ -722,10 +749,28 @@ class mod_attendance_renderer extends plugin_renderer_base {
$row->cells[] = $reportdata->grades[$user->id].' / '.$reportdata->maxgrades[$user->id];
}
if ($bulkmessagecapability) { // Create the checkbox for bulk messaging.
$row->cells[] = html_writer::checkbox('user'.$user->id, 'on', false);
}
$table->data[] = $row;
}
return html_writer::table($table);
if ($bulkmessagecapability) { // Require that the user can bulk message users.
// Display check boxes that will allow the user to send a message to the students that have been checked.
$output = html_writer::empty_tag('input', array('name' => 'sesskey', 'type' => 'hidden', 'value' => sesskey()));
$output .= html_writer::empty_tag('input', array('name' => 'formaction', 'type' => 'hidden', 'value' => 'messageselect.php'));
$output .= html_writer::empty_tag('input', array('name' => 'id', 'type' => 'hidden', 'value' => $GLOBALS['COURSE']->id));
$output .= html_writer::empty_tag('input', array('name' => 'returnto', 'type' => 'hidden', 'value' => s(me())));
$output .= html_writer::table($table);
$output .= html_writer::tag('div',
html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('messageselectadd'))),
array('class' => 'buttons'));
$url = new moodle_url('/user/action_redir.php');
return html_writer::tag('form', $output, array('action' => $url->out(), 'method' => 'post'));
} else {
return html_writer::table($table);
}
}
protected function render_attendance_preferences_data(attendance_preferences_data $prefdata) {

2
report.php

@ -56,7 +56,7 @@ $PAGE->navbar->add(get_string('report', 'attendance'));
$output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_REPORT);
$filtercontrols = new attendance_filter_controls($att);
$reportdata = new attendance_report_data($att);
$reportdata = new attendance_report_data($att, true);
add_to_log($course->id, 'attendance', 'report viewed', '/mod/attendance/report.php?id='.$id, '', $cm->id);

6
sessions.php

@ -216,6 +216,9 @@ function construct_sessions_data_for_add($formdata) {
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
}
fill_groupid($formdata, $sessions, $sess);
}
@ -233,6 +236,9 @@ function construct_sessions_data_for_add($formdata) {
$sess->description = $formdata->sdescription['text'];
$sess->descriptionformat = $formdata->sdescription['format'];
$sess->timemodified = $now;
if (isset($formdata->studentscanmark)) { // Students will be able to mark their own attendance.
$sess->studentscanmark = 1;
}
fill_groupid($formdata, $sessions, $sess);
}

63
student_attenance_form.php

@ -0,0 +1,63 @@
<?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/>.
require_once($CFG->libdir.'/formslib.php');
class mod_attforblock_student_attendance_form extends moodleform {
public function definition() {
global $CFG, $USER;
$mform =& $this->_form;
$course = $this->_customdata['course'];
$cm = $this->_customdata['cm'];
$modcontext = $this->_customdata['modcontext'];
$attforsession = $this->_customdata['session'];
$attblock = $this->_customdata['attendance'];
$statuses = $attblock->get_statuses();
$mform->addElement('hidden', 'sessid', null);
$mform->setType('sessid', PARAM_INT);
$mform->setConstant('sessid', $attforsession->id);
$mform->addElement('hidden', 'sesskey', null);
$mform->setType('sesskey', PARAM_INT);
$mform->setConstant('sesskey', sesskey());
// Set a title as the date and time of the session.
$sesstiontitle = userdate($attforsession->sessdate, get_string('strftimedate')).' '
.userdate($attforsession->sessdate, get_string('strftimehm', 'mod_attforblock'));
$mform->addElement('header', 'session', $sesstiontitle);
// If a session description is set display it.
if (!empty($attforsession->description)) {
$mform->addElement('html', $attforsession->description);
}
// Create radio buttons for setting the attendance status.
$radioarray = array();
foreach ($statuses as $status) {
$radioarray[] =& $mform->createElement('radio', 'status', '', $status->description, $status->id, array());
}
// Add the radio buttons as a control with the user's name in front.
$mform->addGroup($radioarray, 'statusarray', $USER->firstname.' '.$USER->lastname.':', array(''), false);
$mform->addRule('statusarray', get_string('attendancenotset', 'attforblock'), 'required', '', 'client', false, false);
$this->add_action_buttons();
}
}

2
version.php

@ -22,7 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$module->version = 2013082900;
$module->version = 2013082901;
$module->requires = 2013040500;
$module->release = '2.5.1';
$module->maturity = MATURITY_STABLE;

2
view.php

@ -63,7 +63,7 @@ $PAGE->navbar->add(get_string('attendancereport', 'attendance'));
$output = $PAGE->get_renderer('mod_attendance');
$userid = isset($pageparams->studentid) ? $pageparams->studentid : $USER->id;
$userid = (isset($pageparams->studentid) && ($att->perm->can_manage() || $att->perm->can_take() || $att->perm->can_change())) ? $pageparams->studentid : $USER->id;
$userdata = new attendance_user_data($att, $userid);
echo $output->header();

Loading…
Cancel
Save