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);