Browse Source

Merge branch 'allsessionsreport' into main

MOODLE_38_STABLE
Dan Marsden 4 years ago
parent
commit
aae3499a1c
  1. 148
      .github/workflows/ci.yml
  2. 51
      .travis.yml
  3. 60
      classes/event/session_report_updated.php
  4. 16
      classes/event/session_report_viewed.php
  5. 2
      classes/structure.php
  6. 15
      classes/view_page_params.php
  7. 12
      lang/en/attendance.php
  8. 135
      locallib.php
  9. 162
      renderables.php
  10. 826
      renderer.php
  11. 24
      styles.css
  12. 79
      view.php

148
.github/workflows/ci.yml

@ -1,33 +1,11 @@
name: Runtests
name: Run all tests
# Run this workflow every time a new commit pushed to your repository
on: push
jobs:
selftest:
name: CI test (make validate)
runs-on: ubuntu-18.04
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Setup PHP 7.3
uses: shivammathur/setup-php@v2
with:
php-version: 7.3
- name: Initialise
run: make init
- name: Validate
run: make validate
citest:
name: CI test
needs: selftest
runs-on: ubuntu-18.04
setup:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:9.6
@ -36,74 +14,70 @@ jobs:
POSTGRES_HOST_AUTH_METHOD: 'trust'
# Health check to wait for postgres to start.
ports:
- 5432:5432
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
mariadb:
image: mariadb:10
env:
MYSQL_USER: 'root'
MYSQL_ALLOW_EMPTY_PASSWORD: "true"
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3
strategy:
fail-fast: false
matrix:
include:
- php: '7.4'
moodle-branch: 'master'
- php: '7.4'
moodle-branch: 'MOODLE_311_STABLE'
- php: '7.4'
moodle-branch: 'MOODLE_310_STABLE'
- php: '7.4'
moodle-branch: 'MOODLE_39_STABLE'
- php: '7.4'
php-versions: ['7.3', '7.4']
database: ['pgsql', 'mariadb']
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Check out repository code
uses: actions/checkout@v2
with:
# Clone in plugin subdir, so we can setup CI in default directory.
path: plugin
- name: Install node
uses: actions/setup-node@v1
with:
node-version: '14.15.0'
- name: Install node
uses: actions/setup-node@v1
with:
# TODO: Check if we can support .nvmrc
node-version: '14.15.0'
- name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: pgsql, zip, gd, xmlrpc, soap
coverage: none
- name: Setup PHP environment
uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, pgsql, mysqli
tools: phpunit
- name: Initialise moodle-plugin-ci
run: |
make init
cp -R tests/Fixture/moodle-local_travis ../moodle-local_travis
echo $(cd bin; pwd) >> $GITHUB_PATH
echo $(cd vendor/bin; pwd) >> $GITHUB_PATH
echo "TRAVIS_BUILD_DIR="$(cd ../moodle-local_travis; pwd) >> $GITHUB_ENV
# PHPUnit depends on en_AU.UTF-8 locale
sudo locale-gen en_AU.UTF-8
- name: Install moodle-plugin-ci
run: moodle-plugin-ci install -vvv
env:
DB: 'pgsql'
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
IGNORE_PATHS: 'ignore'
IGNORE_NAMES: 'ignore_name.php'
MUSTACHE_IGNORE_NAMES: 'broken.mustache'
- name: Deploy moodle-plugin-ci
run: |
composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
# Add dirs to $PATH
echo $(cd ci/bin; pwd) >> $GITHUB_PATH
echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH
# PHPUnit depends on en_AU.UTF-8 locale
sudo locale-gen en_AU.UTF-8
- name: Install moodle-plugin-ci
# Need explicit IP to stop mysql client fail on attempt to use unix socket.
run: moodle-plugin-ci install -vvv --plugin ./plugin --db-host=127.0.0.1
env:
DB: ${{ matrix.database }}
# TODO: Omitted MOODLE_BRANCH results in regex failure, investigate.
MOODLE_BRANCH: 'master'
- name: Run Integration tests
env:
MOODLE_BRANCH: ${{ matrix.moodle-branch }}
run: |
make test-phpunit
moodle-plugin-ci phplint
moodle-plugin-ci phpcpd
moodle-plugin-ci phpmd
moodle-plugin-ci codechecker
moodle-plugin-ci validate
moodle-plugin-ci savepoints
moodle-plugin-ci mustache
moodle-plugin-ci grunt || [ \
"$MOODLE_BRANCH" != 'master' -a \
"$MOODLE_BRANCH" != 'MOODLE_310_STABLE' -a \
"$MOODLE_BRANCH" != 'MOODLE_39_STABLE' ]
moodle-plugin-ci phpdoc
moodle-plugin-ci phpunit --coverage-text
moodle-plugin-ci behat --profile default
moodle-plugin-ci behat --profile chrome
- name: Run Integration tests
run: |
# Currently it stops if any command return non 0 exit status, needs a
# wrapper to collect exit statuses and list result and the end.
# For testing purposes at this stage, just assume each command succeeds.
moodle-plugin-ci phplint || true
moodle-plugin-ci phpcpd || true
moodle-plugin-ci phpmd || true
moodle-plugin-ci codechecker || true
moodle-plugin-ci validate || true
moodle-plugin-ci savepoints || true
moodle-plugin-ci mustache || true
moodle-plugin-ci grunt || true
moodle-plugin-ci phpdoc || true
moodle-plugin-ci phpunit || true
moodle-plugin-ci behat --profile chrome || true

51
.travis.yml

@ -1,51 +0,0 @@
language: php
addons:
postgresql: "9.6"
services:
- mysql
- postgresql
- docker
cache:
directories:
- $HOME/.composer/cache
- $HOME/.npm
php:
- 7.2
- 7.4
env:
global:
- MOODLE_BRANCH=master
- MUSTACHE_IGNORE_NAMES=mobile_teacher_form.mustache
matrix:
- DB=pgsql
- DB=mysqli
before_install:
- phpenv config-rm xdebug.ini
- nvm install 14.0.0
- nvm use 14.0.0
- cd ../..
- composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
- moodle-plugin-ci install
- docker run -d -p 127.0.0.1:4444:4444 --net=host --shm-size=2g -v $HOME/build/moodle:$HOME/build/moodle selenium/standalone-chrome:3
script:
- moodle-plugin-ci phplint
- moodle-plugin-ci phpcpd
- moodle-plugin-ci phpmd
- moodle-plugin-ci codechecker
- moodle-plugin-ci validate
- moodle-plugin-ci savepoints
- moodle-plugin-ci mustache
- moodle-plugin-ci grunt
- moodle-plugin-ci phpdoc
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat --profile chrome

60
classes/event/session_report_updated.php

