From 3eb9babc7147d8771ff96477df5caf38f4dd574a Mon Sep 17 00:00:00 2001 From: mjollnir_ Date: Sun, 16 Oct 2005 20:55:40 +0000 Subject: [PATCH] Some fixes to stats (performance) and new reports for ranking courses based on usage/particpation --- admin/reports.php | 117 +++++++++++++++++++++++++++++++++++++++++ admin/reportsgraph.php | 56 ++++++++++++++++++++ course/stats.php | 25 ++++++--- course/statsgraph.php | 11 ++-- course/user.php | 18 +++---- lib/statslib.php | 110 +++++++++++++++++++++++++++++--------- 6 files changed, 290 insertions(+), 47 deletions(-) create mode 100644 admin/reports.php create mode 100644 admin/reportsgraph.php diff --git a/admin/reports.php b/admin/reports.php new file mode 100644 index 00000000000..dbe34f14892 --- /dev/null +++ b/admin/reports.php @@ -0,0 +1,117 @@ +dirroot.'/lib/statslib.php'); + + $report = optional_param('report',STATS_REPORT_ACTIVE_COURSES,PARAM_INT); + $time = optional_param('time',0,PARAM_INT); + $numcourses = optional_param('numcourses',20,PARAM_INT); + + if (empty($CFG->enablestats)) { + error("Stats is not enabled."); + } + + require_login(); + + if (!isadmin()) { + error("This page is for admins only"); + } + + $course = get_site(); + stats_check_uptodate($course->id); + + $strheader = get_string('reports'); + + $strnav = ''.get_string('administration').' -> '.$strheader; + + $reportoptions = stats_get_report_options($course->id,STATS_MODE_RANKED); + + $tableprefix = $CFG->prefix.'stats_'; + + $earliestday = get_field_sql('SELECT timeend FROM '.$tableprefix.'daily ORDER BY timeend LIMIT 1'); + $earliestweek = get_field_sql('SELECT timeend FROM '.$tableprefix.'weekly ORDER BY timeend LIMIT 1'); + $earliestmonth = get_field_sql('SELECT timeend FROM '.$tableprefix.'monthly ORDER BY timeend LIMIT 1'); + + if (empty($earliestday)) $earliestday = time(); + if (empty($earliestweek)) $earliestweek = time(); + if (empty($earliestmonth)) $earliestmonth = time(); + + $now = stats_get_base_daily(); + $lastweekend = stats_get_base_weekly(); + $lastmonthend = stats_get_base_monthly(); + + $timeoptions = stats_get_time_options($now,$lastweekend,$lastmonthend,$earliestday,$earliestweek,$earliestmonth); + + if (empty($timeoptions)) { + error(get_string('nostatstodisplay'), $CFG->wwwroot.'/course/view.php?id='.$course->id); + } + + print_header($strheader,$strheader,$strnav,'','',true,' '); + + echo '
'."\n"; + + $table->width = '*'; + + $table->align = array('left','left','left','left','left','left'); + $table->data[] = array(get_string('statsreporttype'),choose_from_menu($reportoptions,'report',$report,'','','',true), + get_string('statstimeperiod'),choose_from_menu($timeoptions,'time',$time,'','','',true), + '', + '') ; + + print_table($table); + echo '
'; + + if (!empty($report) && !empty($time)) { + $param = stats_get_parameters($time,$report,SITEID,STATS_MODE_RANKED); + + $sql = "SELECT courseid,".$param->fields." FROM ".$CFG->prefix.'stats_'.$param->table + ." WHERE timeend >= ".$param->timeafter + ." GROUP BY courseid " + .$param->extras + ." ORDER BY ".$param->orderby + ." LIMIT ".$numcourses; + + $courses = get_records_sql($sql); + + if (empty($courses)) { + error(get_string('statsnodata'),$CFG->wwwroot.'/admin/reports.php'); + } + + echo '
'; + + $table = new object(); + $table->align = array('left','center','center','center'); + $table->head = array(get_string('course'),$param->line1); + if (!empty($param->line2)) { + $table->head[] = $param->line2; + } + if (!empty($param->line3)) { + $table->head[] = $param->line3; + } + + foreach ($courses as $c) { + $a = array(); + $a[] = ''.get_field('course','shortname','id',$c->courseid).''; + $a[] = print_numeric_value($c->line1); + if (isset($c->line2)) { + $a[] = print_numeric_value($c->line2); + } + if (isset($c->line3)) { + $a[] = print_numeric_value($c->line3); + } + $table->data[] = $a; + } + print_table($table); + } + + print_footer(); + +function print_numeric_value($value) { + list($whole, $decimals) = split ('[.,]', $value, 2); + if (intval($decimals) > 0) + return number_format($value,2,".",","); + else + return $value; +} + +?> \ No newline at end of file diff --git a/admin/reportsgraph.php b/admin/reportsgraph.php new file mode 100644 index 00000000000..f2b9361813f --- /dev/null +++ b/admin/reportsgraph.php @@ -0,0 +1,56 @@ +dirroot.'/lib/statslib.php'); + require_once($CFG->dirroot.'/lib/graphlib.php'); + + $report = required_param('report',0,PARAM_INT); + $time = required_param('time',0,PARAM_INT); + $numcourses = required_param('numcourses',20,PARAM_INT); + + require_login(); + + if (!isadmin()) { + error("You must be an admin to use this page"); + } + + stats_check_uptodate($course->id); + + $param = stats_get_parameters($time,$report,SITEID,STATS_MODE_RANKED); + + $sql = "SELECT courseid,".$param->fields." FROM ".$CFG->prefix.'stats_'.$param->table + ." WHERE timeend >= ".$param->timeafter + ." GROUP BY courseid " + .$param->extras + ." ORDER BY ".$param->orderby + ." LIMIT ".$numcourses; + + $courses = get_records_sql($sql); + + if (empty($courses)) { + error(get_string('statsnodata'),$CFG->wwwroot.'/admin/reports.php'); + } + + + $graph = new graph(750,400); + + $graph->parameter['legend'] = 'outside-right'; + $graph->parameter['legend_size'] = 10; + $graph->parameter['x_axis_angle'] = 90; + $graph->parameter['title'] = false; // moodle will do a nicer job. + if ($report != STATS_REPORT_ACTIVE_COURSES) { + $graph->parameter['y_decimal_left'] = 2; + } + + foreach ($courses as $c) { + $graph->x_data[] = get_field('course','shortname','id',$c->courseid); + $graph->y_data['bar1'][] = $c->{$param->graphline}; + } + $graph->y_order = array('bar1'); + $graph->y_format['bar1'] = array('colour' => 'blue','bar' => 'fill','legend' => $param->{$param->graphline}); + + $graph->draw_stack(); + + +} +?> \ No newline at end of file diff --git a/course/stats.php b/course/stats.php index 4d861159c85..b2b2148618c 100644 --- a/course/stats.php +++ b/course/stats.php @@ -17,6 +17,10 @@ $courseid = SITEID; //override } + if ($mode == STATS_MODE_RANKED) { + redirect($CFG->wwwroot.'/admin/reports.php?time='.$time); + } + if (!$course = get_record("course","id",$courseid)) { error("That's an invalid course id"); } @@ -75,6 +79,9 @@ $options = array(); $options[STATS_MODE_GENERAL] = get_string('statsmodegeneral'); $options[STATS_MODE_DETAILED] = get_string('statsmodedetailed'); + if (isadmin()) { + $options[STATS_MODE_RANKED] = get_string('reports'); + } $menu = choose_from_menu($options,'mode',$mode,'','this.form.submit();',0,true); @@ -91,7 +98,7 @@ if ($mode == STATS_MODE_DETAILED) { if (!empty($time)) { - $param = stats_get_parameters($time,null,$course->id); // we only care about the table and the time string. + $param = stats_get_parameters($time,null,$course->id,$mode); // we only care about the table and the time string. $sql = 'SELECT DISTINCT s.userid,s.roleid,u.firstname,u.lastname,u.idnumber,u.nickname FROM '.$CFG->prefix.'stats_user_'.$param->table.' s JOIN '.$CFG->prefix.'user u ON u.id = s.userid ' .'WHERE courseid = '.$course->id.' AND timeend >= '.$param->timeafter . ((!empty($param->stattype)) ? ' AND stattype = \''.$param->stattype.'\'' : ''); if (!isadmin()) { @@ -101,16 +108,18 @@ } else { $sql = 'SELECT s.userid,u.firstname,u.lastname,u.idnumber,u.nickname,1 AS roleid FROM '.$CFG->prefix.'user_students s JOIN '.$CFG->prefix.'user u ON u.id = s.userid WHERE course = '.$course->id; } + $us = get_records_sql($sql); + $admins = get_admins(); foreach ($us as $u) { $role = $course->student; if ($u->roleid == 2) { $role = $course->teacher; } - if (isadmin($u->userid)) { + if (array_key_exists($u->userid,$admins)) { $role = get_string('admin'); } - $users[$u->userid] = $role.' - '.fullname($u,isteacher($course->id)); + $users[$u->userid] = $role.' - '.fullname($u,true); } if (empty($time)) { if (isadmin()) { @@ -120,7 +129,7 @@ $users[$u->userid] = $course->teacher .' - '.fullname($u,true); } } else { - $users[$USER->id] = $course->teacher.' - '.fullname($USER); + $users[$USER->id] = $course->teacher.' - '.fullname($USER,true); } } @@ -153,7 +162,7 @@ if ($report == STATS_REPORT_LOGINS && $course->id != SITEID) { error("This type of report is only available for the site course"); } - $param = stats_get_parameters($time,$report,$course->id); + $param = stats_get_parameters($time,$report,$course->id,$mode); if ($mode == STATS_MODE_DETAILED) { $param->table = 'user_'.$param->table; } @@ -173,15 +182,15 @@ $stats = stats_fix_zeros($stats,$param->timeafter,$param->table,(!empty($param->line2))); - print_heading($course->shortname.' - '.get_string('statsreport'.$report).((!empty($user)) ? ' '.get_string('statsreportforuser').' ' .fullname($user,isteacher($course->id)) : '')); + print_heading($course->shortname.' - '.get_string('statsreport'.$report).((!empty($user)) ? ' '.get_string('statsreportforuser').' ' .fullname($user,true) : '')); if (empty($CFG->gdversion)) { echo "(".get_string("gdneed").")"; } else { if ($mode == STATS_MODE_DETAILED) { - echo '
'; + echo '
'; } else { - echo '
'; + echo '
'; } } diff --git a/course/statsgraph.php b/course/statsgraph.php index 029d2b6980b..2548c511b90 100644 --- a/course/statsgraph.php +++ b/course/statsgraph.php @@ -4,10 +4,11 @@ require_once($CFG->dirroot.'/lib/statslib.php'); require_once($CFG->dirroot.'/lib/graphlib.php'); - $courseid = required_param('course',PARAM_INT); - $report = required_param('report',PARAM_INT); - $time = required_param('time',PARAM_INT); + $courseid = required_param('course',0,PARAM_INT); + $report = required_param('report',0,PARAM_INT); + $time = required_param('time',0,PARAM_INT); $userid = optional_param('userid',0,PARAM_INT); + $mode = required_param('mode',STATS_MODE_GENERAL,PARAM_INT); if (!$course = get_record("course","id",$courseid)) { error("That's an invalid course id"); @@ -26,7 +27,7 @@ stats_check_uptodate($course->id); - $param = stats_get_parameters($time,$report,$course->id); + $param = stats_get_parameters($time,$report,$course->id,$mode); if (!empty($userid)) { $param->table = 'user_'.$param->table; @@ -43,7 +44,7 @@ $stats = stats_fix_zeros($stats,$param->timeafter,$param->table,(!empty($param->line2)),(!empty($param->line3))); - $stats = array_reverse($stats); + $stats = array_reverse($stats); $graph = new graph(750,400); diff --git a/course/user.php b/course/user.php index 9bb48090c81..24609247562 100644 --- a/course/user.php +++ b/course/user.php @@ -86,7 +86,7 @@ require_once($CFG->dirroot.'/lib/statslib.php'); - stats_check_uptodate($course->id); + stats_check_uptodate($course->id); $earliestday = get_field_sql('SELECT timeend FROM '.$CFG->prefix.'stats_user_daily ORDER BY timeend LIMIT 1'); $earliestweek = get_field_sql('SELECT timeend FROM '.$CFG->prefix.'stats_user_weekly ORDER BY timeend LIMIT 1'); @@ -108,29 +108,27 @@ // use the earliest. $time = array_pop(array_keys($timeoptions)); - - $param = stats_get_parameters($time,STATS_REPORT_USER_VIEW,$course->id); + + $param = stats_get_parameters($time,STATS_REPORT_USER_VIEW,$course->id,STATS_MODE_DETAILED); $param->table = 'user_'.$param->table; - + $sql = 'SELECT timeend,'.$param->fields.' FROM '.$CFG->prefix.'stats_'.$param->table.' WHERE ' .(($course->id == SITEID) ? '' : ' courseid = '.$course->id.' AND ') .' userid = '.$user->id - .' AND stattype = \''.$param->stattype.'\'' .' AND timeend >= '.$param->timeafter .$param->extras .' ORDER BY timeend DESC'; - $stats = get_records_sql($sql); if (empty($stats)) { error(get_string('nostatstodisplay'), $CFG->wwwroot.'/course/user.php?id='.$course->id.'&user='.$user->id.'&mode=outline'); } - - echo '
'; - - $stats = stats_fix_zeros($stats,$param->timeafter,$param->table,(!empty($param->line2)),(!empty($param->line3))); + echo '
'; + + $stats = stats_fix_zeros($stats,$param->timeafter,$param->table,(!empty($param->line2)),(!empty($param->line3))); + $table = new object(); $table->align = array('left','center','center','center'); $param->table = str_replace('user_','',$param->table); diff --git a/lib/statslib.php b/lib/statslib.php index a0388386ed1..0c77a36555b 100644 --- a/lib/statslib.php +++ b/lib/statslib.php @@ -15,6 +15,12 @@ define('STATS_REPORT_USER_LOGINS',9); define('STATS_REPORT_USER_VIEW',10); // this is the report you see on the user profile. + // admin only ranking stats reports + define('STATS_REPORT_ACTIVE_COURSES',11); + define('STATS_REPORT_ACTIVE_COURSES_WEIGHTED',12); + define('STATS_REPORT_PARTICIPATORY_COURSES',13); + define('STATS_REPORT_PARTICIPATORY_COURSES_RW',14); + // start after 0 = show dailies. define('STATS_TIME_LASTWEEK',1); define('STATS_TIME_LAST2WEEKS',2); @@ -40,6 +46,7 @@ // different modes for what reports to offer define('STATS_MODE_GENERAL',1); define('STATS_MODE_DETAILED',2); + define('STATS_MODE_RANKED',3); // admins only - ranks courses // return codes - whether to rerun define('STATS_RUN_COMPLETE',1); @@ -94,7 +101,7 @@ function stats_cron_daily () { while ($midnight >= $nextmidnight) { $timesql = " (l.time > $timestart AND l.time < $nextmidnight) "; - + begin_sql(); foreach ($courses as $course) { $stat->students = count_records('user_students','course',$course->id); @@ -141,7 +148,7 @@ function stats_cron_daily () { // now do logins. if ($course->id == SITEID) { - $sql = 'SELECT count(l.id) as count,l.userid FROM '.$CFG->prefix.'log l WHERE action = \'login\' AND '.$timesql.' GROUP BY userid'; + $sql = 'SELECT l.userid,count(l.id) as count FROM '.$CFG->prefix.'log l WHERE action = \'login\' AND '.$timesql.' GROUP BY userid'; $logins = get_records_sql($sql); foreach ($logins as $l) { @@ -166,6 +173,7 @@ function stats_cron_daily () { stats_do_daily_user_cron($course,$user,2,$timesql,$nextmidnight,$daily_modules); } } + commit_sql(); $timestart = $nextmidnight; $nextmidnight = $nextmidnight + (60*60*24); $days++; @@ -232,7 +240,7 @@ function stats_cron_weekly () { while ($sunday >= $nextsunday) { $timesql = " (timeend > $timestart AND timeend < $nextsunday) "; - + begin_sql(); foreach ($courses as $course) { $sql = 'SELECT ceil(avg(students)) as students, ceil(avg(teachers)) as teachers, @@ -273,8 +281,10 @@ function stats_cron_weekly () { foreach ($teachers as $user) { stats_do_aggregate_user_cron($course,$user,2,$timesql,$nextsunday,'weekly',$weekly_modules); } - } + } + stats_do_aggregate_user_login_cron($timesql,$nextsunday,'weekly'); + commit_sql(); $timestart = $nextsunday; $nextsunday = $nextsunday + (60*60*24*7); $weeks++; @@ -339,7 +349,7 @@ function stats_cron_monthly () { while ($monthend >= $nextmonthend) { $timesql = " (timeend > $timestart AND timeend < $nextmonthend) "; - + begin_sql(); foreach ($courses as $course) { $sql = 'SELECT ceil(avg(students)) as students, ceil(avg(teachers)) as teachers, ceil(avg(activestudents)) as activestudents,ceil(avg(activeteachers)) as activeteachers, @@ -378,7 +388,8 @@ function stats_cron_monthly () { stats_do_aggregate_user_cron($course,$user,2,$timesql,$nextmonthend,'monthly',$monthly_modules); } } - + stats_do_aggregate_user_login_cron($timesql,$nextmonthend,'monthly'); + commit_sql(); $timestart = $nextmonthend; $nextmonthend = stats_get_next_monthend($timestart); $months++; @@ -464,7 +475,7 @@ function stats_clean_old() { // don't delete monthlies } -function stats_get_parameters($time,$report,$courseid) { +function stats_get_parameters($time,$report,$courseid,$mode) { if ($time < 10) { // dailies // number of days to go back = 7* time $param->table = 'daily'; @@ -535,9 +546,46 @@ function stats_get_parameters($time,$report,$courseid) { $param->line3 = get_string('statsuseractivity'); $param->stattype = 'activity'; break; + case STATS_REPORT_ACTIVE_COURSES: + $param->fields = 'sum(studentreads+studentwrites+teacherreads+teacherwrites) AS line1'; + $param->orderby = 'line1 DESC'; + $param->line1 = get_string('activity'); + $param->graphline = 'line1'; + break; + case STATS_REPORT_ACTIVE_COURSES_WEIGHTED: + $param->fields = 'sum(studentreads+studentwrites+teacherreads+teacherwrites) AS line1,' + .'sum(students+teachers) AS line2,' + .'sum(studentreads+studentwrites+teacherreads+teacherwrites)::real/sum(students+teachers)::real AS line3'; + $param->extras = 'HAVING sum(students+teachers) != 0'; + $param->orderby = 'line3 DESC'; + $param->line1 = get_string('activity'); + $param->line2 = get_string('users'); + $param->line3 = get_string('activityweighted'); + $param->graphline = 'line3'; + break; + case STATS_REPORT_PARTICIPATORY_COURSES: + $param->fields = 'sum(students+teachers) as line1,sum(activestudents+activeteachers) AS line2,' + .'sum(activestudents+activeteachers)::real/sum(students+teachers)::real AS line3'; + $param->extras = 'HAVING sum(students+teachers) != 0'; + $param->orderby = 'line3 DESC'; + $param->line1 = get_string('users'); + $param->line2 = get_string('activeusers'); + $param->line3 = get_string('participationratio'); + $param->graphline = 'line3'; + break; + case STATS_REPORT_PARTICIPATORY_COURSES_RW: + $param->fields = 'sum(studentreads+teacherreads) as line1,sum(studentwrites+teacherwrites) AS line2,' + .'sum(studentwrites+teacherwrites)::real/sum(studentreads+teacherreads)::real AS line3'; + $param->extras = 'HAVING sum(studentreads+teacherreads) != 0'; + $param->orderby = 'line3 DESC'; + $param->line1 = get_string('views'); + $param->line2 = get_string('posts'); + $param->line3 = get_string('participationratio'); + $param->graphline = 'line3'; + break; } - if ($courseid == SITEID) { // just aggregate all courses. + if ($courseid == SITEID && $mode != STATS_MODE_RANKED) { // just aggregate all courses. $param->fields = preg_replace('/([a-zA-Z0-9+_]*)\W+as\W+([a-zA-Z0-9_]*)/','sum($1) as $2',$param->fields); $param->extras = ' GROUP BY timeend'; } @@ -625,7 +673,7 @@ function stats_do_daily_user_cron($course,$user,$roleid,$timesql,$timeend,$mods) // now ask the modules if they want anything. foreach ($mods as $mod => $fname) { mtrace(' doing daily statistics for '.$mod->name); - $fname($course,$user,$timeend,($roleid == 'student') ? 1 : 2); + $fname($course,$user,$timeend,$roleid); } } @@ -649,26 +697,30 @@ function stats_do_aggregate_user_cron($course,$user,$roleid,$timesql,$timeend,$t insert_record('stats_user_'.$timestr,$stat,false); - if ($course->id == SITEID) { - $sql = 'SELECT sum(reads) as reads, sum(writes) as writes FROM '.$CFG->prefix.'stats_user_daily WHERE courseid = '.$course->id.' AND '.$timesql - ." AND roleid=".$roleid." AND userid = ".$stat->userid." AND stattype='logins'"; // add on roleid in case they have teacher and student records. - - $r = get_record_sql($sql); - $stat->reads = (empty($r->reads)) ? 0 : $r->reads; - $stat->writes = (empty($r->writes)) ? 0 : $r->writes; - - $stat->stattype = 'logins'; - - insert_record('stats_user_'.$timestr,$stat,false); - } - // now ask the modules if they want anything. foreach ($mods as $mod => $fname) { mtrace(' doing '.$timestr.' statistics for '.$mod->name); - $fname($course,$user,$timeend,($roleid == 'student') ? 1 : 2); + $fname($course,$user,$timeend,$roleid); } } +function stats_do_aggregate_user_login_cron($timesql,$timeend,$timestr) { + global $CFG; + + $sql = 'SELECT userid,roleid,sum(reads) as reads, sum(writes) as writes FROM '.$CFG->prefix.'stats_user_daily WHERE stattype = \'logins\' AND '.$timesql.' GROUP BY userid,roleid'; + + $users = get_records_sql($sql); + + foreach ($users as $stat) { + $stat->courseid = SITEID; + $stat->timeend = $timeend; + $stat->stattype = 'logins'; + + insert_record('stats_user_'.$timestr,$stat,false); + } +} + + function stats_get_time_options($now,$lastweekend,$lastmonthend,$earliestday,$earliestweek,$earliestmonth) { $now = stats_get_base_daily(time()); @@ -748,6 +800,15 @@ function stats_get_report_options($courseid,$mode) { $site = get_site(); $reportoptions[STATS_REPORT_USER_LOGINS] = get_string('statsreport'.STATS_REPORT_USER_LOGINS); } + break; + case STATS_MODE_RANKED: + if (isadmin()) { + $reportoptions[STATS_REPORT_ACTIVE_COURSES] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES); + $reportoptions[STATS_REPORT_ACTIVE_COURSES_WEIGHTED] = get_string('statsreport'.STATS_REPORT_ACTIVE_COURSES_WEIGHTED); + $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES); + $reportoptions[STATS_REPORT_PARTICIPATORY_COURSES_RW] = get_string('statsreport'.STATS_REPORT_PARTICIPATORY_COURSES_RW); + } + break; } return $reportoptions; @@ -767,7 +828,8 @@ function stats_fix_zeros($stats,$timeafter,$timestr,$line2=true,$line3=false) { $times = array(); // add something to timeafter since it is our absolute base $actualtimes = array_keys($stats); - $timeafter = $actualtimes[0]; + $timeafter = array_pop($actualtimes); + while ($timeafter < $now) { $times[] = $timeafter; if ($timestr == 'daily') {