From 1d754e373677dfee7695a77837aed9157add78dd Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Fri, 31 May 2019 15:04:21 +1200 Subject: [PATCH 01/13] Add (inaccessible) report on all sessions. --- classes/view_page_params.php | 3 + locallib.php | 56 ++++++++++++ renderables.php | 36 ++++++++ renderer.php | 163 +++++++++++++++++++++++++++++++++++ 4 files changed, 258 insertions(+) diff --git a/classes/view_page_params.php b/classes/view_page_params.php index c0f98a7..d1c04ac 100644 --- a/classes/view_page_params.php +++ b/classes/view_page_params.php @@ -37,6 +37,9 @@ 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; diff --git a/locallib.php b/locallib.php index c40cd53..3a8aef4 100644 --- a/locallib.php +++ b/locallib.php @@ -109,6 +109,62 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) { return $statusname; } +/** + * Get full filtered log. + * @param int $userid + * @return array + */ +function get_user_sessions_log_full($userid) { + 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'); + + // 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 + ORDER BY c.fullname ASC, att.name ASC, att.id ASC, ats.sessdate ASC"; + + $params = array( + 'uid' => $userid, + 'uid1' => $userid, + ); + $params = array_merge($params, $uparams); + $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. * diff --git a/renderables.php b/renderables.php index 1f4fdab..c4cee51 100644 --- a/renderables.php +++ b/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 = get_user_sessions_log_full($userid); + + 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(); diff --git a/renderer.php b/renderer.php index 1a19c86..066c8da 100644 --- a/renderer.php +++ b/renderer.php @@ -1022,6 +1022,9 @@ 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) { + $o .= $this->render_attendance_filter_controls($userdata->filtercontrols); + $o .= $this->construct_user_allsessions_log($userdata); } else { $table = new html_table(); $table->head = array(get_string('course'), @@ -1226,6 +1229,166 @@ 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 $OUTPUT, $USER; + + $shortform = false; + if ($USER->id == $userdata->user->id) { + // This is a user viewing their own stuff - hide non-relevant columns. + $shortform = true; + } + + $context = context_module::instance($userdata->filtercontrols->cm->id); + + $table = new html_table(); + $table->attributes['class'] = 'generaltable attwidth boxaligncenter'; + $table->head = array(); + $table->align = array(); + $table->size = array(); + $table->colclasses = array(); + + $table->head[] = get_string('course'); + $table->align[] = 'left'; + $table->colclasses[] = 'colcourse'; + + $table->head[] = get_string('pluginname', 'mod_attendance'); + $table->align[] = 'left'; + $table->colclasses[] = 'colcourse'; + $table->size[] = '*'; + + // use "session" instead + //$table->head[] = get_string('description', 'attendance'); + $table->head[] = get_string('session', 'attendance'); + $table->align[] = 'left'; + $table->colclasses[] = 'desccol'; + $table->size[] = '*'; + + if (!$shortform) { + $table->head[] = get_string('sessiontypeshort', 'attendance'); + $table->align[] = ''; + $table->size[] = '1px'; + $table->colclasses[] = ''; + } + + $table->head[] = get_string('date'); + $table->align[] = 'left'; + $table->colclasses[] = 'datecol'; + $table->size[] = '1px'; + + $table->head[] = get_string('status', 'attendance'); + $table->align[] = 'center'; + $table->colclasses[] = 'statuscol'; + $table->size[] = '*'; + + $table->head[] = get_string('points', 'attendance'); + $table->align[] = 'center'; + $table->colclasses[] = 'pointscol'; + $table->size[] = '1px'; + + $table->head[] = get_string('remarks', 'attendance'); + $table->align[] = 'center'; + $table->colclasses[] = 'remarkscol'; + $table->size[] = '*'; + + if (has_capability('mod/attendance:takeattendances', $context)) { + $table->head[] = get_string('action'); + $table->align[] = ''; + $table->colclasses[] = 'actioncol'; + $table->size[] = ''; + } + + $statusmaxpoints = array(); + foreach ($userdata->statuses as $attid => $attstatuses) { + $statusmaxpoints[$attid] = attendance_get_statusset_maxpoints($attstatuses); + } + + $i = 0; + foreach ($userdata->sessionslog as $sess) { + $i++; + + $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; + + $row = new html_table_row(); + + // course / activity / session / type / date / status / points / remarks / action + // + $courseurl = new moodle_url('/course/view.php', array('id' => $sess->courseid)); + $row->cells[] = html_writer::link($courseurl, $sess->cname); + + $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); + + $row->cells[] = $sess->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); + } + + $row->cells[] = userdate($sess->sessdate, get_string('strftimedmyw', 'attendance')) . + " ". $this->construct_time($sess->sessdate, $sess->duration); + + 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[] = ''; + } + } + + if (has_capability('mod/attendance:takeattendances', $context)) { + $params = array('id' => $userdata->filtercontrols->cm->id, + 'sessionid' => $sess->id, + 'grouptype' => $sess->groupid); + $url = new moodle_url('/mod/attendance/take.php', $params); + $icon = $OUTPUT->pix_icon('redo', get_string('changeattendance', 'attendance'), 'attendance'); + $row->cells[] = html_writer::link($url, $icon); + } + + $table->data[] = $row; + } + + return html_writer::table($table); + } + /** * Construct time for display. * From fe05aa05f03cef2148a16e3fca5c0e6a97e5499c Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Thu, 13 Jun 2019 10:08:15 +1200 Subject: [PATCH 02/13] Initial working version of allsessions report --- classes/view_page_params.php | 12 + lang/en/attendance.php | 10 + locallib.php | 44 ++- renderables.php | 2 +- renderer.php | 722 ++++++++++++++++++++++++++++++----- styles.css | 21 +- view.php | 4 +- 7 files changed, 711 insertions(+), 104 deletions(-) diff --git a/classes/view_page_params.php b/classes/view_page_params.php index d1c04ac..fd5d696 100644 --- a/classes/view_page_params.php +++ b/classes/view_page_params.php @@ -46,6 +46,12 @@ class mod_attendance_view_page_params extends mod_attendance_page_with_filter_co /** @var string */ public $mode; + /** @var string */ + public $groupby; + + /** @var string */ + public $sesscourses; + /** * mod_attendance_view_page_params constructor. */ @@ -67,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; } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index e7eea16..d2bc83c 100644 --- a/lang/en/attendance.php +++ b/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'; @@ -250,6 +251,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. @@ -487,7 +489,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 +538,7 @@ $string['statusset'] = 'Status set {$a}'; $string['statussetsettings'] = 'Status set'; $string['statusunselected'] = 'unselected'; $string['strftimedm'] = '%b %d'; +$string['strftimedmw'] = '%a %b %d'; $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'] = '%a %d %b %Y'; @@ -583,6 +591,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 +625,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'; \ No newline at end of file diff --git a/locallib.php b/locallib.php index 3a8aef4..3c44506 100644 --- a/locallib.php +++ b/locallib.php @@ -114,13 +114,49 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) { * @param int $userid * @return array */ -function get_user_sessions_log_full($userid) { +function 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'); + // If we base on structure->get_user_filtered_sessions_log_extended: + /* if ($this->pageparams->startdate && $this->pageparams->enddate) { */ + /* $where = "ats.sessdate >= :csdate AND */ + /* ats.sessdate >= :sdate AND ats.sessdate < :edate"; */ + /* } else { */ + /* $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; */ + /* } */ + + $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 @@ -141,13 +177,17 @@ function get_user_sessions_log_full($userid) { 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 - ORDER BY c.fullname ASC, att.name ASC, att.id ASC, ats.sessdate ASC"; + 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) { diff --git a/renderables.php b/renderables.php index c4cee51..1271006 100644 --- a/renderables.php +++ b/renderables.php @@ -516,7 +516,7 @@ class attendance_user_data implements renderable { $this->filtercontrols = new attendance_filter_controls($att); } - $this->sessionslog = get_user_sessions_log_full($userid); + $this->sessionslog = get_user_sessions_log_full($userid, $this->pageparams); foreach ($this->sessionslog as $sessid => $sess) { $this->sessionslog[$sessid]->cmid = $this->coursesatts[$sess->attendanceid]->cmid; diff --git a/renderer.php b/renderer.php index 066c8da..926b2c3 100644 --- a/renderer.php +++ b/renderer.php @@ -56,21 +56,41 @@ 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 ($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(); - $filtertable->data[0][] = $this->render_view_controls($fcontrols); + $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[] = $row; $o = html_writer::table($filtertable); - $o = $this->output->container($o, 'attfiltercontrols'); + $o = $this->output->container($o, $classes); return $o; } @@ -198,6 +218,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. * @@ -960,7 +1033,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 +1067,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); @@ -1023,8 +1100,21 @@ class mod_attendance_renderer extends plugin_renderer_base { $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 .= $this->construct_user_allsessions_log($userdata); + $o .= html_writer::end_div(); + $o .= html_writer::end_div(); + $o .= $allsessions->detail; } else { $table = new html_table(); $table->head = array(get_string('course'), @@ -1238,6 +1328,8 @@ class mod_attendance_renderer extends plugin_renderer_base { private function construct_user_allsessions_log(attendance_user_data $userdata) { global $OUTPUT, $USER; + $allsessions = new stdClass(); + $shortform = false; if ($USER->id == $userdata->user->id) { // This is a user viewing their own stuff - hide non-relevant columns. @@ -1246,21 +1338,69 @@ class mod_attendance_renderer extends plugin_renderer_base { $context = context_module::instance($userdata->filtercontrols->cm->id); + $groupby = $userdata->pageparams->groupby; + //$groupby = 'course'; + //$groupby = 'activity'; + //$groupby = 'date'; + $table = new html_table(); - $table->attributes['class'] = 'generaltable attwidth boxaligncenter'; + $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('course'); - $table->align[] = 'left'; - $table->colclasses[] = 'colcourse'; + $table->head[] = get_string('date'); + $table->align[] = 'left'; + $table->colclasses[] = 'datecol'; + $table->size[] = '1px'; + $colcount++; - $table->head[] = get_string('pluginname', 'mod_attendance'); - $table->align[] = 'left'; - $table->colclasses[] = 'colcourse'; - $table->size[] = '*'; + $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 //$table->head[] = get_string('description', 'attendance'); @@ -1268,125 +1408,509 @@ class mod_attendance_renderer extends plugin_renderer_base { $table->align[] = 'left'; $table->colclasses[] = 'desccol'; $table->size[] = '*'; + $colcount++; if (!$shortform) { $table->head[] = get_string('sessiontypeshort', 'attendance'); $table->align[] = ''; $table->size[] = '1px'; $table->colclasses[] = ''; + $colcount++; } - $table->head[] = get_string('date'); - $table->align[] = 'left'; - $table->colclasses[] = 'datecol'; - $table->size[] = '1px'; - - $table->head[] = get_string('status', 'attendance'); - $table->align[] = 'center'; - $table->colclasses[] = 'statuscol'; - $table->size[] = '*'; + if (!empty($USER->editing)) { + $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'; + $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('remarks', 'attendance'); - $table->align[] = 'center'; - $table->colclasses[] = 'remarkscol'; - $table->size[] = '*'; + $table->head[] = get_string('points', 'attendance'); + $table->align[] = 'center'; + $table->colclasses[] = 'pointscol'; + $table->size[] = '1px'; + $colcount++; + $summarywidth++; - if (has_capability('mod/attendance:takeattendances', $context)) { - $table->head[] = get_string('action'); - $table->align[] = ''; - $table->colclasses[] = 'actioncol'; - $table->size[] = ''; + $table->head[] = get_string('remarks', 'attendance'); + $table->align[] = 'center'; + $table->colclasses[] = 'remarkscol'; + $table->size[] = '*'; + $colcount++; + $summarywidth++; } + /* if (has_capability('mod/attendance:takeattendances', $context)) { */ + /* $table->head[] = get_string('action'); */ + /* $table->align[] = ''; */ + /* $table->colclasses[] = 'actioncol'; */ + /* $table->size[] = ''; */ + /* $colcount++; */ + /* $summarywidth++; */ + /* } */ + $statusmaxpoints = array(); foreach ($userdata->statuses as $attid => $attstatuses) { $statusmaxpoints[$attid] = attendance_get_statusset_maxpoints($attstatuses); } - $i = 0; - foreach ($userdata->sessionslog as $sess) { - $i++; - - $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; + $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. + if ($sess->id == 163) { + error_log("STATUSSET: ".print_r($sess->statusset, true)); + } + 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); + } + } + } + // 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); + } + } + } + // 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); + } + } + } + // 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']; + $summary = ''; + $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(); */ + /* $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); - // course / activity / session / type / date / status / points / remarks / action - // - $courseurl = new moodle_url('/course/view.php', array('id' => $sess->courseid)); - $row->cells[] = html_writer::link($courseurl, $sess->cname); + $lastgroup = array(null, null); + foreach ($groups as $group) { - $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); + $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; - $row->cells[] = $sess->description; + // For use in headings etc. + $sess = $group[0]; - if (!$shortform) { - if ($sess->groupid) { - $sessiontypeshort = get_string('group') . ': ' . $userdata->groups[$sess->courseid][$sess->groupid]->name; + if ($groupby === 'date') { + $row = new html_table_row(); + $row->attributes['class'] = 'grouper'; + $cell = new html_table_cell(); + $cell->rowspan = sizeof($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 { - $sessiontypeshort = get_string('commonsession', 'attendance'); + $summary = get_string('points', 'attendance') . ": " . $points . "/" . $maxpoints; } - $row->cells[] = html_writer::tag('nobr', $sessiontypeshort); + $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 = sizeof($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 += sizeof($group) + 2; + } + $row = new html_table_row(); + $row->attributes['class'] = 'grouper'; + $cell = new html_table_cell(); + $cell->rowspan = sizeof($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; } - $row->cells[] = userdate($sess->sessdate, get_string('strftimedmyw', 'attendance')) . - " ". $this->construct_time($sess->sessdate, $sess->duration); + // Now iterate over sessions in group... - 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. + foreach($group as $sess) { + $row = new html_table_row(); + + // partialdate? / course? / activity? / date? / session / type / status / points / remarks / action + // + + // 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); - $url = new moodle_url('/mod/attendance/attendance.php', + $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->editing)) { + // TODO! + } 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[] = ''; + $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[] = ''; + } + } } - } - if (has_capability('mod/attendance:takeattendances', $context)) { - $params = array('id' => $userdata->filtercontrols->cm->id, - 'sessionid' => $sess->id, - 'grouptype' => $sess->groupid); - $url = new moodle_url('/mod/attendance/take.php', $params); - $icon = $OUTPUT->pix_icon('redo', get_string('changeattendance', 'attendance'), 'attendance'); - $row->cells[] = html_writer::link($url, $icon); + $table->data[] = $row; } - - $table->data[] = $row; } - return html_writer::table($table); + $allsessions->detail = html_writer::table($table); + return $allsessions; } /** diff --git a/styles.css b/styles.css index 6b1a0d8..9432581 100644 --- a/styles.css +++ b/styles.css @@ -4,6 +4,7 @@ margin-left: 2px; margin-right: 2px; padding: 5px; + display: inline-block; } .path-mod-attendance .attcurbtn { @@ -16,7 +17,7 @@ margin-bottom: 10px; margin-left: auto; margin-right: auto; - width: 90%; +/* width: 90%; */ } .path-mod-attendance .attfiltercontrols #currentdate { @@ -105,9 +106,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: #eeeeee; +} +.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; diff --git a/view.php b/view.php index dc5da51..748c3ed 100644 --- a/view.php +++ b/view.php @@ -33,6 +33,8 @@ $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,7 +62,7 @@ if (!$pageparams->studentid) { } } -$PAGE->set_url($att->url_view()); +$PAGE->set_url($att->url_view($pageparams->get_significant_params())); $PAGE->set_title($course->shortname. ": ".$att->name); $PAGE->set_heading($course->fullname); $PAGE->set_cacheable(true); From eb85b45dea3cb8a21aca58aaa31194f016ee384a Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Wed, 10 Jul 2019 11:15:24 +1200 Subject: [PATCH 03/13] Fixes for bugs, and other tweaks to keep travis quiet. --- locallib.php | 8 -------- renderer.php | 25 ++++++++++--------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/locallib.php b/locallib.php index 3c44506..a14c58b 100644 --- a/locallib.php +++ b/locallib.php @@ -121,14 +121,6 @@ function get_user_sessions_log_full($userid, $pageparams) { $usercourses = enrol_get_users_courses($userid); list($usql, $uparams) = $DB->get_in_or_equal(array_keys($usercourses), SQL_PARAMS_NAMED, 'cid0'); - // If we base on structure->get_user_filtered_sessions_log_extended: - /* if ($this->pageparams->startdate && $this->pageparams->enddate) { */ - /* $where = "ats.sessdate >= :csdate AND */ - /* ats.sessdate >= :sdate AND ats.sessdate < :edate"; */ - /* } else { */ - /* $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; */ - /* } */ - $coursesql = "(1 = 1)"; $courseparams = array(); $now = time(); diff --git a/renderer.php b/renderer.php index 926b2c3..4f3fda7 100644 --- a/renderer.php +++ b/renderer.php @@ -62,7 +62,8 @@ class mod_attendance_renderer extends plugin_renderer_base { $filtertable->width = '100%'; $filtertable->align = array('left', 'center', 'right', 'right'); - if ($fcontrols->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) { + if (property_exists($fcontrols->pageparams, 'mode') && + $fcontrols->pageparams->mode === mod_attendance_view_page_params::MODE_ALL_SESSIONS) { $classes .= ' float-right'; $row = array(); @@ -1338,10 +1339,8 @@ class mod_attendance_renderer extends plugin_renderer_base { $context = context_module::instance($userdata->filtercontrols->cm->id); + // 'course', 'activity', 'date' $groupby = $userdata->pageparams->groupby; - //$groupby = 'course'; - //$groupby = 'activity'; - //$groupby = 'date'; $table = new html_table(); $table->attributes['class'] = 'generaltable attwidth boxaligncenter allsessions'; @@ -1402,8 +1401,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $colcount++; } - // use "session" instead - //$table->head[] = get_string('description', 'attendance'); + // use "session" instead of "description" $table->head[] = get_string('session', 'attendance'); $table->align[] = 'left'; $table->colclasses[] = 'desccol'; @@ -1512,9 +1510,6 @@ class mod_attendance_renderer extends plugin_renderer_base { // // A bit yucky because we can't tell whether we've seen statusset before, and // we usually will have, so much wasted spinning. - if ($sess->id == 163) { - error_log("STATUSSET: ".print_r($sess->statusset, true)); - } foreach ($userdata->statuses[$sess->attendanceid] as $attstatus) { if ($attstatus->setnumber === $sess->statusset) { if (!array_key_exists($attstatus->acronym, $stats['date'][$weekformat]['statuses'])) { @@ -1680,7 +1675,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row = new html_table_row(); $row->attributes['class'] = 'grouper'; $cell = new html_table_cell(); - $cell->rowspan = sizeof($group) + 2; + $cell->rowspan = count($group) + 2; $row->cells[] = $cell; $week = date("W", $sess->sessdate); $year = date("Y", $sess->sessdate); @@ -1724,7 +1719,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row = new html_table_row(); $row->attributes['class'] = 'grouper'; $cell = new html_table_cell(); - $cell->rowspan = sizeof($group) + 2; + $cell->rowspan = count($group) + 2; if ($groupby === 'activity') { $headcell = $cell; // keep ref to be able to adjust rowspan later $cell->rowspan += 2; @@ -1766,12 +1761,12 @@ class mod_attendance_renderer extends plugin_renderer_base { } if ($groupby === 'activity') { if ($sess->courseid === $lastgroup[0]) { - $headcell->rowspan += sizeof($group) + 2; + $headcell->rowspan += count($group) + 2; } $row = new html_table_row(); $row->attributes['class'] = 'grouper'; $cell = new html_table_cell(); - $cell->rowspan = sizeof($group) + 2; + $cell->rowspan = count($group) + 2; $row->cells[] = $cell; $attendanceurl = new moodle_url('/mod/attendance/view.php', array('id' => $sess->cmid, 'studentid' => $userdata->user->id, @@ -1812,7 +1807,7 @@ class mod_attendance_renderer extends plugin_renderer_base { // Now iterate over sessions in group... - foreach($group as $sess) { + foreach ($group as $sess) { $row = new html_table_row(); // partialdate? / course? / activity? / date? / session / type / status / points / remarks / action @@ -1868,7 +1863,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } if (!empty($USER->editing)) { - // TODO! + // TODO: add ability to edit attendance here } else { if (!empty($sess->statusid)) { $status = $userdata->statuses[$sess->attendanceid][$sess->statusid]; From 014f6110e6a13d4748d6a009c844a9d05eb8af0e Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Thu, 11 Jul 2019 11:27:46 +1200 Subject: [PATCH 04/13] Keeping travis happy. --- locallib.php | 1 + 1 file changed, 1 insertion(+) diff --git a/locallib.php b/locallib.php index a14c58b..cba6927 100644 --- a/locallib.php +++ b/locallib.php @@ -112,6 +112,7 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) { /** * Get full filtered log. * @param int $userid + * @param stdClass $pageparams * @return array */ function get_user_sessions_log_full($userid, $pageparams) { From 04a4a8f1e1c2af04a3b272e6a4b09b57e93a231d Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Wed, 24 Jul 2019 10:07:16 +1200 Subject: [PATCH 05/13] Make update from student's 'allsessions' attendance report work. --- locallib.php | 50 +++++++++++++++- renderables.php | 141 +++++++++++++++++++++++++++++++++++++++++-- renderer.php | 156 ++++++++++++++++++++++++++++++++++++++++++++---- view.php | 24 +++++--- 4 files changed, 347 insertions(+), 24 deletions(-) diff --git a/locallib.php b/locallib.php index cba6927..ff8bc1e 100644 --- a/locallib.php +++ b/locallib.php @@ -115,7 +115,7 @@ function attendance_get_setname($attid, $statusset, $includevalues = true) { * @param stdClass $pageparams * @return array */ -function get_user_sessions_log_full($userid, $pageparams) { +function attendance_get_user_sessions_log_full($userid, $pageparams) { global $DB; // All taken sessions (including previous groups). @@ -364,6 +364,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 * diff --git a/renderables.php b/renderables.php index 1271006..90ce3ce 100644 --- a/renderables.php +++ b/renderables.php @@ -516,7 +516,7 @@ class attendance_user_data implements renderable { $this->filtercontrols = new attendance_filter_controls($att); } - $this->sessionslog = get_user_sessions_log_full($userid, $this->pageparams); + $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; @@ -546,12 +546,145 @@ 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. + + // XXX - replace this, see below + //$statuses = implode(',', array_keys( (array)$this->get_statuses() )); + $now = time(); + $sesslog = array(); + $formdata = (array)$formdata; + $dbstudlogs = array(); + $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') { + $formlog = array(); + $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? + error_log("User without capability tried to take attendance for context, cmid:", $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); + } + } + } + + // Create url for link in log screen. + $params = $this->pageparams->get_significant_params(); + // XXX - TODO + // Waiting for event for viewing user report(s) before creating derived event for editing. + /* $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_sessions', $session); */ + /* $event->trigger(); */ } + } /** diff --git a/renderer.php b/renderer.php index 4f3fda7..8f365dd 100644 --- a/renderer.php +++ b/renderer.php @@ -1000,6 +1000,71 @@ 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(); + 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['text'][] = $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. * @@ -1453,15 +1518,6 @@ class mod_attendance_renderer extends plugin_renderer_base { $summarywidth++; } - /* if (has_capability('mod/attendance:takeattendances', $context)) { */ - /* $table->head[] = get_string('action'); */ - /* $table->align[] = ''; */ - /* $table->colclasses[] = 'actioncol'; */ - /* $table->size[] = ''; */ - /* $colcount++; */ - /* $summarywidth++; */ - /* } */ - $statusmaxpoints = array(); foreach ($userdata->statuses as $attid => $attstatuses) { $statusmaxpoints[$attid] = attendance_get_statusset_maxpoints($attstatuses); @@ -1863,7 +1919,61 @@ class mod_attendance_renderer extends plugin_renderer_base { } if (!empty($USER->editing)) { - // TODO: add ability to edit attendance here + $context = context_module::instance($sess->cmid); + if (has_capability('mod/attendance:takeattendances', $context)) { + // TODO: add ability to edit attendance here + $celltext = ''; + + // 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); + $cell->colspan = 2; + $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]; @@ -1904,7 +2014,31 @@ class mod_attendance_renderer extends plugin_renderer_base { } } - $allsessions->detail = html_writer::table($table); + if (!empty($USER->editing)) { + $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->editing)) { + $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; } diff --git a/view.php b/view.php index 748c3ed..40aafbf 100644 --- a/view.php +++ b/view.php @@ -62,14 +62,6 @@ if (!$pageparams->studentid) { } } -$PAGE->set_url($att->url_view($pageparams->get_significant_params())); -$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); @@ -79,6 +71,9 @@ if (isset($pageparams->studentid) && $USER->id != $pageparams->studentid) { $userid = $USER->id; } +$url = $att->url_view($pageparams->get_significant_params()); +$PAGE->set_url($url); + $userdata = new attendance_user_data($att, $userid); // Create url for link in log screen. @@ -105,6 +100,19 @@ $event->trigger(); $header = new mod_attendance_header($att); +if (($formdata = data_submitted()) && confirm_sesskey()) { + $userdata->take_sessions_from_form_data($formdata); + + redirect($url, get_string('attendancesuccess', 'attendance')); +} + +$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(); echo $output->render($header); From 19bb08468607a9e0c11dfefdf5150f471a5e97db Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Fri, 9 Aug 2019 14:52:52 +1200 Subject: [PATCH 06/13] Add updated event for per-student reports from view.php, and use it, moving from internal viewed event to the one Dan integrated at the same time. Need to re-look at event validation and possibly-optional params per Dan's changes to mode param. --- classes/event/session_report_updated.php | 61 ++++++++++++++++++++++++ classes/event/session_report_viewed.php | 12 +++-- classes/structure.php | 2 +- lang/en/attendance.php | 1 + renderables.php | 15 ------ view.php | 31 ++++++++---- 6 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 classes/event/session_report_updated.php diff --git a/classes/event/session_report_updated.php b/classes/event/session_report_updated.php new file mode 100644 index 0000000..408a369 --- /dev/null +++ b/classes/event/session_report_updated.php @@ -0,0 +1,61 @@ +. + +/** + * 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 + * @since Moodle 2.7 + * @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'); + } +} diff --git a/classes/event/session_report_viewed.php b/classes/event/session_report_viewed.php index ba3cf0a..4fe8940 100644 --- a/classes/event/session_report_viewed.php +++ b/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; } @@ -80,6 +80,8 @@ class session_report_viewed extends \core\event\base { 'studentid' => $this->relateduserid, 'mode' => $mode, 'view' => $this->other['view'], + 'groupby' => $this->other['groupby'], + 'sesscourses' => $this->other['sesscourses'], 'curdate' => $this->other['curdate'])); } @@ -89,7 +91,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 +121,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(); } diff --git a/classes/structure.php b/classes/structure.php index a767318..c5d349f 100644 --- a/classes/structure.php +++ b/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) { diff --git a/lang/en/attendance.php b/lang/en/attendance.php index d2bc83c..19b3bcb 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -236,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'; diff --git a/renderables.php b/renderables.php index 90ce3ce..d324fdd 100644 --- a/renderables.php +++ b/renderables.php @@ -571,8 +571,6 @@ class attendance_user_data implements renderable { // 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. - // XXX - replace this, see below - //$statuses = implode(',', array_keys( (array)$this->get_statuses() )); $now = time(); $sesslog = array(); $formdata = (array)$formdata; @@ -671,20 +669,7 @@ class attendance_user_data implements renderable { } } } - - // Create url for link in log screen. - $params = $this->pageparams->get_significant_params(); - // XXX - TODO - // Waiting for event for viewing user report(s) before creating derived event for editing. - /* $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_sessions', $session); */ - /* $event->trigger(); */ } - } /** diff --git a/view.php b/view.php index 40aafbf..efed400 100644 --- a/view.php +++ b/view.php @@ -84,27 +84,38 @@ $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(); - -$header = new mod_attendance_header($att); - if (($formdata = data_submitted()) && confirm_sesskey()) { $userdata->take_sessions_from_form_data($formdata); + // 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->add_record_snapshot('user', $); + $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); From e491e79c664b307a94e7c4e81a6779067c169fc0 Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Wed, 8 Apr 2020 16:32:21 +1200 Subject: [PATCH 07/13] Fix session_report_viewed get_url when groupby missing. --- classes/event/session_report_viewed.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/event/session_report_viewed.php b/classes/event/session_report_viewed.php index 4fe8940..b315269 100644 --- a/classes/event/session_report_viewed.php +++ b/classes/event/session_report_viewed.php @@ -74,13 +74,14 @@ class session_report_viewed extends \core\event\base { * @return \moodle_url */ public function get_url() { - // Mode is optional. + // Mode and groupby are optional. $mode = empty($this->other['mode']) ? "" : $this->other['mode']; + $groupby = empty($this->other['groupby']) ? "" : $this->other['groupby']; return new \moodle_url('/mod/attendance/view.php', array('id' => $this->contextinstanceid, 'studentid' => $this->relateduserid, 'mode' => $mode, 'view' => $this->other['view'], - 'groupby' => $this->other['groupby'], + 'groupby' => $groupby, 'sesscourses' => $this->other['sesscourses'], 'curdate' => $this->other['curdate'])); } From 5f9d8e4cbace060b27c87d46534fcfdd896bd029 Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Wed, 8 Apr 2020 16:32:54 +1200 Subject: [PATCH 08/13] Remove extraneous whitespace. --- renderables.php | 2 +- renderer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/renderables.php b/renderables.php index d324fdd..250d685 100644 --- a/renderables.php +++ b/renderables.php @@ -633,7 +633,7 @@ class attendance_user_data implements renderable { 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); diff --git a/renderer.php b/renderer.php index 8f365dd..9bfde85 100644 --- a/renderer.php +++ b/renderer.php @@ -1973,7 +1973,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = $sess->remarks; } } - + } else { if (!empty($sess->statusid)) { $status = $userdata->statuses[$sess->attendanceid][$sess->statusid]; From 303a97c294e982b8fc2f809c295bf8f28ac03497 Mon Sep 17 00:00:00 2001 From: Nick Phillips Date: Thu, 2 Jul 2020 16:24:00 +1200 Subject: [PATCH 09/13] Fixes for squished allsessions report in edit mode, lang strings, sesscourses in session_report_viewed optional. --- classes/event/session_report_viewed.php | 5 +++-- lang/en/attendance.php | 4 ++-- styles.css | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/classes/event/session_report_viewed.php b/classes/event/session_report_viewed.php index b315269..c4946f1 100644 --- a/classes/event/session_report_viewed.php +++ b/classes/event/session_report_viewed.php @@ -74,15 +74,16 @@ class session_report_viewed extends \core\event\base { * @return \moodle_url */ public function get_url() { - // Mode and groupby are 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' => $this->other['sesscourses'], + 'sesscourses' => $sesscourses, 'curdate' => $this->other['curdate'])); } diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 19b3bcb..018fb28 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -491,7 +491,7 @@ $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['sessionsbyactivity'] = 'Attendance instance'; $string['sessionsbycourse'] = 'Course'; $string['sessionsbydate'] = 'Week'; $string['sessionscompleted'] = 'Taken sessions'; @@ -626,6 +626,6 @@ $string['warnings'] = 'Warnings set'; $string['warningthreshold'] = 'Warning threshold'; $string['warningupdated'] = 'Updated warnings'; $string['week'] = 'week(s)'; -$string['weekcommencing'] = 'Week Commencing'; +$string['weekcommencing'] = 'Week commencing'; $string['weeks'] = 'Weeks'; $string['youcantdo'] = 'You can\'t do anything'; \ No newline at end of file diff --git a/styles.css b/styles.css index 9432581..29446a6 100644 --- a/styles.css +++ b/styles.css @@ -55,6 +55,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%; From 22a6015bd5f35b1ef17166ac863e193dcaa2f676 Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Wed, 30 Sep 2020 14:52:59 +1300 Subject: [PATCH 10/13] Tidy up PR from Nick coding guideline stuff/renderers/using own editing button --- classes/event/session_report_updated.php | 3 +- lang/en/attendance.php | 1 + locallib.php | 6 +- renderables.php | 15 ++-- renderer.php | 88 ++++++++++-------------- styles.css | 3 +- view.php | 38 ++++++++-- 7 files changed, 83 insertions(+), 71 deletions(-) diff --git a/classes/event/session_report_updated.php b/classes/event/session_report_updated.php index 408a369..1c649ec 100644 --- a/classes/event/session_report_updated.php +++ b/classes/event/session_report_updated.php @@ -35,7 +35,6 @@ defined('MOODLE_INTERNAL') || die(); * string mode Mode of the report updated. * } * @package mod_attendance - * @since Moodle 2.7 * @copyright 2013 onwards Dan Marsden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -47,7 +46,7 @@ class session_report_updated extends \mod_attendance\event\session_report_viewed protected function init() { $this->data['crud'] = 'u'; $this->data['edulevel'] = self::LEVEL_TEACHING; - // objecttable and objectid can't be meaningfully specified + // Objecttable and objectid can't be meaningfully specified. } /** diff --git a/lang/en/attendance.php b/lang/en/attendance.php index 018fb28..7a5b5ef 100644 --- a/lang/en/attendance.php +++ b/lang/en/attendance.php @@ -335,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.'; diff --git a/locallib.php b/locallib.php index ff8bc1e..7764bef 100644 --- a/locallib.php +++ b/locallib.php @@ -145,16 +145,14 @@ function attendance_get_user_sessions_log_full($userid, $pageparams) { if ($pageparams->groupby === 'date') { $ordersql = "ats.sessdate ASC, c.fullname ASC, att.name ASC, att.id ASC"; - } - else { + } 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 - // + // 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, diff --git a/renderables.php b/renderables.php index 250d685..6246025 100644 --- a/renderables.php +++ b/renderables.php @@ -574,14 +574,12 @@ class attendance_user_data implements renderable { $now = time(); $sesslog = array(); $formdata = (array)$formdata; - $dbstudlogs = array(); $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') { - $formlog = array(); $parts = explode('sess', substr($key, 7)); $stid = $parts[0]; if (!(is_numeric($stid))) { // Sanity check on $stid. @@ -596,13 +594,15 @@ class attendance_user_data implements renderable { $context = context_module::instance($dbsession->cmid); if (!has_capability('mod/attendance:takeattendances', $context)) { // How do we tell user about this? - error_log("User without capability tried to take attendance for context, cmid:", $dbsession->cmid); + \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; }); + $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])) { @@ -624,8 +624,9 @@ class attendance_user_data implements renderable { $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) { + $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. @@ -663,7 +664,7 @@ class attendance_user_data implements renderable { if (!empty($updateatts)) { $attendancegrade = $DB->get_records_list('attendance', 'id', array_keys($updateatts), '', 'id, grade'); - foreach($updateatts as $attid => $updateusers) { + foreach ($updateatts as $attid => $updateusers) { if ($attendancegrade[$attid] != 0) { attendance_update_users_grades_by_id($attid, $grade, $updateusers); } diff --git a/renderer.php b/renderer.php index 9bfde85..759c091 100644 --- a/renderer.php +++ b/renderer.php @@ -1009,6 +1009,7 @@ class mod_attendance_renderer extends plugin_renderer_base { */ 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; @@ -1053,7 +1054,7 @@ class mod_attendance_renderer extends plugin_renderer_base { if ($takedata->pageparams->viewmode == mod_attendance_take_page_params::SORTED_GRID) { $input = html_writer::empty_tag('br').$input; } - $celldata['text'][] = $input; + $celldata['remarks'] = $input; if ($user->enrolmentstart > $takedata->sessioninfo->sessdate + $takedata->sessioninfo->duration) { $celldata['warning'] = get_string('enrolmentstart', 'attendance', @@ -1392,7 +1393,7 @@ class mod_attendance_renderer extends plugin_renderer_base { * @return string */ private function construct_user_allsessions_log(attendance_user_data $userdata) { - global $OUTPUT, $USER; + global $USER; $allsessions = new stdClass(); @@ -1402,9 +1403,6 @@ class mod_attendance_renderer extends plugin_renderer_base { $shortform = true; } - $context = context_module::instance($userdata->filtercontrols->cm->id); - - // 'course', 'activity', 'date' $groupby = $userdata->pageparams->groupby; $table = new html_table(); @@ -1418,7 +1416,7 @@ class mod_attendance_renderer extends plugin_renderer_base { // 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) + // (currently date is only option which does not use course). if ($groupby === 'date') { $table->head[] = ''; $table->align[] = 'left'; @@ -1448,7 +1446,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } } - // Need activity column unless we are using activity to group + // Need activity column unless we are using activity to group. if ($groupby !== 'activity') { $table->head[] = get_string('pluginname', 'mod_attendance'); $table->align[] = 'left'; @@ -1457,7 +1455,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $colcount++; } - // If grouping by date, it belongs up front rather than here + // If grouping by date, it belongs up front rather than here. if ($groupby !== 'date') { $table->head[] = get_string('date'); $table->align[] = 'left'; @@ -1466,7 +1464,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $colcount++; } - // use "session" instead of "description" + // Use "session" instead of "description". $table->head[] = get_string('session', 'attendance'); $table->align[] = 'left'; $table->colclasses[] = 'desccol'; @@ -1476,12 +1474,12 @@ class mod_attendance_renderer extends plugin_renderer_base { if (!$shortform) { $table->head[] = get_string('sessiontypeshort', 'attendance'); $table->align[] = ''; - $table->size[] = '1px'; + $table->size[] = '*'; $table->colclasses[] = ''; $colcount++; } - if (!empty($USER->editing)) { + if (!empty($USER->attendanceediting)) { $table->head[] = get_string('status', 'attendance'); $table->align[] = 'center'; $table->colclasses[] = 'statuscol'; @@ -1561,8 +1559,8 @@ class mod_attendance_renderer extends plugin_renderer_base { ); } $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; - // ensure all possible acronyms for current sess's statusset are available as - // keys in status array for period + // 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. @@ -1576,7 +1574,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } } } - // array_key_exists check is for hidden statuses + // 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']++; @@ -1588,9 +1586,8 @@ class mod_attendance_renderer extends plugin_renderer_base { } $stats['date'][$weekformat]['maxpoints'] += $statussetmaxpoints[$sess->statusset]; $stats['overall']['maxpoints'] += $statussetmaxpoints[$sess->statusset]; - } - else { - // By course and perhaps activity + } else { + // By course and perhaps activity. if ( ($sess->courseid != $lastgroup[0]) || ($groupby === 'activity' && $sess->cmid != $lastgroup[1]) @@ -1613,7 +1610,7 @@ class mod_attendance_renderer extends plugin_renderer_base { ); } $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; - // ensure all possible acronyms for current sess's statusset are available as + // 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 @@ -1628,7 +1625,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } } } - // array_key_exists check is for hidden statuses + // 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']++; @@ -1652,7 +1649,7 @@ class mod_attendance_renderer extends plugin_renderer_base { ); } $statussetmaxpoints = $statusmaxpoints[$sess->attendanceid]; - // ensure all possible acronyms for current sess's statusset are available as + // 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 @@ -1667,7 +1664,7 @@ class mod_attendance_renderer extends plugin_renderer_base { } } } - // array_key_exists check is for hidden statuses + // 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']++; @@ -1687,7 +1684,6 @@ class mod_attendance_renderer extends plugin_renderer_base { $points = $stats['overall']['points']; $maxpoints = $stats['overall']['maxpointstodate']; - $summary = ''; $summarytable = new html_table(); $summarytable->attributes['class'] = 'generaltable table-bordered table-condensed'; $row = new html_table_row(); @@ -1702,8 +1698,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = $status['count']; $summarytable->data[] = $row; } - /* $row = new html_table_row(); */ - /* $summarytable->data[] = $row; */ + $row = new html_table_row(); if ($maxpoints !== 0) { $pctodate = format_float( $points * 100 / $maxpoints); @@ -1735,7 +1730,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = $cell; $week = date("W", $sess->sessdate); $year = date("Y", $sess->sessdate); - // ISO week starts on day 1, Monday + // 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)); @@ -1769,15 +1764,14 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = $cell; $table->data[] = $row; $lastgroup[0] = date("YW", $weekstart); - } - else { + } 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 + $headcell = $cell; // Keep ref to be able to adjust rowspan later. $cell->rowspan += 2; $row->cells[] = $cell; $cell = new html_table_cell(); @@ -1866,13 +1860,9 @@ class mod_attendance_renderer extends plugin_renderer_base { foreach ($group as $sess) { $row = new html_table_row(); - // partialdate? / course? / activity? / date? / session / type / status / points / remarks / action - // - // 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) - // + // (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')) . @@ -1882,8 +1872,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = html_writer::link($courseurl, $sess->cname); } - // Need activity column unless we are using activity to group - // + // 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, @@ -1891,8 +1880,7 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = html_writer::link($attendanceurl, $sess->attname); } - // If grouping by date, it belongs up front rather than here - // + // 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); @@ -1918,13 +1906,10 @@ class mod_attendance_renderer extends plugin_renderer_base { $row->cells[] = html_writer::tag('nobr', $sessiontypeshort); } - if (!empty($USER->editing)) { + if (!empty($USER->attendanceediting)) { $context = context_module::instance($sess->cmid); if (has_capability('mod/attendance:takeattendances', $context)) { - // TODO: add ability to edit attendance here - $celltext = ''; - - // takedata needs: + // Takedata needs: // sessioninfo->sessdate // sessioninfo->duration // statuses @@ -1938,7 +1923,7 @@ class mod_attendance_renderer extends plugin_renderer_base { // enrolmentstart // enrolmentend // enrolmentstatus - // id + // id. $nastyhack = new ReflectionClass('attendance_take_data'); $takedata = $nastyhack->newInstanceWithoutConstructor(); @@ -1963,10 +1948,13 @@ class mod_attendance_renderer extends plugin_renderer_base { } $cell = new html_table_cell($celltext); - $cell->colspan = 2; $row->cells[] = $cell; - } - else { + + $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; @@ -2014,26 +2002,26 @@ class mod_attendance_renderer extends plugin_renderer_base { } } - if (!empty($USER->editing)) { + 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); + $cell->colspan = $colcount + (($groupby == 'activity') ? 2 : 1); $row->cells[] = $cell; $table->data[] = $row; } $logtext = html_writer::table($table); - if (!empty($USER->editing)) { + 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 + // 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')); diff --git a/styles.css b/styles.css index 29446a6..e635046 100644 --- a/styles.css +++ b/styles.css @@ -17,7 +17,6 @@ margin-bottom: 10px; margin-left: auto; margin-right: auto; -/* width: 90%; */ } .path-mod-attendance .attfiltercontrols #currentdate { @@ -117,7 +116,7 @@ text-align: right; } .path-mod-attendance table.allsessions tr.grouper td { - background-color: #eeeeee; + background-color: #eee; } .path-mod-attendance table.allsessions td.groupheading { font-weight: bold; diff --git a/view.php b/view.php index efed400..dea964d 100644 --- a/view.php +++ b/view.php @@ -29,6 +29,7 @@ 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); @@ -74,6 +75,33 @@ if (isset($pageparams->studentid) && $USER->id != $pageparams->studentid) { $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. @@ -93,22 +121,20 @@ if (empty($userdata->pageparams->studentid)) { $relateduserid = $userdata->pageparams->studentid; } -if (($formdata = data_submitted()) && confirm_sesskey()) { +if (($formdata = data_submitted()) && confirm_sesskey() && $edit == -1) { $userdata->take_sessions_from_form_data($formdata); - // Trigger updated event + // 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->add_record_snapshot('user', $); $event->trigger(); redirect($url, get_string('attendancesuccess', 'attendance')); -} -else { - // Trigger viewed event +} else { + // Trigger viewed event. $event = \mod_attendance\event\session_report_viewed::create(array( 'relateduserid' => $relateduserid, 'context' => $context, From ba91bde77cc34de36e0904fe7549343f4e1bf107 Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Thu, 3 Dec 2020 11:42:23 +1300 Subject: [PATCH 11/13] Add github actions config. and remove travis config. --- .github/workflows/ci.yml | 83 ++++++++++++++++++++++++++++++++++++++++ .travis.yml | 51 ------------------------ 2 files changed, 83 insertions(+), 51 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9e6612f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,83 @@ +name: Run all tests + +# Run this workflow every time a new commit pushed to your repository +on: push + +jobs: + setup: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:9.6 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + # Health check to wait for postgres to start. + ports: + - 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: + php-versions: ['7.3', '7.4'] + database: ['pgsql', 'mariadb'] + steps: + - 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: + # TODO: Check if we can support .nvmrc + node-version: '14.15.0' + + - 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: 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 + 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 || true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8b5a809..0000000 --- a/.travis.yml +++ /dev/null @@ -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 From b3d7171f73af86f9ebbcccfdaa5910c058ce936a Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Thu, 3 Dec 2020 13:23:10 +1300 Subject: [PATCH 12/13] Fix some more coding guideline issues. --- renderables.php | 7 ++++--- renderer.php | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/renderables.php b/renderables.php index 6246025..3a5b9ca 100644 --- a/renderables.php +++ b/renderables.php @@ -600,9 +600,10 @@ class attendance_user_data implements renderable { $formkey = 'user'.$stid.'sess'.$sessid; $attid = $dbsession->attendanceid; - $statusset = array_filter($this->statuses[$attid], function($x) use($dbsession) { - return $x->setnumber === $dbsession->statusset; - }); + $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])) { diff --git a/renderer.php b/renderer.php index 759c091..00036de 100644 --- a/renderer.php +++ b/renderer.php @@ -1567,10 +1567,12 @@ class mod_attendance_renderer extends plugin_renderer_base { 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); + $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); + $stats['overall']['statuses'][$attstatus->acronym] = + array('count' => 0, 'description' => $attstatus->description); } } } @@ -1618,10 +1620,12 @@ class mod_attendance_renderer extends plugin_renderer_base { 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); + $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); + $stats['overall']['statuses'][$attstatus->acronym] = + array('count' => 0, 'description' => $attstatus->description); } } } @@ -1657,10 +1661,12 @@ class mod_attendance_renderer extends plugin_renderer_base { 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); + $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); + $stats['overall']['statuses'][$attstatus->acronym] = + array('count' => 0, 'description' => $attstatus->description); } } } @@ -1779,7 +1785,8 @@ class mod_attendance_renderer extends plugin_renderer_base { } $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 = 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'; @@ -1821,7 +1828,8 @@ class mod_attendance_renderer extends plugin_renderer_base { $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 = 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'; From a4b92b7e219dccce2eb5fc297c676d388d7177df Mon Sep 17 00:00:00 2001 From: Dan Marsden Date: Thu, 3 Dec 2020 13:25:54 +1300 Subject: [PATCH 13/13] See if I can use chrome for behat tests. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e6612f..9bf0172 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,4 +80,4 @@ jobs: moodle-plugin-ci grunt || true moodle-plugin-ci phpdoc || true moodle-plugin-ci phpunit || true - moodle-plugin-ci behat || true + moodle-plugin-ci behat --profile chrome || true