diff --git a/locallib.php b/locallib.php index d6971c8..a02d726 100644 --- a/locallib.php +++ b/locallib.php @@ -11,6 +11,9 @@ define('VIEW_MONTHS', 3); define('VIEW_ALLTAKEN', 4); define('VIEW_ALL', 5); +define('SORT_LASTNAME', 1); +define('SORT_FIRSTNAME', 2); + class attforblock_permissions { private $canview; private $canviewreports; @@ -46,6 +49,10 @@ class attforblock_permissions { return $this->canviewreports; } + public function require_view_reports_capability() { + require_capability('mod/attforblock:viewreports', $this->context); + } + public function can_take() { if (is_null($this->cantake)) $this->cantake = has_capability('mod/attforblock:takeattendances', $this->context); @@ -239,9 +246,6 @@ class att_take_page_params { const DEFAULT_VIEW_MODE = self::SORTED_LIST; - const SORT_LASTNAME = 1; - const SORT_FIRSTNAME = 2; - public $sessionid; public $grouptype; public $group; @@ -255,7 +259,7 @@ class att_take_page_params { public function init() { if (!isset($this->group)) $this->group = 0; - if (!isset($this->sort)) $this->sort = self::SORT_LASTNAME; + if (!isset($this->sort)) $this->sort = SORT_LASTNAME; $this->init_view_mode(); $this->init_gridcols(); } @@ -284,13 +288,39 @@ class att_take_page_params { $params['sessionid'] = $this->sessionid; $params['grouptype'] = $this->grouptype; if ($this->group) $params['group'] = $this->group; - if ($this->sort != self::SORT_LASTNAME) $params['sort'] = $this->sort; + if ($this->sort != SORT_LASTNAME) $params['sort'] = $this->sort; if (isset($this->copyfrom)) $params['copyfrom'] = $this->copyfrom; return $params; } } +class att_report_page_params extends att_page_with_filter_controls { + public $group; + public $sort; + + public function __construct() { + $this->selectortype = self::SELECTOR_GROUP; + } + + public function init($courseid) { + parent::init($courseid); + + if (!isset($this->group)) $this->group = 0; + if (!isset($this->sort)) $this->sort = SORT_LASTNAME; + } + + public function get_significant_params() { + $params = array(); + + //if ($this->group) $params['group'] = $this->group; + if ($this->sort != SORT_LASTNAME) $params['sort'] = $this->sort; + + return $params; + } +} + + class attforblock { const SESSION_COMMON = 0; const SESSION_GROUP = 1; @@ -516,8 +546,8 @@ class attforblock { /** * @return moodle_url of report.php for attendance instance */ - public function url_report() { - $params = array('id' => $this->cm->id); + public function url_report($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); return new moodle_url('/mod/attforblock/report.php', $params); } @@ -540,8 +570,8 @@ class attforblock { /** * @return moodle_url of attendances.php for attendance instance */ - public function url_take() { - $params = array('id' => $this->cm->id); + public function url_take($params=array()) { + $params = array_merge(array('id' => $this->cm->id), $params); return new moodle_url('/mod/attforblock/take.php', $params); } @@ -745,7 +775,7 @@ class attforblock { //fields we need from the user table $userfields = user_picture::fields('u'); - if (isset($this->pageparams->sort) and ($this->pageparams->sort == att_take_page_params::SORT_FIRSTNAME)) { + if (isset($this->pageparams->sort) and ($this->pageparams->sort == SORT_FIRSTNAME)) { $orderby = "u.firstname ASC, u.lastname ASC"; } else { @@ -924,10 +954,42 @@ class attforblock { global $DB; if ($this->pageparams->startdate && $this->pageparams->enddate) { - $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.sessdate >= :sdate AND ats.sessdate < :edate"; } else { - $where = "AND ats.attendanceid = :aid AND ats.sessdate >= :csdate"; + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate"; + } + + $sql = "SELECT ats.id, ats.sessdate, ats.groupid, al.statusid + FROM {attendance_sessions} ats + JOIN {attendance_log} al + ON ats.id = al.sessionid AND al.studentid = :uid + WHERE $where + ORDER BY ats.sessdate ASC"; + + $params = array( + 'uid' => $userid, + 'aid' => $this->id, + 'csdate' => $this->course->startdate, + 'sdate' => $this->pageparams->startdate, + 'edate' => $this->pageparams->enddate); + $sessions = $DB->get_records_sql($sql, $params); + + return $sessions; + } + + public function get_user_filtered_sessions_log_extended($userid) { + global $DB; + + $groups = array_keys(groups_get_all_groups($this->course->id, $userid)); + $groups[] = 0; + list($gsql, $gparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'gid0'); + + if ($this->pageparams->startdate && $this->pageparams->enddate) { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND + ats.sessdate >= :sdate AND ats.sessdate < :edate AND ats.groupid $gsql"; + } else { + $where = "ats.attendanceid = :aid AND ats.sessdate >= :csdate AND ats.groupid $gsql"; } $sql = "SELECT ats.id, ats.sessdate, ats.duration, ats.description, al.statusid, al.remarks @@ -943,6 +1005,7 @@ class attforblock { 'csdate' => $this->course->startdate, 'sdate' => $this->pageparams->startdate, 'edate' => $this->pageparams->enddate); + $params = array_merge($params, $gparams); $sessions = $DB->get_records_sql($sql, $params); foreach ($sessions as $sess) { if (empty($sess->description)) { diff --git a/manage.php b/manage.php index 8b8f1f5..48ea9af 100644 --- a/manage.php +++ b/manage.php @@ -16,8 +16,8 @@ $pageparams = new att_manage_page_params(); $id = required_param('id', PARAM_INT); $from = optional_param('from', NULL, PARAM_ACTION); -$pageparams ->view = optional_param('view', NULL, PARAM_INT); -$pageparams ->curdate = optional_param('curdate', NULL, PARAM_INT); +$pageparams->view = optional_param('view', NULL, PARAM_INT); +$pageparams->curdate = optional_param('curdate', NULL, PARAM_INT); $cm = get_coursemodule_from_id('attforblock', $id, 0, false, MUST_EXIST); $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); @@ -57,7 +57,7 @@ $PAGE->navbar->add($att->name); $output = $PAGE->get_renderer('mod_attforblock'); $tabs = new attforblock_tabs($att, attforblock_tabs::TAB_SESSIONS); $filtercontrols = new attforblock_filter_controls($att); -$sesstable = new attforblock_sessions_manage_data($att); +$sesstable = new attforblock_manage_data($att); /// Output starts here diff --git a/renderables.php b/renderables.php index a96b64c..9d2c538 100644 --- a/renderables.php +++ b/renderables.php @@ -84,6 +84,8 @@ class attforblock_filter_controls implements renderable { /** @var int current view mode */ public $pageparams; + public $cm; + public $curdate; public $prevcur; @@ -100,6 +102,8 @@ class attforblock_filter_controls implements renderable { $this->pageparams = $att->pageparams; + $this->cm = $att->cm; + $this->curdate = $att->pageparams->curdate; $date = usergetdate($att->pageparams->curdate); @@ -170,7 +174,7 @@ class attforblock_filter_controls implements renderable { * Represents info about attendance sessions taking into account view parameters. * */ -class attforblock_sessions_manage_data implements renderable { +class attforblock_manage_data implements renderable { /** @var array of sessions*/ public $sessions; @@ -205,24 +209,15 @@ class attforblock_sessions_manage_data implements renderable { $this->att = $att; } - public function url_take($sessionid, $grouptype=NULL) { - $params = array('sessionid' => $sessionid); - $url = new moodle_url($this->att->url_take(), $params); - if (isset($grouptype)) - $url->param('grouptype', $grouptype); - - return $url; + public function url_take($sessionid, $grouptype) { + return url_helpers::url_take($this->att, $sessionid, $grouptype); } /** * Must be called without or with both parameters */ public function url_sessions($sessionid=NULL, $action=NULL) { - $url = new moodle_url($this->att->url_sessions()); - if (isset($sessionid) && isset($action)) - $url->params(array('sessionid' => $sessionid, 'action' => $action)); - - return $url; + return url_helpers::url_sessions($this->att, $sessionid, $action); } } @@ -295,7 +290,7 @@ class attforblock_take_data implements renderable { } public function url_view($params=array()) { - return new moodle_url($this->att->url_view($params), $params); + return url_helpers::url_view($this->att, $params); } public function url_path() { @@ -354,7 +349,7 @@ class attforblock_user_data implements renderable { $this->filtercontrols = new attforblock_filter_controls($att); - $this->sessionslog = $att->get_user_filtered_sessions_log($userid); + $this->sessionslog = $att->get_user_filtered_sessions_log_extended($userid); } else { $this->coursesatts = get_user_courses_attendances($userid); @@ -404,4 +399,113 @@ class attforblock_user_data implements renderable { } } +class attforblock_report_data implements renderable { + public $perm; + public $pageparams; + + public $users; + + public $groups; + + public $sessions; + + public $statuses; + // includes disablrd/deleted statuses + public $allstatuses; + + public $gradable; + + public $decimalpoints; + + public $usersgroups = array(); + + public $sessionslog = array(); + + public $usersstats = array(); + + public $grades = array(); + + public $maxgrades = array(); + + private $att; + + public function __construct(attforblock $att) { + global $CFG; + + $this->perm = $att->perm; + $this->pageparams = $att->pageparams; + + $this->users = $att->get_users($att->pageparams->group); + + $this->groups = groups_get_all_groups($att->course->id); + + $this->sessions = $att->get_filtered_sessions(); + + $this->statuses = $att->get_statuses(); + $this->allstatuses = $att->get_statuses(false); + + $this->gradable = $att->grade > 0; + + if (!$this->decimalpoints = grade_get_setting($att->course->id, 'decimalpoints')) { + $this->decimalpoints = $CFG->grade_decimalpoints; + } + + foreach ($this->users as $user) { + $this->usersgroups[$user->id] = groups_get_all_groups($att->course->id, $user->id); + + $this->sessionslog[$user->id] = $att->get_user_filtered_sessions_log($user->id); + + $this->usersstats[$user->id] = $att->get_user_statuses_stat($user->id); + + if ($this->gradable) { + $this->grades[$user->id] = $att->get_user_grade($user->id); + $this->maxgrades[$user->id] = $att->get_user_max_grade($user->id); + } + } + + $this->att = $att; + } + + public function url_take($sessionid, $grouptype) { + return url_helpers::url_take($this->att, $sessionid, $grouptype); + } + + public function url_view($params=array()) { + return url_helpers::url_view($this->att, $params); + } + + public function url($params=array()) { + $params = array_merge($params, $this->pageparams->get_significant_params()); + + return $this->att->url_report($params); + } + +} + +class url_helpers { + public static function url_take($att, $sessionid, $grouptype) { + $params = array('sessionid' => $sessionid); + if (isset($grouptype)) + $params['grouptype'] = $grouptype; + + return $att->url_take($params); + } + + /** + * Must be called without or with both parameters + */ + public static function url_sessions($att, $sessionid=NULL, $action=NULL) { + if (isset($sessionid) && isset($action)) + $params = array('sessionid' => $sessionid, 'action' => $action); + else + $params = array(); + + return $att->url_sessions($params); + } + + public static function url_view($att, $params=array()) { + return $att->url_view($params); + } +} + ?> \ No newline at end of file diff --git a/renderer.php b/renderer.php index 2c9532a..28c5056 100644 --- a/renderer.php +++ b/renderer.php @@ -70,6 +70,9 @@ class mod_attforblock_renderer extends plugin_renderer_base { return html_writer::tag('div', $output, array('class' => 'groupselector')); } + break; + case att_page_with_filter_controls::SELECTOR_GROUP: + return groups_print_activity_menu($fcontrols->cm, $fcontrols->url(), true); } return ''; @@ -143,10 +146,10 @@ class mod_attforblock_renderer extends plugin_renderer_base { /** * Renders attendance sessions managing table * - * @param attforblock_sessions_manage_data $sessdata to display + * @param attforblock_manage_data $sessdata to display * @return string html code */ - protected function render_attforblock_sessions_manage_data(attforblock_sessions_manage_data $sessdata) { + protected function render_attforblock_manage_data(attforblock_manage_data $sessdata) { // TODO: nosessionexists // TODO: log $o = $this->render_sess_manage_table($sessdata) . $this->render_sess_manage_control($sessdata); @@ -157,7 +160,7 @@ class mod_attforblock_renderer extends plugin_renderer_base { return $o; } - protected function render_sess_manage_table(attforblock_sessions_manage_data $sessdata) { + protected function render_sess_manage_table(attforblock_manage_data $sessdata) { $this->page->requires->js('/mod/attforblock/attforblock.js'); $this->page->requires->js_init_call('M.mod_attforblock.init_manage'); @@ -193,7 +196,7 @@ class mod_attforblock_renderer extends plugin_renderer_base { return html_writer::table($table); } - private function construct_date_time_actions(attforblock_sessions_manage_data $sessdata, $sess) { + private function construct_date_time_actions(attforblock_manage_data $sessdata, $sess) { $actions = ''; $date = userdate($sess->sessdate, get_string('strftimedmyw', 'attforblock')); @@ -230,7 +233,7 @@ class mod_attforblock_renderer extends plugin_renderer_base { return array('date' => $date, 'time' => $time, 'actions' => $actions); } - protected function render_sess_manage_control(attforblock_sessions_manage_data $sessdata) { + protected function render_sess_manage_control(attforblock_manage_data $sessdata) { $table = new html_table(); $table->attributes['class'] = ' '; $table->width = '100%'; @@ -430,16 +433,16 @@ class mod_attforblock_renderer extends plugin_renderer_base { return html_writer::table($table); } - private function construct_fullname_head(attforblock_take_data $takedata) { + private function construct_fullname_head($data) { global $CFG; - if ($takedata->pageparams->sort == att_take_page_params::SORT_LASTNAME) - $firstname = html_writer::link($takedata->url(array('sort' => att_take_page_params::SORT_FIRSTNAME)), get_string('firstname')); + if ($data->pageparams->sort == SORT_LASTNAME) + $firstname = html_writer::link($data->url(array('sort' => SORT_FIRSTNAME)), get_string('firstname')); else $firstname = get_string('firstname'); - if ($takedata->pageparams->sort == att_take_page_params::SORT_FIRSTNAME) - $lastname = html_writer::link($takedata->url(array('sort' => att_take_page_params::SORT_LASTNAME)), get_string('lastname')); + if ($data->pageparams->sort == SORT_FIRSTNAME) + $lastname = html_writer::link($data->url(array('sort' => SORT_LASTNAME)), get_string('lastname')); else $lastname = get_string('lastname'); @@ -459,7 +462,13 @@ class mod_attforblock_renderer extends plugin_renderer_base { $celldata['colspan'] = count($takedata->statuses) + 1; $celldata['class'] = 'userwithoutenrol'; } - elseif ($user->enrolmentstatus == ENROL_USER_SUSPENDED) { + elseif ($user->enrolmentend and $user->enrolmentend < $takedata->sessioninfo->sessdate) { + $celldata['text'] = get_string('enrolmentend', 'attforblock', userdate($user->enrolmentend, '%d.%m.%Y')); + $celldata['colspan'] = count($takedata->statuses) + 1; + $celldata['class'] = 'userwithoutenrol'; + } + // no enrolmentend and ENROL_USER_SUSPENDED + elseif (!$user->enrolmentend and $user->enrolmentstatus == ENROL_USER_SUSPENDED) { $celldata['text'] = get_string('enrolmentsuspended', 'attforblock'); $celldata['colspan'] = count($takedata->statuses) + 1; $celldata['class'] = 'userwithoutenrol'; @@ -614,12 +623,12 @@ class mod_attforblock_renderer extends plugin_renderer_base { $row->cells[] = $userdata->statuses[$sess->statusid]->description; $row->cells[] = $sess->remarks; } - elseif ($userdata->user->enrolmentstart && $sess->sessdate < $userdata->user->enrolmentstart) { + elseif ($sess->sessdate < $userdata->user->enrolmentstart) { $cell = new html_table_cell(get_string('enrolmentstart', 'attforblock', userdate($userdata->user->enrolmentstart, '%d.%m.%Y'))); $cell->colspan = 2; $row->cells[] = $cell; } - elseif ($userdata->user->enrolmentend && $sess->sessdate > $userdata->user->enrolmentend) { + elseif ($userdata->user->enrolmentend and $sess->sessdate > $userdata->user->enrolmentend) { $cell = new html_table_cell(get_string('enrolmentend', 'attforblock', userdate($userdata->user->enrolmentend, '%d.%m.%Y'))); $cell->colspan = 2; $row->cells[] = $cell; @@ -642,5 +651,124 @@ class mod_attforblock_renderer extends plugin_renderer_base { return $time; } + + protected function render_attforblock_report_data(attforblock_report_data $reportdata) { + $table = new html_table(); + + $table->attributes['class'] = 'generaltable attwidth'; + + // user picture + $table->head[] = ''; + $table->align[] = 'left'; + $table->size[] = '1px'; + + $table->head[] = $this->construct_fullname_head($reportdata); + $table->align[] = 'left'; + $table->size[] = ''; + + foreach ($reportdata->sessions as $sess) { + $sesstext = userdate($sess->sessdate, get_string('strftimedm', 'attforblock')); + $sesstext .= html_writer::empty_tag('br'); + $sesstext .= userdate($sess->sessdate, '('.get_string('strftimehm', 'attforblock').')'); + if (is_null($sess->lasttaken) and $reportdata->perm->can_take() or $reportdata->perm->can_change()) + $sesstext = html_writer::link($reportdata->url_take($sess->id, $sess->groupid), $sesstext); + $sesstext .= html_writer::empty_tag('br'); + $sesstext .= $sess->groupid ? $reportdata->groups[$sess->groupid]->name : get_string('commonsession', 'attforblock'); + + $table->head[] = $sesstext; + $table->align[] = 'center'; + $table->size[] = '1px'; + } + + foreach ($reportdata->statuses as $status) { + $table->head[] = $status->acronym; + $table->align[] = 'center'; + $table->size[] = '1px'; + } + + if ($reportdata->gradable) { + $table->head[] = get_string('grade'); + $table->align[] = 'center'; + $table->size[] = '1px'; + } + + foreach ($reportdata->users as $user) { + $row = new html_table_row(); + + $row->cells[] = $this->output->user_picture($user); + $row->cells[] = html_writer::link($reportdata->url_view(array('studentid' => $user->id)), fullname($user)); + + $cell = null; + foreach ($reportdata->sessions as $sess) { + if (array_key_exists($sess->id, $reportdata->sessionslog[$user->id])) { + $statusid = $reportdata->sessionslog[$user->id][$sess->id]->statusid; + if (array_key_exists($statusid, $reportdata->statuses)) { + $row->cells[] = $reportdata->statuses[$statusid]->acronym; + } else { + $row->cells[] = html_writer::tag('s', $reportdata->allstatuses[$statusid]->acronym); + } + } else { + if ($user->enrolmentstart > $sess->sessdate) { + $starttext = get_string('enrolmentstart', 'attforblock', userdate($user->enrolmentstart, '%d.%m.%Y')); + $this->construct_report_cell($starttext, $cell); + } + elseif ($user->enrolmentend and $user->enrolmentend < $sess->sessdate) { + $endtext = get_string('enrolmentend', 'attforblock', userdate($user->enrolmentend, '%d.%m.%Y')); + $this->construct_report_cell($endtext, $cell); + } + // no enrolmentend and ENROL_USER_SUSPENDED + elseif (!$user->enrolmentend and $user->enrolmentstatus == ENROL_USER_SUSPENDED) { + $suspendext = get_string('enrolmentsuspended', 'attforblock', userdate($user->enrolmentend, '%d.%m.%Y')); + $this->construct_report_cell($suspendext, $cell); + } + else { + if ($cell) { + $row->cells[] = $cell; + $cell = null; + } + + if ($sess->groupid == 0 or array_key_exists($sess->groupid, $reportdata->usersgroups[$user->id])) + $row->cells[] = '?'; + else + $row->cells[] = ''; + } + } + } + if ($cell) $row->cells[] = $cell; + + foreach ($reportdata->statuses as $status) { + if (array_key_exists($status->id, $reportdata->usersstats[$user->id])) + $row->cells[] = $reportdata->usersstats[$user->id][$status->id]->stcnt; + else + // no attendance data for this $status => no statistic for this status + $row->cells[] = 0; + } + + if ($reportdata->gradable) { + $row->cells[] = $reportdata->grades[$user->id].' / '.$reportdata->maxgrades[$user->id]; + } + + $table->data[] = $row; + } + + return html_writer::table($table); + } + + private function construct_report_cell($text, &$cell) { + if (is_null($cell)) { + $cell = new html_table_cell($text); + $cell->colspan = 1; + } + else { + if ($cell->text != $text) { + $row->cells[] = $cell; + $cell = new html_table_cell($text); + $cell->colspan = 1; + } + else + $cell->colspan++; + + } + } } ?> diff --git a/report.php b/report.php new file mode 100644 index 0000000..78186be --- /dev/null +++ b/report.php @@ -0,0 +1,59 @@ +view = optional_param('view', NULL, PARAM_INT); +$pageparams->curdate = optional_param('curdate', NULL, PARAM_INT); +$pageparams->group = optional_param('group', null, PARAM_INT); +$pageparams->sort = optional_param('sort', null, PARAM_INT); + +$cm = get_coursemodule_from_id('attforblock', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attforblock', array('id' => $cm->instance), '*', MUST_EXIST); + +require_login($course, true, $cm); + +$pageparams->init($course->id); +$att = new attforblock($att, $cm, $course, $PAGE->context, $pageparams); + +$att->perm->require_view_reports_capability(); + +$PAGE->set_url($att->url_report()); +$PAGE->set_title($course->shortname. ": ".$att->name.' - '.get_string('report','attforblock')); +$PAGE->set_heading($course->fullname); +$PAGE->set_cacheable(true); +$PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attforblock')); +$PAGE->navbar->add(get_string('report','attforblock')); + +$output = $PAGE->get_renderer('mod_attforblock'); +$tabs = new attforblock_tabs($att, attforblock_tabs::TAB_REPORT); +$filtercontrols = new attforblock_filter_controls($att); +$reportdata = new attforblock_report_data($att); + +// TODO: log + +/// Output starts here + +echo $output->header(); +echo $output->heading(get_string('attendanceforthecourse','attforblock').' :: ' .$course->fullname); +echo $output->render($tabs); +echo $output->render($filtercontrols); +echo $output->render($reportdata); + +echo $output->footer(); + +?>