@ -0,0 +1,60 @@
<?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/>.
/**
* This file contains an event for when a student's attendance report is viewed.
*
* @package mod_attendance
* @copyright 2014 onwards Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_attendance\event;
defined('MOODLE_INTERNAL') || die();
/**
* Event for when a student's attendance report is updated.
*
* @property-read array $other {
* Extra information about event properties.
*
* string studentid Id of student whose attendances were updated.
* string mode Mode of the report updated.
* }
* @package mod_attendance
* @copyright 2013 onwards Dan Marsden
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class session_report_updated extends \mod_attendance\event\session_report_viewed {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_TEACHING;
// Objecttable and objectid can't be meaningfully specified.
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventstudentattendancesessionsupdated', 'mod_attendance');
}
}

16
classes/event/session_report_viewed.php

@ -55,7 +55,7 @@ class session_report_viewed extends \core\event\base {
* @return string
*/
public function get_description() {
return 'User with id ' . $this->userid . ' viewed attendance sessions for student with id ' .
return 'User with id ' . $this->userid . ' ' . $this->action . ' attendance sessions for student with id ' .
$this->relateduserid;
}
@ -74,12 +74,16 @@ class session_report_viewed extends \core\event\base {
* @return \moodle_url
*/
public function get_url() {
// Mode is optional.
// Mode, groupby, sesscourses are optional.
$mode = empty($this->other['mode']) ? "" : $this->other['mode'];
$groupby = empty($this->other['groupby']) ? "" : $this->other['groupby'];
$sesscourses = empty($this->other['sesscourses']) ? "" : $this->other['sesscourses'];
return new \moodle_url('/mod/attendance/view.php', array('id' => $this->contextinstanceid,
'studentid' => $this->relateduserid,
'mode' => $mode,
'view' => $this->other['view'],
'groupby' => $groupby,
'sesscourses' => $sesscourses,
'curdate' => $this->other['curdate']));
}
@ -89,7 +93,7 @@ class session_report_viewed extends \core\event\base {
* @return array of parameters to be passed to legacy add_to_log() function.
*/
protected function get_legacy_logdata() {
return array($this->courseid, 'attendance', 'student sessions viewed', $this->get_url(),
return array($this->courseid, 'attendance', 'student sessions ' . $this->action, $this->get_url(),
'student id ' . $this->relateduserid, $this->contextinstanceid);
}
@ -119,17 +123,17 @@ class session_report_viewed extends \core\event\base {
*/
protected function validate_data() {
if (!isset($this->relateduserid)) {
throw new \coding_exception('The event mod_attendance\\event\\session_report_viewed must specify relateduserid.');
throw new \coding_exception('The event ' . $this->eventname . ' must specify relateduserid.');
}
// View params can be left out as defaults will be the same when log event is viewed as when
// it was stored.
// filter params are important, but stored in session so default effectively unknown,
// hence required here.
if (!isset($this->other['view'])) {
throw new \coding_exception('The event mod_attendance\\event\\session_report_viewed must specify view.');
throw new \coding_exception('The event ' . $this->eventname . ' must specify view.');
}
if (!isset($this->other['curdate'])) {
throw new \coding_exception('The event mod_attendance\\event\\session_report_viewed must specify curdate.');
throw new \coding_exception('The event ' . $this->eventname . ' must specify curdate.');
}
parent::validate_data();
}

2
classes/structure.php

@ -114,7 +114,7 @@ class mod_attendance_structure {
* @param stdClass $dbrecord Attandance instance data from {attendance} table
* @param stdClass $cm Course module record as returned by {@see get_coursemodule_from_id()}
* @param stdClass $course Course record from {course} table
* @param stdClass $context The context of the workshop instance
* @param stdClass $context The context of the attendance instance
* @param stdClass $pageparams
*/
public function __construct(stdClass $dbrecord, stdClass $cm, stdClass $course, stdClass $context=null, $pageparams=null) {

15
classes/view_page_params.php

@ -37,12 +37,21 @@ class mod_attendance_view_page_params extends mod_attendance_page_with_filter_co
/** All courses */
const MODE_ALL_COURSES = 1;
/** All sessions */
const MODE_ALL_SESSIONS = 2;
/** @var int */
public $studentid;
/** @var string */
public $mode;
/** @var string */
public $groupby;
/** @var string */
public $sesscourses;
/**
* mod_attendance_view_page_params constructor.
*/
@ -64,6 +73,12 @@ class mod_attendance_view_page_params extends mod_attendance_page_with_filter_co
if ($this->mode != self::MODE_THIS_COURSE) {
$params['mode'] = $this->mode;
}
if ($this->groupby != 'course') {
$params['groupby'] = $this->groupby;
}
if ($this->sesscourses != 'current') {
$params['sesscourses'] = $this->sesscourses;
}
return $params;
}

12
lang/en/attendance.php

@ -43,6 +43,7 @@ $string['all'] = 'All';
$string['allcourses'] = 'All courses';
$string['allpast'] = 'All past';
$string['allsessions'] = 'All sessions';
$string['allsessionstotals'] = 'Totals for selected sessions';
$string['attendance:addinstance'] = 'Add a new attendance activity';
$string['attendance:canbelisted'] = 'Appears in the roster';
$string['attendance:changeattendances'] = 'Changing Attendances';
@ -235,6 +236,7 @@ $string['eventsessionupdated'] = 'Session updated';
$string['eventstatusadded'] = 'Status added';
$string['eventstatusupdated'] = 'Status updated';
$string['eventstudentattendancesessionsviewed'] = 'Session report viewed';
$string['eventstudentattendancesessionsupdated'] = 'Session report updated';
$string['eventtaken'] = 'Attendance taken';
$string['eventtakenbystudent'] = 'Attendance taken by student';
$string['export'] = 'Export';
@ -250,6 +252,7 @@ $string['gridcolumns'] = 'Grid columns';
$string['group'] = 'Group';
$string['groups'] = 'Groups';
$string['groupsession'] = 'Group of students';
$string['groupsessionsby'] = 'Group sessions by';
$string['hiddensessions'] = 'Hidden sessions';
$string['hiddensessions_help'] = 'Sessions are hidden if they are scheduled before the course start date.
@ -332,6 +335,7 @@ $string['noabsentstatusset'] = 'The status set in use does not have a status to
$string['noattendanceusers'] = 'It is not possible to export any data as there are no students enrolled in the course.';
$string['noattforuser'] = 'No attendance records exist for the user';
$string['noautomark'] = 'Disabled';
$string['nocapabilitytotakethisattendance'] = 'You tried to change the attendance of a session with the cmid: {$a} that you do not have permission to modify.';
$string['nodescription'] = 'Regular class session';
$string['noeventstoreset'] = 'There are no calendar events that require an update.';
$string['nogroups'] = 'You can\'t add group sessions. No groups exists in course.';
@ -487,7 +491,12 @@ $string['sessionduplicate'] = 'A duplicate session exists for course: {$a->cours
$string['sessionexist'] = 'Session not added (already exists)!';
$string['sessiongenerated'] = 'One session was successfully generated';
$string['sessions'] = 'Sessions';
$string['sessionsallcourses'] = 'All courses';
$string['sessionsbyactivity'] = 'Attendance instance';
$string['sessionsbycourse'] = 'Course';
$string['sessionsbydate'] = 'Week';
$string['sessionscompleted'] = 'Taken sessions';
$string['sessionscurrentcourses'] = 'Current courses';
$string['sessionsgenerated'] = '{$a} sessions were successfully generated';
$string['sessionsids'] = 'IDs of sessions: ';
$string['sessionsnotfound'] = 'There is no sessions in the selected timespan';
@ -531,6 +540,7 @@ $string['statusset'] = 'Status set {$a}';
$string['statussetsettings'] = 'Status set';
$string['statusunselected'] = 'unselected';
$string['strftimedm'] = '%b %d';
$string['strftimedmw'] = '<nobr>%a %b %d</nobr>';
$string['strftimedmy'] = '%d %b %Y';
$string['strftimedmyhm'] = '%d %b %Y %I.%M%p'; // Line added to allow multiple sessions in the same day.
$string['strftimedmyw'] = '<nobr>%a %d %b %Y</nobr>';
@ -583,6 +593,7 @@ $string['thiscourse'] = 'This course';
$string['time'] = 'Time';
$string['timeahead'] = 'Multiple sessions that exceed one year cannot be created, please adjust the start and end dates.';
$string['to'] = 'to:';
$string['todate'] = 'to date';
$string['triggered'] = 'First notified';
$string['tuseremail'] = 'Email';
$string['tusername'] = 'Full name';
@ -616,5 +627,6 @@ $string['warnings'] = 'Warnings set';
$string['warningthreshold'] = 'Warning threshold';
$string['warningupdated'] = 'Updated warnings';
$string['week'] = 'week(s)';
$string['weekcommencing'] = 'Week commencing';
$string['weeks'] = 'Weeks';
$string['youcantdo'] = 'You can\'t do anything';

135
locallib.php

@ -109,6 +109,93 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) {
return $statusname;
}
/**
* Get full filtered log.
* @param int $userid
* @param stdClass $pageparams
* @return array
*/
function attendance_get_user_sessions_log_full($userid, $pageparams) {
global $DB;
// All taken sessions (including previous groups).
$usercourses = enrol_get_users_courses($userid);
list($usql, $uparams) = $DB->get_in_or_equal(array_keys($usercourses), SQL_PARAMS_NAMED, 'cid0');
$coursesql = "(1 = 1)";
$courseparams = array();
$now = time();
if ($pageparams->sesscourses === 'current') {
$coursesql = "(c.startdate = 0 OR c.startdate <= :now1) AND (c.enddate = 0 OR c.enddate >= :now2)";
$courseparams = array(
'now1' => $now,
'now2' => $now,
);
}
$datesql = "(1 = 1)";
$dateparams = array();
if ($pageparams->startdate && $pageparams->enddate) {
$datesql = "ats.sessdate >= :sdate AND ats.sessdate < :edate";
$dateparams = array(
'sdate' => $pageparams->startdate,
'edate' => $pageparams->enddate,
);
}
if ($pageparams->groupby === 'date') {
$ordersql = "ats.sessdate ASC, c.fullname ASC, att.name ASC, att.id ASC";
} else {
$ordersql = "c.fullname ASC, att.name ASC, att.id ASC, ats.sessdate ASC";
}
// WHERE clause is important:
// gm.userid not null => get unmarked attendances for user's current groups
// ats.groupid 0 => get all sessions that are for all students enrolled in course
// al.id not null => get all marked sessions whether or not user currently still in group.
$sql = "SELECT ats.id, ats.groupid, ats.sessdate, ats.duration, ats.description, ats.statusset,
al.statusid, al.remarks, ats.studentscanmark, ats.autoassignstatus,
ats.preventsharedip, ats.preventsharediptime,
ats.attendanceid, att.name AS attname, att.course AS courseid, c.fullname AS cname
FROM {attendance_sessions} ats
JOIN {attendance} att
ON att.id = ats.attendanceid
JOIN {course} c
ON att.course = c.id
LEFT JOIN {attendance_log} al
ON ats.id = al.sessionid AND al.studentid = :uid
LEFT JOIN {groups_members} gm
ON (ats.groupid = gm.groupid AND gm.userid = :uid1)
WHERE (gm.userid IS NOT NULL OR ats.groupid = 0 OR al.id IS NOT NULL)
AND att.course $usql
AND $datesql
AND $coursesql
ORDER BY $ordersql";
$params = array(
'uid' => $userid,
'uid1' => $userid,
);
$params = array_merge($params, $uparams);
$params = array_merge($params, $dateparams);
$params = array_merge($params, $courseparams);
$sessions = $DB->get_records_sql($sql, $params);
foreach ($sessions as $sess) {
if (empty($sess->description)) {
$sess->description = get_string('nodescription', 'attendance');
} else {
$modinfo = get_fast_modinfo($sess->courseid);
$cmid = $modinfo->instances['attendance'][$sess->attendanceid]->get_course_module_record()->id;
$ctx = context_module::instance($cmid);
$sess->description = file_rewrite_pluginfile_urls($sess->description,
'pluginfile.php', $ctx->id, 'mod_attendance', 'session', $sess->id);
}
}
return $sessions;
}
/**
* Get users courses and the relevant attendances.
*
@ -275,6 +362,54 @@ function attendance_update_users_grade($attendance, $userids=array()) {
return grade_update('mod/attendance', $course->id, 'mod', 'attendance', $attendance->id, 0, $grades);
}
/**
* Update grades for specified users for specified attendance
*
* @param integer $attendanceid - the id of the attendance to update
* @param integer $grade - the value of the 'grade' property of the specified attendance
* @param array $userids - the userids of the users to be updated
*/
function attendance_update_users_grades_by_id($attendanceid, $grade, $userids) {
global $DB;
if (empty($grade)) {
return false;
}
list($course, $cm) = get_course_and_cm_from_instance($attendanceid, 'attendance');
$summary = new mod_attendance_summary($attendanceid, $userids);
if (empty($userids)) {
$context = context_module::instance($cm->id);
$userids = array_keys(get_enrolled_users($context, 'mod/attendance:canbelisted', 0, 'u.id'));
}
if ($grade < 0) {
$dbparams = array('id' => -($grade));
$scale = $DB->get_record('scale', $dbparams);
$scalearray = explode(',', $scale->scale);
$attendancegrade = count($scalearray);
} else {
$attendancegrade = $grade;
}
$grades = array();
foreach ($userids as $userid) {
$grades[$userid] = new stdClass();
$grades[$userid]->userid = $userid;
if ($summary->has_taken_sessions($userid)) {
$usersummary = $summary->get_taken_sessions_summary_for($userid);
$grades[$userid]->rawgrade = $usersummary->takensessionspercentage * $attendancegrade;
} else {
$grades[$userid]->rawgrade = null;
}
}
return grade_update('mod/attendance', $course->id, 'mod', 'attendance', $attendanceid, 0, $grades);
}
/**
* Add an attendance status variable
*

162
renderables.php

@ -486,6 +486,42 @@ class attendance_user_data implements renderable {
$this->sessionslog = $att->get_user_filtered_sessions_log_extended($userid);
$this->groups = groups_get_all_groups($att->course->id);
} else if ($this->pageparams->mode == mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$this->coursesatts = attendance_get_user_courses_attendances($userid);
$this->statuses = array();
$this->summaries = array();
$this->groups = array();
foreach ($this->coursesatts as $atid => $ca) {
// Check to make sure the user can view this cm.
$modinfo = get_fast_modinfo($ca->courseid);
if (!$modinfo->instances['attendance'][$ca->attid]->uservisible) {
unset($this->coursesatts[$atid]);
continue;
} else {
$this->coursesatts[$atid]->cmid = $modinfo->instances['attendance'][$ca->attid]->get_course_module_record()->id;
}
$this->statuses[$ca->attid] = attendance_get_statuses($ca->attid);
$this->summaries[$ca->attid] = new mod_attendance_summary($ca->attid, array($userid));
if (!array_key_exists($ca->courseid, $this->groups)) {
$this->groups[$ca->courseid] = groups_get_all_groups($ca->courseid);
}
}
if (!$mobile) {
$this->summary = new mod_attendance_summary($att->id, array($userid), $att->pageparams->startdate,
$att->pageparams->enddate);
$this->filtercontrols = new attendance_filter_controls($att);
}
$this->sessionslog = attendance_get_user_sessions_log_full($userid, $this->pageparams);
foreach ($this->sessionslog as $sessid => $sess) {
$this->sessionslog[$sessid]->cmid = $this->coursesatts[$sess->attendanceid]->cmid;
}
} else {
$this->coursesatts = attendance_get_user_courses_attendances($userid);
$this->statuses = array();
@ -510,11 +546,131 @@ class attendance_user_data implements renderable {
}
/**
* url helper.
* Url function
* @param array $params
* @param array $excludeparams
* @return moodle_url
*/
public function url() {
return new moodle_url($this->urlpath, $this->urlparams);
public function url($params=array(), $excludeparams=array()) {
$params = array_merge($this->urlparams, $params);
foreach ($excludeparams as $paramkey) {
unset($params[$paramkey]);
}
return new moodle_url($this->urlpath, $params);
}
/**
* Take multiple sessions attendance from form data.
*
* @param stdClass $formdata
*/
public function take_sessions_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 whole function could do with a nice clean up.
$now = time();
$sesslog = array();
$formdata = (array)$formdata;
$updatedsessions = array();
$sessionatt = array();
foreach ($formdata as $key => $value) {
// Look at Remarks field because the user options may not be passed if empty.
if (substr($key, 0, 7) == 'remarks') {
$parts = explode('sess', substr($key, 7));
$stid = $parts[0];
if (!(is_numeric($stid))) { // Sanity check on $stid.
print_error('nonnumericid', 'attendance');
}
$sessid = $parts[1];
if (!(is_numeric($sessid))) { // Sanity check on $sessid.
print_error('nonnumericid', 'attendance');
}
$dbsession = $this->sessionslog[$sessid];
$context = context_module::instance($dbsession->cmid);
if (!has_capability('mod/attendance:takeattendances', $context)) {
// How do we tell user about this?
\core\notification::warning(get_string("nocapabilitytotakethisattendance", "attendance", $dbsession->cmid));
continue;
}
$formkey = 'user'.$stid.'sess'.$sessid;
$attid = $dbsession->attendanceid;
$statusset = array_filter($this->statuses[$attid],
function($x) use($dbsession) {
return $x->setnumber === $dbsession->statusset;
});
$sessionatt[$sessid] = $attid;
$formlog = new stdClass();
if (array_key_exists($formkey, $formdata) && is_numeric($formdata[$formkey])) {
$formlog->statusid = $formdata[$formkey];
}
$formlog->studentid = $stid; // We check is_numeric on this above.
$formlog->statusset = implode(',', array_keys($statusset));
$formlog->remarks = $value;
$formlog->sessionid = $sessid;
$formlog->timetaken = $now;
$formlog->takenby = $USER->id;
if (!array_key_exists($stid, $sesslog)) {
$sesslog[$stid] = array();
}
$sesslog[$stid][$sessid] = $formlog;
}
}
$updateatts = array();
foreach ($sesslog as $stid => $userlog) {
$dbstudlog = $DB->get_records('attendance_log', array('studentid' => $stid), '',
'sessionid,statusid,remarks,id,statusset');
foreach ($userlog as $log) {
if (array_key_exists($log->sessionid, $dbstudlog)) {
$attid = $sessionatt[$log->sessionid];
// Check if anything important has changed before updating record.
// Don't update timetaken/takenby records if nothing has changed.
if ($dbstudlog[$log->sessionid]->remarks != $log->remarks ||
$dbstudlog[$log->sessionid]->statusid != $log->statusid ||
$dbstudlog[$log->sessionid]->statusset != $log->statusset) {
$log->id = $dbstudlog[$log->sessionid]->id;
$DB->update_record('attendance_log', $log);
$updatedsessions[$log->sessionid] = $log->sessionid;
if (!array_key_exists($attid, $updateatts)) {
$updateatts[$attid] = array();
}
array_push($updateatts[$attid], $log->studentid);
}
} else {
$DB->insert_record('attendance_log', $log, false);
$updatedsessions[$log->sessionid] = $log->sessionid;
if (!array_key_exists($attid, $updateatts)) {
$updateatts[$attid] = array();
}
array_push($updateatts[$attid], $log->studentid);
}
}
}
foreach ($updatedsessions as $sessionid) {
$session = $this->sessionslog[$sessionid];
$session->lasttaken = $now;
$session->lasttakenby = $USER->id;
$DB->update_record('attendance_sessions', $session);
}
if (!empty($updateatts)) {
$attendancegrade = $DB->get_records_list('attendance', 'id', array_keys($updateatts), '', 'id, grade');
foreach ($updateatts as $attid => $updateusers) {
if ($attendancegrade[$attid] != 0) {
attendance_update_users_grades_by_id($attid, $grade, $updateusers);
}
}
}
}
}

826
renderer.php

@ -56,21 +56,42 @@ class mod_attendance_renderer extends plugin_renderer_base {
* @return string html code
*/
protected function render_attendance_filter_controls(attendance_filter_controls $fcontrols) {
$classes = 'attfiltercontrols';
$filtertable = new html_table();
$filtertable->attributes['class'] = ' ';
$filtertable->width = '100%';
$filtertable->align = array('left', 'center', 'right', 'right');
$filtertable->data[0][] = $this->render_sess_group_selector($fcontrols);
if (property_exists($fcontrols->pageparams, 'mode') &&
$fcontrols->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$classes .= ' float-right';
$filtertable->data[0][] = $this->render_curdate_controls($fcontrols);
$row = array();
$row[] = '';
$row[] = '';
$row[] = '';
$row[] = $this->render_grouping_controls($fcontrols);
$filtertable->data[] = $row;
$filtertable->data[0][] = $this->render_paging_controls($fcontrols);
$row = array();
$row[] = '';
$row[] = '';
$row[] = '';
$row[] = $this->render_course_controls($fcontrols);
$filtertable->data[] = $row;
}
$row = array();
$row[] = $this->render_sess_group_selector($fcontrols);
$row[] = $this->render_curdate_controls($fcontrols);
$row[] = $this->render_paging_controls($fcontrols);
$row[] = $this->render_view_controls($fcontrols);
$filtertable->data[0][] = $this->render_view_controls($fcontrols);
$filtertable->data[] = $row;
$o = html_writer::table($filtertable);
$o = $this->output->container($o, 'attfiltercontrols');
$o = $this->output->container($o, $classes);
return $o;
}
@ -198,6 +219,59 @@ class mod_attendance_renderer extends plugin_renderer_base {
return $curdatecontrols;
}
/**
* Render grouping controls (for all sessions report).
*
* @param attendance_filter_controls $fcontrols
* @return string
*/
protected function render_grouping_controls(attendance_filter_controls $fcontrols) {
if ($fcontrols->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$groupoptions = array(
'date' => get_string('sessionsbydate', 'attendance'),
'activity' => get_string('sessionsbyactivity', 'attendance'),
'course' => get_string('sessionsbycourse', 'attendance')
);
$groupcontrols = get_string('groupsessionsby', 'attendance') . ":";
foreach ($groupoptions as $key => $opttext) {
if ($key != $fcontrols->pageparams->groupby) {
$link = html_writer::link($fcontrols->url(array('groupby' => $key)), $opttext);
$groupcontrols .= html_writer::tag('span', $link, array('class' => 'attbtn'));
} else {
$groupcontrols .= html_writer::tag('span', $opttext, array('class' => 'attcurbtn'));
}
}
return html_writer::tag('nobr', $groupcontrols);
}
return "";
}
/**
* Render course controls (for all sessions report).
*
* @param attendance_filter_controls $fcontrols
* @return string
*/
protected function render_course_controls(attendance_filter_controls $fcontrols) {
if ($fcontrols->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$courseoptions = array(
'all' => get_string('sessionsallcourses', 'attendance'),
'current' => get_string('sessionscurrentcourses', 'attendance')
);
$coursecontrols = "";
foreach ($courseoptions as $key => $opttext) {
if ($key != $fcontrols->pageparams->sesscourses) {
$link = html_writer::link($fcontrols->url(array('sesscourses' => $key)), $opttext);
$coursecontrols .= html_writer::tag('span', $link, array('class' => 'attbtn'));
} else {
$coursecontrols .= html_writer::tag('span', $opttext, array('class' => 'attcurbtn'));
}
}
return html_writer::tag('nobr', $coursecontrols);
}
return "";
}
/**
* Render view controls.
*
@ -926,6 +1000,72 @@ class mod_attendance_renderer extends plugin_renderer_base {
return $celldata;
}
/**
* Construct take session controls.
*
* @param attendance_take_data $takedata
* @param stdClass $user
* @return array
*/
private function construct_take_session_controls(attendance_take_data $takedata, $user) {
$celldata = array();
$celldata['remarks'] = '';
if ($user->enrolmentend and $user->enrolmentend < $takedata->sessioninfo->sessdate) {
$celldata['text'] = get_string('enrolmentend', 'attendance', userdate($user->enrolmentend, '%d.%m.%Y'));
$celldata['colspan'] = count($takedata->statuses) + 1;
$celldata['class'] = 'userwithoutenrol';
} else if (!$user->enrolmentend and $user->enrolmentstatus == ENROL_USER_SUSPENDED) {
// No enrolmentend and ENROL_USER_SUSPENDED.
$celldata['text'] = get_string('enrolmentsuspended', 'attendance');
$celldata['colspan'] = count($takedata->statuses) + 1;
$celldata['class'] = 'userwithoutenrol';
} else {
if ($takedata->updatemode and !array_key_exists($user->id, $takedata->sessionlog)) {
$celldata['class'] = 'userwithoutdata';
}
$celldata['text'] = array();
foreach ($takedata->statuses as $st) {
$params = array(
'type' => 'radio',
'name' => 'user'.$user->id.'sess'.$takedata->sessioninfo->id,
'class' => 'st'.$st->id,
'value' => $st->id);
if (array_key_exists($user->id, $takedata->sessionlog) and $st->id == $takedata->sessionlog[$user->id]->statusid) {
$params['checked'] = '';
}
$input = html_writer::empty_tag('input', $params);
if ($takedata->pageparams->viewmode == mod_attendance_take_page_params::SORTED_GRID) {
$input = html_writer::tag('nobr', $input . $st->acronym);
}
$celldata['text'][] = $input;
}
$params = array(
'type' => 'text',
'name' => 'remarks'.$user->id.'sess'.$takedata->sessioninfo->id,
'maxlength' => 255);
if (array_key_exists($user->id, $takedata->sessionlog)) {
$params['value'] = $takedata->sessionlog[$user->id]->remarks;
}
$input = html_writer::empty_tag('input', $params);
if ($takedata->pageparams->viewmode == mod_attendance_take_page_params::SORTED_GRID) {
$input = html_writer::empty_tag('br').$input;
}
$celldata['remarks'] = $input;
if ($user->enrolmentstart > $takedata->sessioninfo->sessdate + $takedata->sessioninfo->duration) {
$celldata['warning'] = get_string('enrolmentstart', 'attendance',
userdate($user->enrolmentstart, '%H:%M %d.%m.%Y'));
$celldata['class'] = 'userwithoutenrol';
}
}
return $celldata;
}
/**
* Render header.
*
@ -960,7 +1100,8 @@ class mod_attendance_renderer extends plugin_renderer_base {
$o = $this->render_user_report_tabs($userdata);
if ($USER->id == $userdata->user->id) {
if ($USER->id == $userdata->user->id ||
$userdata->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$o .= $this->construct_user_data($userdata);
@ -993,11 +1134,14 @@ class mod_attendance_renderer extends plugin_renderer_base {
$userdata->url()->out(true, array('mode' => mod_attendance_view_page_params::MODE_THIS_COURSE)),
get_string('thiscourse', 'attendance'));
// Skip the 'all courses' tab for 'temporary' users.
// Skip the 'all courses' and 'all sessions' tabs for 'temporary' users.
if ($userdata->user->type == 'standard') {
$tabs[] = new tabobject(mod_attendance_view_page_params::MODE_ALL_COURSES,
$userdata->url()->out(true, array('mode' => mod_attendance_view_page_params::MODE_ALL_COURSES)),
get_string('allcourses', 'attendance'));
$tabs[] = new tabobject(mod_attendance_view_page_params::MODE_ALL_SESSIONS,
$userdata->url()->out(true, array('mode' => mod_attendance_view_page_params::MODE_ALL_SESSIONS)),
get_string('allsessions', 'attendance'));
}
return print_tabs(array($tabs), $userdata->pageparams->mode, null, null, true);
@ -1022,6 +1166,22 @@ class mod_attendance_renderer extends plugin_renderer_base {
$o .= html_writer::empty_tag('hr');
$o .= construct_user_data_stat($userdata->summary->get_all_sessions_summary_for($userdata->user->id),
$userdata->pageparams->view);
} else if ($userdata->pageparams->mode == mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
$allsessions = $this->construct_user_allsessions_log($userdata);
$o .= html_writer::start_div('allsessionssummary');
$o .= html_writer::start_div('float-left');
$o .= html_writer::start_div('float-left');
$o .= $this->user_picture($userdata->user, array('size' => 100, 'class' => 'userpicture float-left'));
$o .= html_writer::end_div();
$o .= html_writer::start_div('float-right');
$o .= $allsessions->summary;
$o .= html_writer::end_div();
$o .= html_writer::end_div();
$o .= html_writer::start_div('float-right');
$o .= $this->render_attendance_filter_controls($userdata->filtercontrols);
$o .= html_writer::end_div();
$o .= html_writer::end_div();
$o .= $allsessions->detail;
} else {
$table = new html_table();
$table->head = array(get_string('course'),
@ -1226,6 +1386,658 @@ class mod_attendance_renderer extends plugin_renderer_base {
return html_writer::table($table);
}
/**
* Construct table showing all sessions, not limited to current course.
*
* @param attendance_user_data $userdata
* @return string
*/
private function construct_user_allsessions_log(attendance_user_data $userdata) {
global $USER;
$allsessions = new stdClass();
$shortform = false;
if ($USER->id == $userdata->user->id) {
// This is a user viewing their own stuff - hide non-relevant columns.
$shortform = true;
}
$groupby = $userdata->pageparams->groupby;
$table = new html_table();
$table->attributes['class'] = 'generaltable attwidth boxaligncenter allsessions';
$table->head = array();
$table->align = array();
$table->size = array();
$table->colclasses = array();
$colcount = 0;
$summarywidth = 0;
// If grouping by date, we need some form of date up front.
// Only need course column if we are not using course to group
// (currently date is only option which does not use course).
if ($groupby === 'date') {
$table->head[] = '';
$table->align[] = 'left';
$table->colclasses[] = 'grouper';
$table->size[] = '1px';
$table->head[] = get_string('date');
$table->align[] = 'left';
$table->colclasses[] = 'datecol';
$table->size[] = '1px';
$colcount++;
$table->head[] = get_string('course');
$table->align[] = 'left';
$table->colclasses[] = 'colcourse';
$colcount++;
} else {
$table->head[] = '';
$table->align[] = 'left';
$table->colclasses[] = 'grouper';
$table->size[] = '1px';
if ($groupby === 'activity') {
$table->head[] = '';
$table->align[] = 'left';
$table->colclasses[] = 'grouper';
$table->size[] = '1px';
}
}
// Need activity column unless we are using activity to group.
if ($groupby !== 'activity') {
$table->head[] = get_string('pluginname', 'mod_attendance');
$table->align[] = 'left';
$table->colclasses[] = 'colcourse';
$table->size[] = '*';
$colcount++;
}
// If grouping by date, it belongs up front rather than here.
if ($groupby !== 'date') {
$table->head[] = get_string('date');
$table->align[] = 'left';
$table->colclasses[] = 'datecol';
$table->size[] = '1px';
$colcount++;
}
// Use "session" instead of "description".
$table->head[] = get_string('session', 'attendance');
$table->align[] = 'left';
$table->colclasses[] = 'desccol';
$table->size[] = '*';
$colcount++;
if (!$shortform) {
$table->head[] = get_string('sessiontypeshort', 'attendance');
$table->align[] = '';
$table->size[] = '*';
$table->colclasses[] = '';
$colcount++;
}
if (!empty($USER->attendanceediting)) {
$table->head[] = get_string('status', 'attendance');
$table->align[] = 'center';
$table->colclasses[] = 'statuscol';
$table->size[] = '*';
$colcount++;
$summarywidth++;
$table->head[] = get_string('remarks', 'attendance');
$table->align[] = 'center';
$table->colclasses[] = 'remarkscol';
$table->size[] = '*';
$colcount++;
$summarywidth++;
} else {
$table->head[] = get_string('status', 'attendance');
$table->align[] = 'center';
$table->colclasses[] = 'statuscol';
$table->size[] = '*';
$colcount++;
$summarywidth++;
$table->head[] = get_string('points', 'attendance');
$table->align[] = 'center';
$table->colclasses[] = 'pointscol';
$table->size[] = '1px';
$colcount++;
$summarywidth++;
$table->head[] = get_string('remarks', 'attendance');
$table->align[] = 'center';
$table->colclasses[] = 'remarkscol';
$table->size[] = '*';
$colcount++;
$summarywidth++;
}
$statusmaxpoints = array();
foreach ($userdata->statuses as $attid => $attstatuses) {
$statusmaxpoints[$attid] = attendance_get_statusset_maxpoints($attstatuses);
}
$lastgroup = array(null, null);
$groups = array();
$stats = array(
'course' => array(),
'activity' => array(),
'date' => array(),
'overall' => array(
'points' => 0,
'maxpointstodate' => 0,
'maxpoints' => 0,
'pcpointstodate' => null,
'pcpoints' => null,
'statuses' => array()
)
);
$group = null;
if ($userdata->sessionslog) {
foreach ($userdata->sessionslog as $sess) {
if ($groupby === 'date') {
$weekformat = date("YW", $sess->sessdate);
if ($weekformat != $lastgroup[0]) {
if ($group !== null) {
array_push($groups, $group);
}
$group = array();
$lastgroup[0] = $weekformat;
}
if (!array_key_exists($weekformat, $stats['date'])) {
$stats['date'][$weekformat] = array(
'points' => 0,
'maxpointstodate' => 0,
'maxpoints' => 0,
'pcpointstodate' => null,
'pcpoints' => null,
'statuses' => array()
);
}
$statussetmaxpoints = $statusmaxpoints[$sess->attendanceid];
// Ensure all possible acronyms for current sess's statusset are available as
// keys in status array for period.
//
// A bit yucky because we can't tell whether we've seen statusset before, and
// we usually will have, so much wasted spinning.
foreach ($userdata->statuses[$sess->attendanceid] as $attstatus) {
if ($attstatus->setnumber === $sess->statusset) {
if (!array_key_exists($attstatus->acronym, $stats['date'][$weekformat]['statuses'])) {
$stats['date'][$weekformat]['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
if (!array_key_exists($attstatus->acronym, $stats['overall']['statuses'])) {
$stats['overall']['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
}
}
// The array_key_exists check is for hidden statuses.
if (isset($sess->statusid) && array_key_exists($sess->statusid, $userdata->statuses[$sess->attendanceid])) {
$status = $userdata->statuses[$sess->attendanceid][$sess->statusid];
$stats['date'][$weekformat]['statuses'][$status->acronym]['count']++;
$stats['date'][$weekformat]['points'] += $status->grade;
$stats['date'][$weekformat]['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['statuses'][$status->acronym]['count']++;
$stats['overall']['points'] += $status->grade;
$stats['overall']['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
}
$stats['date'][$weekformat]['maxpoints'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['maxpoints'] += $statussetmaxpoints[$sess->statusset];
} else {
// By course and perhaps activity.
if (
($sess->courseid != $lastgroup[0]) ||
($groupby === 'activity' && $sess->cmid != $lastgroup[1])
) {
if ($group !== null) {
array_push($groups, $group);
}
$group = array();
$lastgroup[0] = $sess->courseid;
$lastgroup[1] = $sess->cmid;
}
if (!array_key_exists($sess->courseid, $stats['course'])) {
$stats['course'][$sess->courseid] = array(
'points' => 0,
'maxpointstodate' => 0,
'maxpoints' => 0,
'pcpointstodate' => null,
'pcpoints' => null,
'statuses' => array()
);
}
$statussetmaxpoints = $statusmaxpoints[$sess->attendanceid];
// Ensure all possible acronyms for current sess's statusset are available as
// keys in status array for course
//
// A bit yucky because we can't tell whether we've seen statusset before, and
// we usually will have, so much wasted spinning.
foreach ($userdata->statuses[$sess->attendanceid] as $attstatus) {
if ($attstatus->setnumber === $sess->statusset) {
if (!array_key_exists($attstatus->acronym, $stats['course'][$sess->courseid]['statuses'])) {
$stats['course'][$sess->courseid]['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
if (!array_key_exists($attstatus->acronym, $stats['overall']['statuses'])) {
$stats['overall']['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
}
}
// The array_key_exists check is for hidden statuses.
if (isset($sess->statusid) && array_key_exists($sess->statusid, $userdata->statuses[$sess->attendanceid])) {
$status = $userdata->statuses[$sess->attendanceid][$sess->statusid];
$stats['course'][$sess->courseid]['statuses'][$status->acronym]['count']++;
$stats['course'][$sess->courseid]['points'] += $status->grade;
$stats['course'][$sess->courseid]['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['statuses'][$status->acronym]['count']++;
$stats['overall']['points'] += $status->grade;
$stats['overall']['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
}
$stats['course'][$sess->courseid]['maxpoints'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['maxpoints'] += $statussetmaxpoints[$sess->statusset];
if (!array_key_exists($sess->cmid, $stats['activity'])) {
$stats['activity'][$sess->cmid] = array(
'points' => 0,
'maxpointstodate' => 0,
'maxpoints' => 0,
'pcpointstodate' => null,
'pcpoints' => null,
'statuses' => array()
);
}
$statussetmaxpoints = $statusmaxpoints[$sess->attendanceid];
// Ensure all possible acronyms for current sess's statusset are available as
// keys in status array for period
//
// A bit yucky because we can't tell whether we've seen statusset before, and
// we usually will have, so much wasted spinning.
foreach ($userdata->statuses[$sess->attendanceid] as $attstatus) {
if ($attstatus->setnumber === $sess->statusset) {
if (!array_key_exists($attstatus->acronym, $stats['activity'][$sess->cmid]['statuses'])) {
$stats['activity'][$sess->cmid]['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
if (!array_key_exists($attstatus->acronym, $stats['overall']['statuses'])) {
$stats['overall']['statuses'][$attstatus->acronym] =
array('count' => 0, 'description' => $attstatus->description);
}
}
}
// The array_key_exists check is for hidden statuses.
if (isset($sess->statusid) && array_key_exists($sess->statusid, $userdata->statuses[$sess->attendanceid])) {
$status = $userdata->statuses[$sess->attendanceid][$sess->statusid];
$stats['activity'][$sess->cmid]['statuses'][$status->acronym]['count']++;
$stats['activity'][$sess->cmid]['points'] += $status->grade;
$stats['activity'][$sess->cmid]['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['statuses'][$status->acronym]['count']++;
$stats['overall']['points'] += $status->grade;
$stats['overall']['maxpointstodate'] += $statussetmaxpoints[$sess->statusset];
}
$stats['activity'][$sess->cmid]['maxpoints'] += $statussetmaxpoints[$sess->statusset];
$stats['overall']['maxpoints'] += $statussetmaxpoints[$sess->statusset];
}
array_push($group, $sess);
}
array_push($groups, $group);
}
$points = $stats['overall']['points'];
$maxpoints = $stats['overall']['maxpointstodate'];
$summarytable = new html_table();
$summarytable->attributes['class'] = 'generaltable table-bordered table-condensed';
$row = new html_table_row();
$cell = new html_table_cell(get_string('allsessionstotals', 'attendance'));
$cell->colspan = 2;
$cell->header = true;
$row->cells[] = $cell;
$summarytable->data[] = $row;
foreach ($stats['overall']['statuses'] as $acronym => $status) {
$row = new html_table_row();
$row->cells[] = $status['description'] . ":";
$row->cells[] = $status['count'];
$summarytable->data[] = $row;
}
$row = new html_table_row();
if ($maxpoints !== 0) {
$pctodate = format_float( $points * 100 / $maxpoints);
$pointsinfo = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
$pointsinfo .= " (" . $pctodate . "%)";
} else {
$pointsinfo = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
}
$pointsinfo .= " " . get_string('todate', 'attendance');
$cell = new html_table_cell($pointsinfo);
$cell->colspan = 2;
$row->cells[] = $cell;
$summarytable->data[] = $row;
$allsessions->summary = html_writer::table($summarytable);
$lastgroup = array(null, null);
foreach ($groups as $group) {
$statussetmaxpoints = $statusmaxpoints[$sess->attendanceid];
// For use in headings etc.
$sess = $group[0];
if ($groupby === 'date') {
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$cell = new html_table_cell();
$cell->rowspan = count($group) + 2;
$row->cells[] = $cell;
$week = date("W", $sess->sessdate);
$year = date("Y", $sess->sessdate);
// ISO week starts on day 1, Monday.
$weekstart = date_timestamp_get(date_isodate_set(date_create(), $year, $week, 1));
$dmywformat = get_string('strftimedmyw', 'attendance');
$cell = new html_table_cell(get_string('weekcommencing', 'attendance') . ": " . userdate($weekstart, $dmywformat));
$cell->colspan = $colcount - $summarywidth;
$cell->rowspan = 2;
$cell->attributes['class'] = 'groupheading';
$row->cells[] = $cell;
$weekformat = date("YW", $sess->sessdate);
$points = $stats['date'][$weekformat]['points'];
$maxpoints = $stats['date'][$weekformat]['maxpointstodate'];
if ($maxpoints !== 0) {
$pctodate = format_float( $points * 100 / $maxpoints);
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
$summary .= " (" . $pctodate . "%)";
} else {
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
}
$summary .= " " . get_string('todate', 'attendance');
$cell = new html_table_cell($summary);
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$summary = array();
foreach ($stats['date'][$weekformat]['statuses'] as $acronym => $status) {
array_push($summary, html_writer::tag('b', $acronym) . $status['count']);
}
$cell = new html_table_cell(implode(" ", $summary));
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
$lastgroup[0] = date("YW", $weekstart);
} else {
if ($groupby === 'course' || $sess->courseid !== $lastgroup[0]) {
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$cell = new html_table_cell();
$cell->rowspan = count($group) + 2;
if ($groupby === 'activity') {
$headcell = $cell; // Keep ref to be able to adjust rowspan later.
$cell->rowspan += 2;
$row->cells[] = $cell;
$cell = new html_table_cell();
$cell->rowspan = 2;
}
$row->cells[] = $cell;
$courseurl = new moodle_url('/course/view.php', array('id' => $sess->courseid));
$cell = new html_table_cell(get_string('course', 'attendance') . ": " .
html_writer::link($courseurl, $sess->cname));
$cell->colspan = $colcount - $summarywidth;
$cell->rowspan = 2;
$cell->attributes['class'] = 'groupheading';
$row->cells[] = $cell;
$points = $stats['course'][$sess->courseid]['points'];
$maxpoints = $stats['course'][$sess->courseid]['maxpointstodate'];
if ($maxpoints !== 0) {
$pctodate = format_float( $points * 100 / $maxpoints);
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
$summary .= " (" . $pctodate . "%)";
} else {
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
}
$summary .= " " . get_string('todate', 'attendance');
$cell = new html_table_cell($summary);
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$summary = array();
foreach ($stats['course'][$sess->courseid]['statuses'] as $acronym => $status) {
array_push($summary, html_writer::tag('b', $acronym) . $status['count']);
}
$cell = new html_table_cell(implode(" ", $summary));
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
}
if ($groupby === 'activity') {
if ($sess->courseid === $lastgroup[0]) {
$headcell->rowspan += count($group) + 2;
}
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$cell = new html_table_cell();
$cell->rowspan = count($group) + 2;
$row->cells[] = $cell;
$attendanceurl = new moodle_url('/mod/attendance/view.php', array('id' => $sess->cmid,
'studentid' => $userdata->user->id,
'view' => ATT_VIEW_ALL));
$cell = new html_table_cell(get_string('pluginname', 'mod_attendance') .
": " . html_writer::link($attendanceurl, $sess->attname));
$cell->colspan = $colcount - $summarywidth;
$cell->rowspan = 2;
$cell->attributes['class'] = 'groupheading';
$row->cells[] = $cell;
$points = $stats['activity'][$sess->cmid]['points'];
$maxpoints = $stats['activity'][$sess->cmid]['maxpointstodate'];
if ($maxpoints !== 0) {
$pctodate = format_float( $points * 100 / $maxpoints);
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
$summary .= " (" . $pctodate . "%)";
} else {
$summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints;
}
$summary .= " " . get_string('todate', 'attendance');
$cell = new html_table_cell($summary);
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
$row = new html_table_row();
$row->attributes['class'] = 'grouper';
$summary = array();
foreach ($stats['activity'][$sess->cmid]['statuses'] as $acronym => $status) {
array_push($summary, html_writer::tag('b', $acronym) . $status['count']);
}
$cell = new html_table_cell(implode(" ", $summary));
$cell->colspan = $summarywidth;
$row->cells[] = $cell;
$table->data[] = $row;
}
$lastgroup[0] = $sess->courseid;
$lastgroup[1] = $sess->cmid;
}
// Now iterate over sessions in group...
foreach ($group as $sess) {
$row = new html_table_row();
// If grouping by date, we need some form of date up front.
// Only need course column if we are not using course to group
// (currently date is only option which does not use course).
if ($groupby === 'date') {
// What part of date do we want if grouped by it already?
$row->cells[] = userdate($sess->sessdate, get_string('strftimedmw', 'attendance')) .
" ". $this->construct_time($sess->sessdate, $sess->duration);
$courseurl = new moodle_url('/course/view.php', array('id' => $sess->courseid));
$row->cells[] = html_writer::link($courseurl, $sess->cname);
}
// Need activity column unless we are using activity to group.
if ($groupby !== 'activity') {
$attendanceurl = new moodle_url('/mod/attendance/view.php', array('id' => $sess->cmid,
'studentid' => $userdata->user->id,
'view' => ATT_VIEW_ALL));
$row->cells[] = html_writer::link($attendanceurl, $sess->attname);
}
// If grouping by date, it belongs up front rather than here.
if ($groupby !== 'date') {
$row->cells[] = userdate($sess->sessdate, get_string('strftimedmyw', 'attendance')) .
" ". $this->construct_time($sess->sessdate, $sess->duration);
}
$sesscontext = context_module::instance($sess->cmid);
if (has_capability('mod/attendance:takeattendances', $sesscontext)) {
$sessionurl = new moodle_url('/mod/attendance/take.php', array('id' => $sess->cmid,
'sessionid' => $sess->id,
'grouptype' => $sess->groupid));
$description = html_writer::link($sessionurl, $sess->description);
} else {
$description = $sess->description;
}
$row->cells[] = $description;
if (!$shortform) {
if ($sess->groupid) {
$sessiontypeshort = get_string('group') . ': ' . $userdata->groups[$sess->courseid][$sess->groupid]->name;
} else {
$sessiontypeshort = get_string('commonsession', 'attendance');
}
$row->cells[] = html_writer::tag('nobr', $sessiontypeshort);
}
if (!empty($USER->attendanceediting)) {
$context = context_module::instance($sess->cmid);
if (has_capability('mod/attendance:takeattendances', $context)) {
// Takedata needs:
// sessioninfo->sessdate
// sessioninfo->duration
// statuses
// updatemode
// sessionlog[userid]->statusid
// sessionlog[userid]->remarks
// pageparams->viewmode == mod_attendance_take_page_params::SORTED_GRID
// and urlparams to be able to use url method later.
//
// user needs:
// enrolmentstart
// enrolmentend
// enrolmentstatus
// id.
$nastyhack = new ReflectionClass('attendance_take_data');
$takedata = $nastyhack->newInstanceWithoutConstructor();
$takedata->sessioninfo = $sess;
$takedata->statuses = array_filter($userdata->statuses[$sess->attendanceid], function($x) use ($sess) {
return ($x->setnumber == $sess->statusset);
});
$takedata->updatemode = true;
$takedata->sessionlog = array($userdata->user->id => $sess);
$takedata->pageparams = new stdClass();
$takedata->pageparams->viewmode = mod_attendance_take_page_params::SORTED_GRID;
$ucdata = $this->construct_take_session_controls($takedata, $userdata->user);
$celltext = join($ucdata['text']);
if (array_key_exists('warning', $ucdata)) {
$celltext .= html_writer::empty_tag('br');
$celltext .= $ucdata['warning'];
}
if (array_key_exists('class', $ucdata)) {
$row->attributes['class'] = $ucdata['class'];
}
$cell = new html_table_cell($celltext);
$row->cells[] = $cell;
$celltext = empty($ucdata['remarks']) ? '' : $ucdata['remarks'];
$cell = new html_table_cell($celltext);
$row->cells[] = $cell;
} else {
if (!empty($sess->statusid)) {
$status = $userdata->statuses[$sess->attendanceid][$sess->statusid];
$row->cells[] = $status->description;
$row->cells[] = $sess->remarks;
}
}
} else {
if (!empty($sess->statusid)) {
$status = $userdata->statuses[$sess->attendanceid][$sess->statusid];
$row->cells[] = $status->description;
$row->cells[] = format_float($status->grade, 1, true, true) . ' / ' .
format_float($statussetmaxpoints[$status->setnumber], 1, true, true);
$row->cells[] = $sess->remarks;
} else if (($sess->sessdate + $sess->duration) < $userdata->user->enrolmentstart) {
$cell = new html_table_cell(get_string('enrolmentstart', 'attendance',
userdate($userdata->user->enrolmentstart, '%d.%m.%Y')));
$cell->colspan = 3;
$row->cells[] = $cell;
} else if ($userdata->user->enrolmentend and $sess->sessdate > $userdata->user->enrolmentend) {
$cell = new html_table_cell(get_string('enrolmentend', 'attendance',
userdate($userdata->user->enrolmentend, '%d.%m.%Y')));
$cell->colspan = 3;
$row->cells[] = $cell;
} else {
list($canmark, $reason) = attendance_can_student_mark($sess, false);
if ($canmark) {
// 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 = 3;
$row->cells[] = $cell;
} else { // Student cannot mark their own attendace.
$row->cells[] = '?';
$row->cells[] = '? / ' . format_float($statussetmaxpoints[$sess->statusset], 1, true, true);
$row->cells[] = '';
}
}
}
$table->data[] = $row;
}
}
if (!empty($USER->attendanceediting)) {
$row = new html_table_row();
$params = array(
'type' => 'submit',
'class' => 'btn btn-primary',
'value' => get_string('save', 'attendance'));
$cell = new html_table_cell(html_writer::tag('center', html_writer::empty_tag('input', $params)));
$cell->colspan = $colcount + (($groupby == 'activity') ? 2 : 1);
$row->cells[] = $cell;
$table->data[] = $row;
}
$logtext = html_writer::table($table);
if (!empty($USER->attendanceediting)) {
$formtext = html_writer::start_div('no-overflow');
$formtext .= $logtext;
$formtext .= html_writer::input_hidden_params($userdata->url(array('sesskey' => sesskey())));
$formtext .= html_writer::end_div();
// Could use userdata->urlpath if not private or userdata->url_path() if existed, but '' turns
// out to DTRT.
$logtext = html_writer::tag('form', $formtext, array('method' => 'post', 'action' => '',
'id' => 'attendancetakeform'));
}
$allsessions->detail = $logtext;
return $allsessions;
}
/**
* Construct time for display.
*

24
styles.css

@ -4,6 +4,7 @@
margin-left: 2px;
margin-right: 2px;
padding: 5px;
display: inline-block;
}
.path-mod-attendance .attcurbtn {
@ -16,7 +17,6 @@
margin-bottom: 10px;
margin-left: auto;
margin-right: auto;
width: 90%;
}
.path-mod-attendance .attfiltercontrols #currentdate {
@ -54,6 +54,10 @@
font-size: 0.8em;
}
.path-mod-attendance div.allsessionssummary + form#attendancetakeform > div {
width: 100%;
}
.path-mod-attendance table.controls {
text-align: center;
width: 100%;
@ -105,9 +109,27 @@
background-color: #eee;
padding: 30px 10px;
}
.path-mod-attendance table.userinfobox .userpicture {
margin: 0;
}
.path-mod-attendance table.attlist td.c0 {
text-align: right;
}
.path-mod-attendance table.allsessions tr.grouper td {
background-color: #eee;
}
.path-mod-attendance table.allsessions td.groupheading {
font-weight: bold;
}
.path-mod-attendance .allsessionssummary > * {
display: inline-block;
}
.path-mod-attendance .allsessionssummary .float-right {
float: right;
}
.path-mod-attendance .allsessionssummary .float-left {
float: left;
}
#page-mod-attendance-preferences .generalbox {
text-align: center;

79
view.php

@ -29,10 +29,13 @@ require_once(dirname(__FILE__).'/locallib.php');
$pageparams = new mod_attendance_view_page_params();
$id = required_param('id', PARAM_INT);
$edit = optional_param('edit', -1, PARAM_BOOL);
$pageparams->studentid = optional_param('studentid', null, PARAM_INT);
$pageparams->mode = optional_param('mode', mod_attendance_view_page_params::MODE_THIS_COURSE, PARAM_INT);
$pageparams->view = optional_param('view', null, PARAM_INT);
$pageparams->curdate = optional_param('curdate', null, PARAM_INT);
$pageparams->groupby = optional_param('groupby', 'course', PARAM_ALPHA);
$pageparams->sesscourses = optional_param('sesscourses', 'current', PARAM_ALPHA);
$cm = get_coursemodule_from_id('attendance', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
@ -60,14 +63,6 @@ if (!$pageparams->studentid) {
}
}
$PAGE->set_url($att->url_view());
$PAGE->set_title($course->shortname. ": ".$att->name);
$PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(true);
$PAGE->navbar->add(get_string('attendancereport', 'attendance'));
$output = $PAGE->get_renderer('mod_attendance');
if (isset($pageparams->studentid) && $USER->id != $pageparams->studentid) {
// Only users with proper permissions should be able to see any user's individual report.
require_capability('mod/attendance:viewreports', $context);
@ -77,6 +72,36 @@ if (isset($pageparams->studentid) && $USER->id != $pageparams->studentid) {
$userid = $USER->id;
}
$url = $att->url_view($pageparams->get_significant_params());
$PAGE->set_url($url);
$buttons = '';
$capabilities = array('mod/attendance:takeattendances', 'mod/attendance:changeattendances');
if (has_any_capability($capabilities, $context) &&
$pageparams->mode == mod_attendance_view_page_params::MODE_ALL_SESSIONS) {
if (!isset($USER->attendanceediting)) {
$USER->attendanceediting = false;
}
if (($edit == 1) and confirm_sesskey()) {
$USER->attendanceediting = true;
} else if ($edit == 0 and confirm_sesskey()) {
$USER->attendanceediting = false;
}
if ($USER->attendanceediting) {
$options['edit'] = 0;
$string = get_string('turneditingoff');
} else {
$options['edit'] = 1;
$string = get_string('turneditingon');
}
$options['sesskey'] = sesskey();
$button = new single_button(new moodle_url($PAGE->url, $options), $string, 'post');
$PAGE->set_button($OUTPUT->render($button));
}
$userdata = new attendance_user_data($att, $userid);
// Create url for link in log screen.
@ -87,21 +112,43 @@ $filterparams = array(
'enddate' => $userdata->pageparams->enddate
);
$params = array_merge($userdata->pageparams->get_significant_params(), $filterparams);
$header = new mod_attendance_header($att);
if (empty($userdata->pageparams->studentid)) {
$relateduserid = $USER->id;
} else {
$relateduserid = $userdata->pageparams->studentid;
}
// Trigger viewed event.
$event = \mod_attendance\event\session_report_viewed::create(array(
'relateduserid' => $relateduserid,
'context' => $context,
'other' => $params));
$event->add_record_snapshot('course_modules', $cm);
$event->trigger();
if (($formdata = data_submitted()) && confirm_sesskey() && $edit == -1) {
$userdata->take_sessions_from_form_data($formdata);
$header = new mod_attendance_header($att);
// Trigger updated event.
$event = \mod_attendance\event\session_report_updated::create(array(
'relateduserid' => $relateduserid,
'context' => $context,
'other' => $params));
$event->add_record_snapshot('course_modules', $cm);
$event->trigger();
redirect($url, get_string('attendancesuccess', 'attendance'));
} else {
// Trigger viewed event.
$event = \mod_attendance\event\session_report_viewed::create(array(
'relateduserid' => $relateduserid,
'context' => $context,
'other' => $params));
$event->add_record_snapshot('course_modules', $cm);
$event->trigger();
}
$PAGE->set_title($course->shortname. ": ".$att->name);
$PAGE->set_heading($course->fullname);
$PAGE->set_cacheable(true);
$PAGE->navbar->add(get_string('attendancereport', 'attendance'));
$output = $PAGE->get_renderer('mod_attendance');
echo $output->header();

Loading…
Cancel
Save