$mform->addElement('checkbox', 'addmultiply', '', get_string('createmultiplesessions', 'attendance'));
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
+ // Students can mark own attendance.
+ $mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark','attendance'));
+ $mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
+
$mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance'));
for ($i=0; $i<=23; $i++) {
--- /dev/null
+<?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 attendance
+ * @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);
+$attendance = $DB->get_record('attendance', array('id' => $attforsession->attendanceid), '*', MUST_EXIST);
+$cm = get_coursemodule_from_instance('attendance', $attendance->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 attendance($attendance, $cm, $course, $PAGE->context, $pageparams);
+
+// Require that a session key is passed to this page.
+require_sesskey();
+
+// Create the form.
+$mform = new mod_attendance_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/attendance/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/attendance/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_attendance', $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_attendance');
+echo $output->header();
+$mform->display();
+echo $output->footer();
// Link to attendance view by moduleid.
$search = "/(" . $base . "\/mod\/attendance\/view.php\?id\=)([0-9]+)/";
- $content= preg_replace($search, '$@ATTFORBLOCKVIEWBYID*$2@$', $content);
+ $content= preg_replace($search, '$@ATTENDANCEVIEWBYID*$2@$', $content);
// Link to attendance view by moduleid and studentid.
$search = "/(" . $base . "\/mod\/attendance\/view.php\?id\=)([0-9]+)\&studentid\=([0-9]+)/";
- $content= preg_replace($search, '$@ATTFORBLOCKVIEWBYIDSTUD*$2*$3@$', $content);
+ $content= preg_replace($search, '$@ATTENDANCEVIEWBYIDSTUD*$2*$3@$', $content);
return $content;
}
static public function define_decode_rules() {
$rules = array();
- $rules[] = new restore_decode_rule('ATTFORBLOCKVIEWBYID',
+ $rules[] = new restore_decode_rule('ATTENDANCEVIEWBYID',
'/mod/attendance/view.php?id=$1', 'course_module');
- $rules[] = new restore_decode_rule('ATTFORBLOCKVIEWBYIDSTUD',
+ $rules[] = new restore_decode_rule('ATTENDANCEVIEWBYIDSTUD',
'/mod/attendance/view.php?id=$1&studentid=$2', array('course_module', 'user'));
return $rules;
<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"/>
$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($result, 2013082901, 'attendance');
+ }
+
if ($oldversion < 2013082902) {
// Replace values that reference old module "attforblock" to "attendance".
$sql = "UPDATE {grade_items}
if (isset($formdata->ident['uname'])) {
$data->tabhead[] = get_string('username');
}
+
+ $optional = array('idnumber', 'institution', 'department');
+ foreach ($optional as $opt) {
+ if (isset($formdata->ident[$opt])) {
+ $data->tabhead[] = get_string($opt);
+ }
+ }
+
$data->tabhead[] = get_string('lastname');
$data->tabhead[] = get_string('firstname');
$groupmode = groups_get_activity_groupmode($cm, $course);
$data->tabhead[] = get_string('groups');
}
-
if (count($reportdata->sessions) > 0) {
foreach ($reportdata->sessions as $sess) {
$text = userdate($sess->sessdate, get_string('strftimedmyhm', 'attendance'));
if (isset($formdata->ident['uname'])) {
$data->table[$i][] = $user->username;
}
+
+ $optional_row = array('idnumber', 'institution', 'department');
+ foreach ($optional_row as $opt) {
+ if (isset($formdata->ident[$opt])) {
+ $data->table[$i][] = $user->$opt;
+ }
+ }
+
$data->table[$i][] = $user->lastname;
$data->table[$i][] = $user->firstname;
if (!empty($groupmode)) {
$ident = array();
$ident[] =& $mform->createElement('checkbox', 'id', '', get_string('studentid', 'attendance'));
-
$ident[] =& $mform->createElement('checkbox', 'uname', '', get_string('username'));
+
+ $optional = array('idnumber', 'institution', 'department');
+ foreach ($optional as $opt) {
+ $ident[] =& $mform->createElement('checkbox', $opt, '', get_string($opt));
+ $mform->setType($opt, PARAM_NOTAGS);
+ }
+
$mform->addGroup($ident, 'ident', get_string('identifyby', 'attendance'), array('<br />'), true);
$mform->setDefaults(array('ident[id]' => true, 'ident[uname]' => true));
$mform->setType('id', PARAM_INT);
$string['eventdurationupdated'] = 'Session duration updated';
$string['eventstatusupdated'] = 'Status updated';
$string['eventstatusadded'] = 'Status added';
+
+$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';
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);
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";
}
$event->trigger();
}
+ /**
+ * 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_attendance');
+ $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));
+
+ /* create url for link in log screen
+ * need to set grouptype to 0 to allow take attendance page to be called
+ * from report/log page */
+
+ $params = array(
+ 'sessionid' => $this->pageparams->sessionid,
+ 'grouptype' => 0);
+
+ // Log the change.
+ $event = \mod_attendance\event\attendance_taken::create(array(
+ 'objectid' => $this->id,
+ 'context' => $this->context,
+ 'other' => $params));
+ $event->add_record_snapshot('course_modules', $this->cm);
+ $event->add_record_snapshot('attendance', $this);
+ $event->trigger();
+
+ 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.
$this->update_users_grade(array_keys($sesslog));
}
+ // create url for link in log screen
$params = array(
'sessionid' => $this->pageparams->sessionid,
'grouptype' => $this->pageparams->grouptype);
global $DB, $CFG;
// Fields we need from the user table.
- $userfields = user_picture::fields('u', array('username'));
+ $userfields = user_picture::fields('u', array('username' , 'idnumber' , 'institution' , 'department'));
if (isset($this->pageparams->sort) and ($this->pageparams->sort == ATT_SORT_FIRSTNAME)) {
- $orderby = "u.firstname ASC, u.lastname ASC";
+ $orderby = "u.firstname ASC, u.lastname ASC, u.idnumber ASC, u.institution ASC, u.department ASC";
} else {
- $orderby = "u.lastname ASC, u.firstname ASC";
+ $orderby = "u.lastname ASC, u.firstname ASC, u.idnumber ASC, u.institution ASC, u.department ASC";
}
if ($page) {
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;
$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 = '';
+ }
+
+
$period = '';
if (!empty($this->pageparams->startdate) && !empty($this->pageparams->enddate)) {
$period = ' AND ats.sessdate >= :sdate AND ats.sessdate < :edate ';
$params['edate'] = $this->pageparams->enddate;
}
- if (!array_key_exists($userid, $this->userstatusesstat)) {
- if ($this->get_group_mode()) {
- $qry = "SELECT al.statusid, count(al.statusid) AS stcnt
- FROM {attendance_log} al
- JOIN {attendance_sessions} ats ON al.sessionid = ats.id
- LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
- WHERE ats.attendanceid = :aid AND
- ats.sessdate >= :cstartdate AND
- al.studentid = :uid AND
- (ats.groupid = 0 or gm.id is NOT NULL)".$period."
- GROUP BY al.statusid";
- } else {
- $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".$period."
- GROUP BY al.statusid";
-
- }
-
-
+ if ($this->get_group_mode()) {
+ $qry = "SELECT al.statusid, count(al.statusid) AS stcnt
+ FROM {attendance_log} al
+ JOIN {attendance_sessions} ats ON al.sessionid = ats.id
+ LEFT JOIN {groups_members} gm ON gm.userid = al.studentid AND gm.groupid = ats.groupid
+ WHERE ats.attendanceid = :aid AND
+ ats.sessdate >= :cstartdate AND
+ al.studentid = :uid AND
+ (ats.groupid = 0 or gm.id is NOT NULL)".$period.$processed_filters."
+ GROUP BY al.statusid";
+ } else {
+ $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".$period.$processed_filters."
+ GROUP BY al.statusid";
+ }
+
+ // We do not want to cache, or use a cached version of the results when a filter is set.
+ if ($filters !== null) {
+ 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.
// It would be better as a UNION query butunfortunatly MS SQL does not seem to support doing a DISTINCT on a the description field.
$id = $DB->sql_concat(':value', 'ats.id');
if ($this->get_group_mode()) {
- $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks
+ $sql = "SELECT $id, 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 AND (ats.groupid = 0 or gm.id is NOT NULL)
ORDER BY ats.sessdate ASC";
} else {
- $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks
+ $sql = "SELECT $id, 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 = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql";
}
- $sql = "SELECT $id, ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks
+ $sql = "SELECT $id, 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 = :uid
$event->add_record_snapshot('attendance', $this);
$event->trigger();
}
+
}
-
function att_get_statuses($attid, $onlyvisible=true) {
global $DB;
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);
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, $att->pageparams->page);
$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]);
}
}
}
$totalusers = count_enrolled_users(context_module::instance($fcontrols->cm->id), 'mod/attendance:canbelisted', $group);
- $usersperpage = $fcontrols->pageparams->perpage;
- if (empty($fcontrols->pageparams->page) || !$fcontrols->pageparams->page || !$totalusers || !$usersperpage) {
+
+ if (empty($fcontrols->pageparams->page) || !$fcontrols->pageparams->page || !$totalusers || empty($fcontrols->pageparams->perpage)) {
return $paging_controls;
}
- $numberofpages = ceil($totalusers / $usersperpage);
+ $numberofpages = ceil($totalusers / $fcontrols->pageparams->perpage);
if ($fcontrols->pageparams->page > 1) {
$paging_controls .= html_writer::link($fcontrols->url(array('curdate' => $fcontrols->nextcur, 'page' => $fcontrols->pageparams->page - 1)),
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', 'attendance');
+ }
$views[ATT_VIEW_MONTHS] = get_string('months', 'attendance');
$views[ATT_VIEW_WEEKS] = get_string('weeks', 'attendance');
$views[ATT_VIEW_DAYS] = get_string('days', 'attendance');
$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/attendance/attendance.php',
+ array('sessid' => $sess->id, 'sesskey' => sesskey()));
+ $cell = new html_table_cell(html_writer::link($url, get_string('submitattendance', 'attendance')));
+ $cell->colspan = 2;
+ $row->cells[] = $cell;
+ } else { // Student cannot mark their own attendace.
+ $row->cells[] = '?';
+ $row->cells[] = '';
+ }
}
$table->data[] = $row;
}
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_attendance.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';
$table->align[] = 'center';
$table->size[] = '1px';
}
-
+
if ($reportdata->sessionslog) {
$table->head[] = get_string('remarks', 'attendance');
$table->align[] = 'center';
$table->size[] = '200px';
}
-
+
+ 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();
$row->cells[] = '';
}
}
+
+ if ($bulkmessagecapability) { // Create the checkbox for bulk messaging.
+ $row->cells[] = html_writer::checkbox('user'.$user->id, 'on', false);
+ }
+
$table->data[] = $row;
}
foreach ($reportdata->sessions as $sess) {
foreach ($reportdata->users as $user) {
foreach($reportdata->statuses as $status) {
- if ($reportdata->sessionslog[$user->id][$sess->id]->statusid == $status->id) $sessionstats[$status->id]++;
+ if (!empty($reportdata->sessionslog[$user->id][$sess->id])) {
+ if ($reportdata->sessionslog[$user->id][$sess->id]->statusid == $status->id) $sessionstats[$status->id]++;
+ }
}
}
}
$table->data[] = $statrow;
-
- return html_writer::table($table).html_writer::tag('div', get_string('users').': '.count($reportdata->users));
+
+ 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).html_writer::tag('div', get_string('users').': '.count($reportdata->users));;
+ $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).html_writer::tag('div', get_string('users').': '.count($reportdata->users));
+ }
+
}
protected function render_attendance_preferences_data(attendance_preferences_data $prefdata) {
$output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_REPORT);
-$filtercontrols = new attendance_filter_controls($att);
+$filtercontrols = new attendance_filter_controls($att, true);
$reportdata = new attendance_report_data($att);
// Trigger a report viewed event.
$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);
}
$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);
}
--- /dev/null
+<?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_attendance_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_attendance'));
+
+ $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', 'attendance'), 'required', '', 'client', false, false);
+
+ $this->add_action_buttons();
+ }
+}
\ No newline at end of file
--- /dev/null
+@mod @uon @mod_attendance
+Feature: Teachers and Students can record session attendance
+ In order to record session attendance
+ As a student
+ I need to be able to mark my own attendance to a session
+ And as a teacher
+ I need to be able to mark any students attendance to a session
+ In order to report on session attendance
+ As a teacher
+ I need to be able to export session attendance and run reports
+ In order to contact students with poor attendance
+ As a teacher
+ I need the ability to message a group of students with low attendance
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | summary | category |
+ | Course 1 | C101 | Prove the attendance activity works | 0 |
+ And the following "users" exist:
+ | username | firstname | lastname | email | idnumber | department | institution |
+ | student1 | Sam | Student | student1@asd.com | 1234 | computer science | University of Nottingham |
+ | teacher1 | Teacher | One | teacher1@asd.com | 5678 | computer science | University of Nottingham |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C101 | student |
+ | teacher1 | C101 | editingteacher |
+ And I log in as "teacher1"
+ And I follow "Course 1"
+ And I turn editing mode on
+ And I add a "Attendance" to section "1"
+ And I press "Save and display"
+ And I log out
+
+ Scenario: Students can mark their own attendance
+ When I log in as "teacher1"
+ And I follow "Course 1"
+ And I follow "Attendance"
+ And I follow "Add"
+ And I check "Allow students to record own attendance"
+ And I set the following fields to these values:
+ | id_sessiondate_hour | 23 |
+ And I click on "id_submitbutton" "button"
+ And I follow "Continue"
+ And I log out
+ When I log in as "student1"
+ And I follow "Course 1"
+ And I follow "Attendance"
+ And I follow "Submit attendance"
+ And I check "Present"
+ And I press "Save changes"
+ Then I should see "Self-recorded"
+ And I log out
+ When I log in as "teacher1"
+ And I follow "Course 1"
+ And I expand "Reports" node
+ And I follow "Logs"
+ And I click on "Get these logs" "button"
+ Then "attendance taken by student" "link" should exist
+
+ Scenario: Teachers can view low grade report and send a message
+ When I log in as "teacher1"
+ And I follow "Course 1"
+ And I follow "Attendance"
+ And I follow "Add"
+ And I set the following fields to these values:
+ | id_sessiondate_hour | 01 |
+ And I click on "id_submitbutton" "button"
+ And I follow "Continue"
+ And I follow "Report"
+ And I follow "Low grade"
+ And I check "user3"
+ And I click on "Send a message" "button"
+ Then I should see "Message body"
+ And I should see "student1@asd.com"
+ And I expand "Reports" node
+ And I follow "Logs"
+ And I click on "Get these logs" "button"
+ Then "attendance report viewed" "link" should exist
+
+ # Dependency - selenium running with firefox profile with auto saving of txt files to $CFG->behat_download.
+ @javascript @_file_download
+ Scenario: Export report includes id number, department and institution
+ When I log in as "teacher1"
+ And I follow "Course 1"
+ And I follow "Attendance"
+ And I follow "Add"
+ And I set the following fields to these values:
+ | id_sessiondate_hour | 01 |
+ And I click on "id_submitbutton" "button"
+ And I follow "Continue"
+ And I follow "Export"
+ Then the "id_ident_idnumber" checkbox should not be checked
+ And the "id_ident_institution" checkbox should not be checked
+ And the "id_ident_department" checkbox should not be checked
+ And I check "id_ident_idnumber"
+ And I check "id_ident_institution"
+ And I check "id_ident_department"
+ And I set the following fields to these values:
+ | format | Download in text format |
+ And I click on "OK" "button"
+ Then attendance export file is ok
+ And I should see "ID number" as "1234" in the file
+ And I should see "Department" as "computer science" in the file
+ And I should see "Institution" as "University of Nottingham" in the file
+
--- /dev/null
+<?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/>.
+
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
+
+use Behat\Mink\Exception\ExpectationException as ExpectationException,
+ Behat\Behat\Exception\PendingException as PendingException;
+
+/**
+ * Attendance steps definitions.
+ *
+ * @package mod
+ * @subpackage attendance
+ * @category test
+ * @copyright 2014 University of Nottingham
+ * @author Joseph Baxter (joseph.baxter@nottingham.ac.uk)
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_mod_attendance extends behat_base {
+
+ protected $file_contents;
+
+ /**
+ * @Then /^attendance export file is ok$/
+ */
+ public function attendance_export_file_is_ok() {
+
+ global $CFG;
+
+ $check = true;
+
+ // Location selenium will download to.
+ $dir = $CFG->behat_download;
+ $files = scandir($dir, 1);
+ $filename = $files[0];
+ $file = fopen($dir . $filename, "r");
+
+ $count = 0;
+ $header = null;
+
+ // The file is tab seperated but not exactly a tsv.
+ while (($row = fgetcsv($file, 0, "\t")) !== FALSE) {
+
+ // Ignore unwanted information at the start of the file.
+ if ($count < 3) {
+ $count++;
+ continue;
+ }
+
+ if (!$header) {
+ $header = $row;
+ } else {
+ $this->file_contents = array_combine($header, $row);
+ }
+
+ $count++;
+ }
+
+ fclose($file);
+ unlink($dir . $filename);
+
+ // Check if data rows exist.
+ if ($count < 2) {
+ $check = false;
+ }
+
+ if ($check) {
+
+ return true;
+
+ } else {
+
+ throw new ExpectationException('Attendance export file not ok', $this->getSession());
+ }
+
+ }
+
+ /**
+ * @Given /^I should see "([^"]*)" as "([^"]*)" in the file$/
+ */
+ public function i_should_see_as_in_the_file($field, $value) {
+
+ foreach ($this->file_contents as $array_field => $array_value) {
+
+ if ($field == $array_field) {
+
+ if ($value == $array_value) {
+
+ return true;
+
+ } else {
+
+ throw new PendingException();
+
+ }
+ }
+ }
+ }
+
+}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$plugin->version = 2014050900;
+$plugin->version = 2014072100;
$plugin->requires = 2014042900;
$plugin->release = '2.7.0';
$plugin->maturity = MATURITY_STABLE;