From: nicolasconnault Date: Fri, 13 Feb 2009 08:04:10 +0000 (+0000) Subject: MDL-18163 Implemented stats report from LSU gradebook X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=1d1d29985f7c87ffd974724000f201ed60d088bc;p=moodle.git MDL-18163 Implemented stats report from LSU gradebook --- diff --git a/grade/report/grader/quickedit_item.php b/grade/report/grader/quickedit_item.php new file mode 100644 index 0000000000..18f88eea82 --- /dev/null +++ b/grade/report/grader/quickedit_item.php @@ -0,0 +1,115 @@ +libdir.'/gradelib.php'; +require_once $CFG->dirroot.'/grade/lib.php'; +require_once $CFG->dirroot.'/grade/report/grader/lib.php'; + +$courseid = required_param('id', PARAM_INT); // course id +$itemid = required_param('itemid', PARAM_INT); // item id +$page = optional_param('page', 0, PARAM_INT); // active page +$perpageurl = optional_param('perpage', 0, PARAM_INT); + +/// basic access checks +if (!$course = $DB->get_record('course', array('id' => $courseid))) { + print_error('nocourseid'); +} + +if (!$item = $DB->get_record('grade_items', array('id' => $itemid))) { + print_error('noitemid', 'grades'); +} + +require_login($course); +$context = get_context_instance(CONTEXT_COURSE, $course->id); + +require_capability('gradereport/grader:view', $context); +require_capability('moodle/grade:viewall', $context); +require_capability('moodle/grade:edit', $context); + +/// return tracking object +$gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'grader', 'courseid'=>$courseid, 'page'=>$page)); + +/// last selected report session tracking +if (!isset($USER->grade_last_report)) { + $USER->grade_last_report = array(); +} +$USER->grade_last_report[$course->id] = 'grader'; + +// Initialise the grader report object +$report = new grade_report_grader($courseid, $gpr, $context, $page); + +/// processing posted grades & feedback here +if ($data = data_submitted() and confirm_sesskey() and has_capability('moodle/grade:edit', $context)) { + $warnings = $report->process_data($data); +} else { + $warnings = array(); +} + +// Override perpage if set in URL +if ($perpageurl) { + $report->user_prefs['studentsperpage'] = $perpageurl; +} + +// final grades MUST be loaded after the processing +$report->load_users(); +$numusers = $report->get_numusers(); +$report->load_final_grades(); + +/// Print header +$a->item = $item->itemname; +$reportname = get_string('quickedititem', 'gradereport_grader', $a); +print_grade_page_head($COURSE->id, 'report', 'grader', $reportname); + +echo $report->group_selector; +echo '
'; +// echo $report->get_toggles_html(); + +//show warnings if any +foreach($warnings as $warning) { + notify($warning); +} + +$studentsperpage = $report->get_pref('studentsperpage'); +// Don't use paging if studentsperpage is empty or 0 at course AND site levels +if (!empty($studentsperpage)) { + print_paging_bar($numusers, $report->page, $studentsperpage, $report->pbarurl); +} + +/// TODO Print links to previous - next grade items in this course +/// TODO Print Quick Edit Interface here +/// TODO The teacher may only be allowed to view one group: check capabilities + +// print submit button +echo '
'; +echo ''; + +// prints paging bar at bottom for large pages +if (!empty($studentsperpage) && $studentsperpage >= 20) { + print_paging_bar($numusers, $report->page, $studentsperpage, $report->pbarurl); +} + +print_footer($course); +?> diff --git a/grade/report/stats.tar.bz2 b/grade/report/stats.tar.bz2 new file mode 100644 index 0000000000..85028e6bf4 Binary files /dev/null and b/grade/report/stats.tar.bz2 differ diff --git a/grade/report/stats/README.txt b/grade/report/stats/README.txt new file mode 100644 index 0000000000..0d0e1ce9bf --- /dev/null +++ b/grade/report/stats/README.txt @@ -0,0 +1,104 @@ + ____ __ __ ____ __ +/\ _`\ /\ \__ /\ \__ /\ _`\ /\ \__ +\ \,\L\_\\ \ ,_\ __ \ \ ,_\ ____ \ \ \L\ \ __ _____ ___ _ __ \ \ ,_\ + \/_\__ \ \ \ \/ /'__`\ \ \ \/ /',__\ \ \ , / /'__`\/\ '__`\ / __`\ /\`'__\\ \ \/ + /\ \L\ \\ \ \_ /\ \L\.\_\ \ \_ /\__, `\ \ \ \\ \ /\ __/\ \ \L\ \/\ \L\ \\ \ \/ \ \ \_ + \ `\____\\ \__\\ \__/.\_\\ \__\\/\____/ \ \_\ \_\\ \____\\ \ ,__/\ \____/ \ \_\ \ \__\ + \/_____/ \/__/ \/__/\/_/ \/__/ \/___/ \/_/\/ / \/____/ \ \ \/ \/___/ \/_/ \/__/ + \ \_\ + \/_/ + ,+7$$Z$ZO= + :+$$Z$$ZOZZZOOOOOOOOD,:,,, + +Z$ZOOO8OO8OOOOOOOOO8+====~~:, + 7$ZOOOOZOZOOOOOOOOOO8+++++=~:, + 77$88OZ8MMDZZZOOOOOOOZ+?++==~:, + 77Z$$Z8MM8OZOZZZZOOOO8ONDN+=~:, + I7$Z$NMOOOOOO$$$ZZOOOOOD8D8D8:, + II7$7MDZOOZZZZOZ$$ZZ$OOZNDNDDDD8DO ,IIII777I~ + 7?$$ZOMOOZZZ777MMMMMMMMMMMDNDN8DDDD$$7, =+++++???IIII7777$? + ,,,:Z==++===7NMMMND8DDDMMD8DDDDD777777:?=~~~~==+++???II777$$$, + ,:DM~~~~::,O8ND8OO8Z$$$$DDDDDD7IIIIII+~~~=?I77$$$$77IIII77$$Z+, + ,8,,,, ONND$ZZ$$777DDDDNDO$77II??+++I7$ZZOOOOZZ$77IIII7$ZZZ:, + M,::, O$D888ZO$II77DDD88OZ$7IIIIIII7$ZOO8OOOOOZ$77III77$ZO=: + =M,,, ?+~~+I$$$ZI778D$I?+=$77IIIII7$ZOO++===~~~$$7IIII7$ZOO~: + OM,,, I?==?I7$O8D777?+=:,,:77I?III7ZOO++=~:,,,,:77I?II7$ZOO=~, + 8MI::, I?=+II7$O8??+=~:, 77I?II7$ZOO+=~, ?7I??II$ZOO+~: + ~M=~~, I?++II7ZOO+=~, II?+?I7$ZOO=~, ,II+?II7ZOO+~: + M::~: I?++II7ZOO+=:, II++?I7$ZOO=~, II++II7ZOO+~: + M,:~: I?++II7ZOO+=: II++?II$ZOO=~, I?++II7ZOO+=: + M=::, I?++II7ZOO+=: II++?II$ZOO=~, I?++II7ZOO+=: + MI::: I?++?I7ZOO+=: II++?II$ZOO=~, I?=+II7ZOO+=: + ,M?:~: I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+=: + =M=:~: I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+=: + M,:~: I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+~: + M,:~: I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+~: + M,::, I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+~: + ,:, I?++?I7$OO+=: II+=?II$ZOO=~, I?=+II7ZOO+~: + , IIII77$ZOO+=: IIIII77$OOO=~, IIII77$ZOO+~: + 77$$ZZZOOO+=: 77$$ZZZOOOO=~, 77$ZZZOOOO+~: + $ZOOOOOOOO+=: $$ZOOOOOOOO=~, $ZOOOOOOOO+=: + + +=== About Stats Report === +The stats report gradebook plug-in was developed as part of a Google Summer of Code 2008 +project The goal of the plug-in is to provide a framework for providing text based +statistics for grades in a course. Several statistics such as Highest, Lowest, Mean, +Median, Mode, Percent Pass and Standard Deviation are included and new statistics can be +easily dropped in by developers. + + +=== About Author === +This plug-in was oringal created by a computer science student named Daniel Servos as part of +a Google Summer of Code 2008 project. + +==== Contact Information ==== +Name: Daniel Servos +E-mail: dservos@lakeheadu.ca +Blog: HackerDan.com + + +=== Copyright === +Moodle - Modular Object-Oriented Dynamic Learning Environment +http://moodle.org + +Copyright (C) 1999 onwards Martin Dougiamas http://moodle.com + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details: + +http://www.gnu.org/copyleft/gpl.html + + +=== Version / Release === +This plug-in was developed for Moodle 2.0 dev. + +This is the first real release of this plug-in and should be considered an beta version in that it +has not yet had a chance to be extensively tested by users in real life situations and may have +unknown bugs or issues. + +Version: 1.0.0b + + +=== Development Information === +==== Adding new Statistics ==== +To add a new statistic to the plug-in extend the abstract class stats (grade/report/stats/statistics/stats.php) and place you class in a file name stat_yournamehere.php in grade/report/stats/statistics and it should be automatically loaded in to plug-in. + +==== Specification ==== +http://docs.moodle.org/en/Student_projects/Animated_grade_statistics_report + +==== TO DO ===== +* Back port to Moodle 1.9.x +* Add more statistics. +* Add more settings for the report. +* Improve look. +* Add help windows/html. +* Add report defaults page. +* Deal with outcomes better/at all. +* Add export functionality to different formats. +* Improve this readme file. +* Improve documentation. \ No newline at end of file diff --git a/grade/report/stats/arrayview.php b/grade/report/stats/arrayview.php new file mode 100755 index 0000000000..53e76bf83a --- /dev/null +++ b/grade/report/stats/arrayview.php @@ -0,0 +1,47 @@ +'; +} +?> diff --git a/grade/report/stats/db/access.php b/grade/report/stats/db/access.php new file mode 100755 index 0000000000..f4fe096b3f --- /dev/null +++ b/grade/report/stats/db/access.php @@ -0,0 +1,115 @@ + array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:lowest' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:highest' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:passpercent' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:standarddeviation' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:mean' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:median' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ), + 'gradereport/stats:stat:mode' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'admin' => CAP_ALLOW + ) + ) +); + +?> diff --git a/grade/report/stats/index.php b/grade/report/stats/index.php new file mode 100755 index 0000000000..1e9cb9cc9f --- /dev/null +++ b/grade/report/stats/index.php @@ -0,0 +1,94 @@ +libdir.'/gradelib.php'; +require_once $CFG->dirroot.'/grade/lib.php'; +require_once $CFG->dirroot.'/grade/report/stats/lib.php'; + +$courseid = required_param('id'); +$toggle = optional_param('toggle', NULL, PARAM_INT); +$toggle_type = optional_param('toggle_type', 0, PARAM_ALPHANUM); + +/// basic access checks +$course = $DB->get_record('course', array('id' => $courseid)); + +if (!$course) { + print_error('nocourseid'); +} + +require_login($course); +$context = get_context_instance(CONTEXT_COURSE, $course->id); +require_capability('gradereport/stats:view', $context); + +/// get tracking object +$gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'stats', 'courseid'=>$courseid)); + +/// last selected report session tracking +if (!isset($USER->grade_last_report)) { + $USER->grade_last_report = array(); +} +$USER->grade_last_report[$course->id] = 'stats'; + +/// Build navigation +$strgrades = get_string('grades'); +$reportname = get_string('modulename', 'gradereport_stats'); +$navigation = grade_build_nav(__FILE__, $reportname, $courseid); + +/// Handle toggle change request +if (!is_null($toggle) && !empty($toggle_type)) { + set_user_preferences(array('grade_report_statsshow'.$toggle_type => $toggle)); +} + +grade_regrade_final_grades($courseid); + +/// Get report object +$report = new grade_report_stats($courseid, $gpr, $context); + +print_grade_page_head($courseid, 'report', 'stats', $reportname); + +/// Build report to output +$report->load_users(); +$report->harvest_data(); +$report->report_data(); +$report->adapt_data(); + +/// Print report +echo $report->group_selector; +echo '
'; +echo $report->get_toggles_html(); +echo '
'; +echo $report->html; + +/// Print footer +print_footer($course); + +?> diff --git a/grade/report/stats/lang/en_utf8/gradereport_stats.php b/grade/report/stats/lang/en_utf8/gradereport_stats.php new file mode 100755 index 0000000000..978520520f --- /dev/null +++ b/grade/report/stats/lang/en_utf8/gradereport_stats.php @@ -0,0 +1,79 @@ + \ No newline at end of file diff --git a/grade/report/stats/lib.php b/grade/report/stats/lib.php new file mode 100755 index 0000000000..976a714c98 --- /dev/null +++ b/grade/report/stats/lib.php @@ -0,0 +1,573 @@ + dirroot . '/grade/report/lib.php'); +require_once($CFG->libdir.'/tablelib.php'); + +foreach (glob($CFG->dirroot . '/grade/report/stats/statistics/stat_*.php') as $filename) { + require_once($filename); +} + +/** + * Class providing the API for the stats report, including harvesters, + * reports, and adaptor methods for turing grades in to statistics. + * @uses grade_report + * @package gradebook + */ +class grade_report_stats extends grade_report { + /** + * Capability to view hidden items. + * @var bool $canviewhidden + */ + private $canviewhidden; + + /** + * Grade objects of users in the course + * @var array $grades + */ + private $grades = array(); + + /** + * Array of users final grades affter filtered based on + * settings. + * @var array $finalgrades + */ + private $finalgrades = array(); + + /** + * The value returned from each statistic. + * @var array $reportedstats + */ + private $reportedstats = array(); + + /** + * The html of the report to output. + * @var string $html + */ + public $html; + + /** + * The table class used to make the html of the report. + * @var object $table + */ + private $table; + + /** + * Array of clases that extend stats witch have the logic to + * generate the statstics. + * @var array $stats + */ + private static $stats = array(); + + /** + * Constructor. Initialises grade_tree, sets up group, baseurl + * and pbarurl. + * @param int $courseid the coures id for the report + * @param object $gpr grade plugin tracking object + * @context string $context + */ + public function __construct($courseid, $gpr, $context) { + global $CFG; + parent::__construct($courseid, $gpr, $context, null); + + $this->canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->course->id)); + + /// Set up urls + $this->baseurl = 'index.php?id=' . $this->courseid; + $this->pbarurl = 'index.php?id=' . $this->courseid; + + /// Set the position of the aggregation categorie based on pref + $switch = $this->get_pref('statsaggregationposition'); + if ($switch == '' && isset($CFG->grade_aggregationposition)) { + $switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition); + } + + /// Build grade tree + $this->gtree = new grade_tree($this->courseid, false, $switch); + + $this->course->groupmode = 2; + /// Set up Groups + if ($this->get_pref('statsshowgroups') || is_null($this->get_pref('statsshowgroups'))) { + $this->setup_groups(); + } + + /// Load stats classes from ./statistics + $this->load_stats(); + } + + /** + * Load all the stats classes into $stats. + * Looks in /statistics and trys to make an instence of any + * class that is in a file that starts with stats_ and extends + * stats directly. + * @param bool $return if true return stats array, else store in $this->stats + * @returns array array of clases that extend stats + */ + private function load_stats($return=false) { + global $CFG; + + $stats = array(); + + foreach (glob($CFG->dirroot . '/grade/report/stats/statistics/stat_*.php') as $path) { + $filename = substr(basename($path, '.php'), 5); + + if(class_exists($filename) && get_parent_class($class = new $filename) == 'stats' ) { + $stats[$filename] = $class; + } + } + + if($return) { + return $stats; + } else { + grade_report_stats::$stats = $stats; + } + } + + /** + * Returns the current stats being used in the report or if no stats are + * set, it returns the stats as whould be loaded by load_stats. + * @returns array array of classes that extend stats + */ + public function get_stats() { + if(!isset(grade_report_stats::$stats) || is_null(grade_report_stats::$stats) || empty(grade_report_stats::$stats)) { + return grade_report_stats::load_stats(true); + } else { + return grade_report_stats::$stats; + } + } + + /// Added to keep grade_report happy + public function process_data($data){} + public function process_action($target, $action){} + + /** + * Based on load user function from grader report. + * Pulls out the userids of the users to be used in the stats. + * @return array array of user ids to use in stats + */ + public function load_users() { + global $CFG, $DB; + + $params = array(); + list($usql, $gbr_params) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED); + + $sql = "SELECT u.id + FROM {user} u + JOIN {role_assignments} ra ON u.id = ra.userid + $this->groupsql + WHERE ra.roleid $usql + $this->groupwheresql + AND ra.contextid ".get_related_contexts_string($this->context); + + $params = array_merge($gbr_params, $this->groupwheresql_params); + + $this->users = $DB->get_records_sql($sql, $params); + + if (empty($this->users)) { + $this->userselect = ''; + $this->users = array(); + $this->userselect_params = array(); + } else { + if(isset($DB) && !is_null($DB)) { + list($usql, $params) = $DB->get_in_or_equal(array_keys($this->users)); + $this->userselect = "AND g.userid $usql"; + $this->userselect_params = $params; + }else{ + $this->userselect = 'AND g.userid in ('.implode(',', array_keys($this->users)).')'; + } + } + + return $this->users; + } + + /** + * Encode an array of stat's data in to a stirng so it can + * be put in the get part of a url. + * @param arrray $array array of data to encode + * @param object $item grade_item to use to fromat stats + * @param object $stat stats object to use to format stats + * @return string encoded string + */ + private function encode_array(array $array, $item, $stat) { + $string = ''; + + /// Encode each elment by puting a delimiter and base64 encoding it. + foreach($array as $id=>$data) { + $string .= addslashes(grade_format_gradevalue($data, $item, true, $stat->displaytype, $stat->decimals)) . '"'; + } + return strtr(base64_encode($string), '+/=', '-_,'); + } + + /** + * Harvest the grades from the data base and build the finalgrades array. + * Filters out hidden, locked and null grades based on users settings. + * Partly based on grader reports load_final_grades function. + */ + public function harvest_data() { + global $CFG, $DB; + + $params = array(); + + if(isset($DB) && !is_null($DB)) { + $params = array_merge(array($this->courseid), $this->userselect_params); + + /// please note that we must fetch all grade_grades fields if we want to contruct grade_grade object from it! + $sql = "SELECT g.* + FROM {grade_items} gi, + {grade_grades} g + WHERE g.itemid = gi.id AND gi.courseid = ? {$this->userselect}"; + + $grades = $DB->get_records_sql($sql, $params); + } else { + /// please note that we must fetch all grade_grades fields if we want to contruct grade_grade object from it! + $sql = "SELECT g.* + FROM {$CFG->prefix}grade_items gi, + {$CFG->prefix}grade_grades g + WHERE g.itemid = gi.id AND gi.courseid = {$this->courseid} {$this->userselect}"; + + $grades = get_records_sql($sql); + } + + $userids = array_keys($this->users); + + if ($grades) { + foreach ($grades as $graderec) { + if (in_array($graderec->userid, $userids) and array_key_exists($graderec->itemid, $this->gtree->items)) { // some items may not be present!! + $this->grades[$graderec->itemid][$graderec->userid] = new grade_grade($graderec, false); + $this->grades[$graderec->itemid][$graderec->userid]->grade_item =& $this->gtree->items[$graderec->itemid]; // db caching + } + } + } + + /// prefil grades that do not exist yet + foreach ($userids as $userid) { + foreach ($this->gtree->items as $itemid=>$unused) { + if (!isset($this->grades[$itemid][$userid])) { + $this->grades[$itemid][$userid] = new grade_grade(); + $this->grades[$itemid][$userid]->itemid = $itemid; + $this->grades[$itemid][$userid]->userid = $userid; + $this->grades[$itemid][$userid]->grade_item =& $this->gtree->items[$itemid]; // db caching + } + } + } + + $this->finalgrades = array(); + + /// Build finalgrades array and filliter out unwanted grades. + foreach ($this->gtree->items as $id=>$item) { + if(($item->gradetype == GRADE_TYPE_SCALE && ($this->get_pref('statsshowscaleitems') || is_null($this->get_pref('statsshowscaleitems')))) + || ($item->gradetype == GRADE_TYPE_VALUE && ($this->get_pref('statsshowvalueitems') || is_null($this->get_pref('statsshowvalueitems'))))) { + $this->finalgrades[$id] = array(); + $i = 0; + + if(isset($this->grades[$id]) && !is_null($this->grades[$id])) { + foreach ($this->grades[$id] as $grade) { + if( (($grade->is_hidden() && $this->canviewhidden && ($this->get_pref('statsusehidden') || is_null($this->get_pref('statsusehidden')))) || !$grade->is_hidden()) + && (($grade->is_locked() && ($this->get_pref('statsuselocked') || is_null($this->get_pref('statsuselocked')))) || !$grade->is_locked())) { + if($this->get_pref('statsincompleasmin') && is_null($grade->finalgrade)) { + $this->finalgrades[$id][$i] = $item->grademin; + $i++; + } elseif(!is_null($grade->finalgrade)) { + $this->finalgrades[$id][$i] = $grade->finalgrade; + $i++; + } + } + } + } + } + } + } + + /** + * Runs grades for each item threw the report functions + * of each stats class in $stats and stores the values in + * reportedstats. + */ + public function report_data() { + $this->reportedstats = array(); + + foreach(grade_report_stats::$stats as $name=>$stat) { + if(($stat->capability == null || has_capability($stat->capability, $this->context)) && ($this->get_pref('stats'. $name) || is_null($this->get_pref('stats'. $name)))) { + $this->reportedstats[$name] = array(); + + foreach($this->finalgrades as $itemid=>$item) { + sort($item); + if(count($item) > 0) { + $this->reportedstats[$name][$itemid] = $stat->report_data($item, $this->gtree->items[$itemid]); + } else { + $this->reportedstats[$name][$itemid] = null; + } + } + } + } + } + + /** + * Take the reported data and adapt it in to HTML to output. + * HTML is stored in html. + * TODO: Deal with tables growing to wide. + * TODO: Make it look nice. + */ + public function adapt_data($printerversion = false) { + global $CFG; + + $inverted = $this->get_pref('statsshowinverted'); + + /// Set up table arrays + $tablecolumns = array('statistic'); + $tableheaders = array($this->get_lang_string('statistic', 'gradereport_stats')); + + /// Loop threw items and build arrays + if ($inverted) { + if($this->get_pref('statsshowranges')) { + array_push($tablecolumns, 'range'); + array_push($tableheaders, $this->get_lang_string('range', 'gradereport_stats')); + } + + foreach($this->reportedstats as $name=>$data) { + array_push($tablecolumns, $name); + array_push($tableheaders, grade_report_stats::$stats[$name]->name); + } + + if($this->get_pref('statsshownumgrades')) { + array_push($tablecolumns, 'num_grades'); + array_push($tableheaders, $this->get_lang_string('num_grades', 'gradereport_stats')); + } + } else { + /// Set up range column and number of grades column + $ranges = array(format_text('' . $this->get_lang_string('range', 'gradereport_stats') . '', FORMAT_HTML)); + $numgrades = array(format_text('' . $this->get_lang_string('num_grades', 'gradereport_stats') . '', FORMAT_HTML)); + + foreach($this->finalgrades as $itemid=>$grades) { + array_push($tablecolumns, $itemid); + array_push($tableheaders, format_text($this->gtree->items[$itemid]->get_name(), FORMAT_HTML)); + array_push($ranges, format_text('' . grade_format_gradevalue($this->gtree->items[$itemid]->grademin, $this->gtree->items[$itemid], true) . '-' . grade_format_gradevalue($this->gtree->items[$itemid]->grademax, $this->gtree->items[$itemid], true) . '' , FORMAT_HTML)); + array_push($numgrades, format_text(count($grades), FORMAT_HTML)); + } + } + + /// Set up flexible table + $this->table = new flexible_table('grade-report-stats-' . $this->courseid); + $this->table->define_columns($tablecolumns); + $this->table->define_headers($tableheaders); + if ($printerversion) { + $this->table->collapsible(false); + $this->table->set_attribute('cellspacing', '1'); + $this->table->set_attribute('border', '1'); + } else { + $this->table->define_baseurl($this->baseurl); + $this->table->collapsible(true); + $this->table->set_attribute('cellspacing', '1'); + $this->table->set_attribute('id', 'stats-grade'); + $this->table->set_attribute('class', 'grade-report-stats gradestable flexible'); + } + $this->table->setup(); + + /// If ranges are being shown add them to the table + if(!$inverted){ + if ($this->get_pref('statsshowranges')){ + $this->table->add_data($ranges); + $this->table->add_separator(); + } + } + + /// Loop threw all the reported data and format it in to cells + /// If stat retured an array of values display the elements or + /// make a link to a popup with the data in it. + if($inverted) { + foreach($this->finalgrades as $itemid=>$grades) { + $item = $this->gtree->items[$itemid]; + $row = array(format_text('' . $item->get_name() . '' , FORMAT_HTML)); + + if($this->get_pref('statsshowranges')) { + array_push($row, format_text('' . grade_format_gradevalue($item->grademin, $item, true) . '-' . grade_format_gradevalue($item->grademax, $item, true) . '' , FORMAT_HTML)); + } + + foreach($this->reportedstats as $name=>$data) { + $stat = $data[$itemid]; + + if(!is_array($stat)) { + array_push($row, format_text(grade_format_gradevalue($stat, $item, true, grade_report_stats::$stats[$name]->displaytype, grade_report_stats::$stats[$name]->decimals), FORMAT_HTML)); + } else { + $statstring = ""; + + for($i = 0; $i < 2; $i++) { + if($i >= count($stat)) { + break; + } + $statstring .= grade_format_gradevalue($stat[$i], $item, true, grade_report_stats::$stats[$name]->displaytype, grade_report_stats::$stats[$name]->decimals) . ', '; + } + + if($i < count($stat)) { + if(!$printerversion) { + $statstring = "wwwroot}/grade/report/stats/arrayview.php?id={$this->courseid}&data={$this->encode_array($stat, $item, grade_report_stats::$stats[$name])}','{$this->get_lang_string('moredata', 'gradereport_stats')}','width=300,height=500,menubar=no,status=no,location=no,directories=no,toolbar=no,scrollbars=yes');\">". format_text($statstring, FORMAT_HTML) . '....'; + } else { + $statstring .= '...'; + } + } else { + $statstring = substr($statstring, 0, strlen($statstring) - 2); + } + array_push($row, $statstring); + } + } + + if($this->get_pref('statsshownumgrades')) { + array_push($row, format_text(count($grades), FORMAT_HTML)); + } + + $this->table->add_data($row); + } + } else { + foreach($this->reportedstats as $name=>$data) { + $row = array(format_text('' . grade_report_stats::$stats[$name]->name . '', FORMAT_HTML)); + + foreach($data as $itemid=>$stat) { + if(!is_array($stat)) { + array_push($row, format_text(grade_format_gradevalue($stat, $this->gtree->items[$itemid], true, grade_report_stats::$stats[$name]->displaytype, grade_report_stats::$stats[$name]->decimals), FORMAT_HTML)); + } else { + $statstring = ""; + + for($i = 0; $i < 2; $i++) { + if($i >= count($stat)) { + break; + } + $statstring .= grade_format_gradevalue($stat[$i], $this->gtree->items[$itemid], true, grade_report_stats::$stats[$name]->displaytype, grade_report_stats::$stats[$name]->decimals) . ', '; + } + + if($i < count($stat)) { + if(!$printerversion) { + $statstring = "wwwroot}/grade/report/stats/arrayview.php?id={$this->courseid}&data={$this->encode_array($stat, $this->gtree->items[$itemid], grade_report_stats::$stats[$name])}','{$this->get_lang_string('moredata', 'gradereport_stats')}','width=300,height=500,menubar=no,status=no,location=no,directories=no,toolbar=no,scrollbars=yes');\">". format_text($statstring, FORMAT_HTML) . '....'; + } else { + $statstring .= '...'; + } + } else { + $statstring = substr($statstring, 0, strlen($statstring) - 2); + } + array_push($row, $statstring); + } + } + $this->table->add_data($row); + } + } + + /// If the number of grades is being shown add it to the table. + if(!$inverted) { + if ($this->get_pref('statsshownumgrades')){ + $this->table->add_separator(); + $this->table->add_data($numgrades); + } + } + + /// Build html + ob_start(); + if($this->currentgroup == 0) { + echo format_text('Group: All participants', FORMAT_HTML); + } else { + echo format_text('Group: ' . groups_get_group_name($this->currentgroup), FORMAT_HTML); + } + $this->table->print_html(); + $this->html = ob_get_clean(); + } + + /** + * Builds HTML for toggles on top of report. + * Based on grader report get_toggles_html + * @return string html code for toggles. + */ + public function get_toggles_html() { + global $CFG, $USER; + + $html = '
'; + $html .= $this->print_toggle('numgrades', true); + $html .= $this->print_toggle('groups', true); + $html .= $this->print_toggle('ranges', true); + $html .= $this->print_toggle('inverted', true); + $html .= '
'; + + return $html; + } + + /** + * Builds HTML for each individual toggle. + * Based on grader report print_toggle + * @param string $type The toggle type. + * @param bool $return Wheather ro return the HTML or print it. + */ + private function print_toggle($type, $return=false) { + global $CFG; + + $icons = array('eyecons' => 't/hide.gif', + 'numgrades' => 't/grades.gif', + 'calculations' => 't/calc.gif', + 'locks' => 't/lock.gif', + 'averages' => 't/mean.gif', + 'inverted' => 't/switch_whole.gif', + 'nooutcomes' => 't/outcomes.gif'); + + $pref_name = 'grade_report_statsshow' . $type; + + if (array_key_exists($pref_name, $CFG)) { + $show_pref = get_user_preferences($pref_name, $CFG->$pref_name); + } else { + $show_pref = get_user_preferences($pref_name); + } + + $strshow = $this->get_lang_string('show' . $type, 'gradereport_stats'); + $strhide = $this->get_lang_string('hide' . $type, 'gradereport_stats'); + + $show_hide = 'show'; + $toggle_action = 1; + + if ($show_pref) { + $show_hide = 'hide'; + $toggle_action = 0; + } + + if (array_key_exists($type, $icons)) { + $image_name = $icons[$type]; + } else { + $image_name = "t/$type.gif"; + } + + $string = ${'str' . $show_hide}; + + $img = ''
+                      .$string.''. "\n"; + + $retval = $img . '" + . format_text($string, FORMAT_HTML) . ' '; + + if ($return) { + return $retval; + } else { + echo $retval; + } + } +} +?> diff --git a/grade/report/stats/preferences.php b/grade/report/stats/preferences.php new file mode 100755 index 0000000000..05c1f1e44e --- /dev/null +++ b/grade/report/stats/preferences.php @@ -0,0 +1,95 @@ +libdir . '/gradelib.php'; +require_once $CFG->dirroot.'/grade/lib.php'; + +$courseid = required_param('id', PARAM_INT); + + +/// Make sure they can even access this course +if(isset($DB) && !is_null($DB)) { + $course = $DB->get_record('course', array('id' => $courseid)); +} else { + $course = get_record('course', 'id', $courseid); +} + +if (!$course) { + print_error('nocourseid'); +} + +require_login($course->id); + +$context = get_context_instance(CONTEXT_COURSE, $course->id); +$systemcontext = get_context_instance(CONTEXT_SYSTEM); +require_capability('gradereport/stats:view', $context); + + +require('preferences_form.php'); +$mform = new stats_report_preferences_form('preferences.php', compact('course')); + +// If data submitted, then process and store. +if ($data = $mform->get_data()) { + foreach ($data as $preference => $value) { + if (substr($preference, 0, 18) !== 'grade_report_stats') { + continue; + } + + if ($value == GRADE_REPORT_PREFERENCE_DEFAULT || strlen($value) == 0) { + unset_user_preference($preference); + } else { + set_user_preference($preference, $value); + } + } +} + +/// If cancelled go back to report +if ($mform->is_cancelled()){ + redirect($CFG->wwwroot . '/grade/report/stats/index.php?id='.$courseid); +} + +print_grade_page_head($courseid, 'settings', 'stats', get_string('preferences', 'gradereport_stats')); + +/// If USER has admin capability, print a link to the site config page for this report +/// TODO: Add admin config page for this report +if (has_capability('moodle/site:config', $systemcontext)) { + echo '\n"; +} + +print_simple_box_start("center"); + +$mform->display(); +print_simple_box_end(); + +print_footer($course); +?> diff --git a/grade/report/stats/preferences_form.php b/grade/report/stats/preferences_form.php new file mode 100755 index 0000000000..eff26cba56 --- /dev/null +++ b/grade/report/stats/preferences_form.php @@ -0,0 +1,181 @@ +libdir.'/formslib.php'); +require_once($CFG->dirroot . '/grade/report/stats/lib.php'); + +/** + * Moodle form to be used to set user preferences for report/stats + * gradebook plugin. + * @uses moodleform + */ +class stats_report_preferences_form extends moodleform { + + /** + * Fourm definition. + */ + public function definition() { + global $USER, $CFG; + + $stats = grade_report_stats::get_stats(); + + $mform =& $this->_form; + $course = $this->_customdata['course']; + + $context = get_context_instance(CONTEXT_COURSE, $course->id); + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + $stryes = get_string('yes'); + $strno = get_string('no'); + + $checkbox_default = array(GRADE_REPORT_PREFERENCE_DEFAULT => '*default*', 0 => $strno, 1 => $stryes); + + $advanced = array(); + $preferences = array(); + + $preferences['prefgeneral'] = array( + /* 'aggregationview' => array(GRADE_REPORT_PREFERENCE_DEFAULT => '*default*', + GRADE_REPORT_AGGREGATION_VIEW_FULL => get_string('fullmode', 'grades'), + GRADE_REPORT_AGGREGATION_VIEW_AGGREGATES_ONLY => get_string('aggregatesonly', 'grades'), + GRADE_REPORT_AGGREGATION_VIEW_GRADES_ONLY => get_string('gradesonly', 'grades') + ),*/ + + 'aggregationposition' => array(GRADE_REPORT_PREFERENCE_DEFAULT => '*default*', + GRADE_REPORT_AGGREGATION_POSITION_FIRST => get_string('positionfirst', 'grades'), + GRADE_REPORT_AGGREGATION_POSITION_LAST => get_string('positionlast', 'grades') + ) + ); + $preferences['prefstats'] = array(); + $preferences['prefshow'] = array(); + + foreach($stats as $key=>$stat) { + $preferences['prefstats'][$key] = $checkbox_default; + } + + $preferences['prefshow']['showgroups'] = $checkbox_default; + $preferences['prefshow']['showranges'] = $checkbox_default; + $preferences['prefshow']['shownumgrades'] = $checkbox_default; + $preferences['prefshow']['showscaleitems'] = $checkbox_default; + $preferences['prefshow']['showvalueitems'] = $checkbox_default; + $preferences['prefshow']['showinverted'] = $checkbox_default; + + $preferences['prefcalc']['incompleasmin'] = $checkbox_default; + $preferences['prefcalc']['usehidden'] = $checkbox_default; + $preferences['prefcalc']['uselocked'] = $checkbox_default; + + foreach ($preferences as $group => $prefs) { + $mform->addElement('header', $group, get_string($group, 'gradereport_stats')); + + foreach ($prefs as $pref => $type) { + $full_pref = 'grade_report_stats' . $pref; + $pref_value = get_user_preferences($full_pref); + $course_value = null; + //$options = $type; + //$type = 'select'; + + if (!empty($CFG->{$full_pref})) { + $course_value = grade_get_setting($course->id, $pref, $CFG->{$full_pref}); + } + + $options = null; + if (is_array($type)) { + $options = $type; + $type = 'select'; + // MDL-11478 + // get default aggregationposition from grade_settings + if (!empty($CFG->{$full_pref})) { + $course_value = grade_get_setting($course->id, $pref, $CFG->{$full_pref}); + } + + if ($pref == 'aggregationposition') { + if (!empty($options[$course_value])) { + $default = $options[$course_value]; + } else { + $default = $options[$CFG->grade_aggregationposition]; + } + } elseif (isset($options[$CFG->{$full_pref}])) { + $default = $options[$CFG->{$full_pref}]; + } else { + $default = ''; + } + } else { + $default = $CFG->$full_pref; + } + /* + if ($pref == 'aggregationposition') { + if (!empty($options[$course_value])) { + $default = $options[$course_value]; + } elseif(isset($CFG->grade_aggregationposition)) { + $default = $options[$CFG->grade_aggregationposition]; + } + } elseif ($pref == 'aggregationview' && isset($CFG->grade_report_aggregationview) && isset($options[$CFG->grade_report_aggregationview])) { + $default = $options[$CFG->grade_report_aggregationview]; + } else { + if (!empty($options[$course_value])) { + $default = $options[$course_value]; + } else { + if ($pref == 'incompleasmin') { + $default = $strno; + } else { + $default = $stryes; + } + } + }*/ + + $help_string = get_string("config$pref", 'gradereport_stats'); + + // Replace the '*default*' value with the site default language string - 'default' might collide with custom language packs + if (!is_null($options) AND isset($options[GRADE_REPORT_PREFERENCE_DEFAULT]) && $options[GRADE_REPORT_PREFERENCE_DEFAULT] == '*default*') { + $options[GRADE_REPORT_PREFERENCE_DEFAULT] = get_string('reportdefault', 'grades', $default); + } elseif ($type == 'text') { + $help_string = get_string("config{$pref}default", 'gradereport_stats', $default); + } + + if($group == 'prefstats') { + $label = get_string('statsshow', 'gradereport_stats') . ' ' . $stats[$pref]->name; + } else { + $label = get_string($pref, 'gradereport_stats'); + } + + $mform->addElement($type, $full_pref, $label, $options); + $mform->setHelpButton($full_pref, array('stats' . $pref, get_string($pref, 'gradereport_stats'), 'grade'), true); + $mform->setDefault($full_pref, $pref_value); + $mform->setType($full_pref, PARAM_ALPHANUM); + } + } + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + $mform->setDefault('id', $course->id); + + $this->add_action_buttons(); + } +} + +?> diff --git a/grade/report/stats/print.php b/grade/report/stats/print.php new file mode 100644 index 0000000000..de9033a657 --- /dev/null +++ b/grade/report/stats/print.php @@ -0,0 +1,80 @@ +libdir.'/gradelib.php'; +require_once $CFG->dirroot.'/grade/lib.php'; +require_once $CFG->dirroot.'/grade/report/stats/lib.php'; + +$courseid = required_param('id'); + +$reportname = get_string('modulename', 'gradereport_stats'); + +/// basic access checks +if(isset($DB) && !is_null($DB)) { + $course = $DB->get_record('course', array('id' => $courseid)); +} else { + $course = get_record('course', 'id', $courseid); +} +if (!$course) { + print_error('nocourseid'); +} +require_login($course); +$context = get_context_instance(CONTEXT_COURSE, $course->id); +require_capability('gradereport/stats:view', $context); + +/// get tracking object +$gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'stats', 'courseid'=>$courseid)); + +/// last selected report session tracking +if (!isset($USER->grade_last_report)) { + $USER->grade_last_report = array(); +} +$USER->grade_last_report[$course->id] = 'stats'; + +grade_regrade_final_grades($courseid); + +/// Get report object +$report = new grade_report_stats($courseid, $gpr, $context); + + +/// Build report to output +$report->load_users(); +$report->harvest_data(); +$report->report_data(); +$report->adapt_data(true); + +/// Print report +echo '' . $reportname . ' for ' . $course->shortname . ''; +echo '
' . $course->fullname . ': ' . $reportname. '
'; +echo '
' . userdate(time()) . '
'; +echo '

'; +echo $report->html; +echo ''; +?> \ No newline at end of file diff --git a/grade/report/stats/settings.php b/grade/report/stats/settings.php new file mode 100644 index 0000000000..8037e7deba --- /dev/null +++ b/grade/report/stats/settings.php @@ -0,0 +1,101 @@ +add(new admin_setting_configselect('grade_report_stats_aggregationposition', + get_string('aggregationposition', 'grades'), + get_string('configaggregationposition', 'grades'), + GRADE_REPORT_AGGREGATION_POSITION_LAST, + array(GRADE_REPORT_AGGREGATION_POSITION_FIRST => get_string('positionfirst', 'grades'), + GRADE_REPORT_AGGREGATION_POSITION_LAST => get_string('positionlast', 'grades')))); + +$settings->add(new admin_setting_configcheckbox('grade_report_statshighest', + $strshow . get_string('highest', 'gradereport_stats'), + get_string('stats:stat:highest', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statslowest', + $strshow . get_string('lowest', 'gradereport_stats'), + get_string('stats:stat:lowest', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsmean', + $strshow . get_string('mean', 'gradereport_stats'), + get_string('stats:stat:mean', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsmedian', + $strshow . get_string('median', 'gradereport_stats'), + get_string('stats:stat:median', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsmode', + $strshow . get_string('mode', 'gradereport_stats'), + get_string('stats:stat:mode', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statspass_percent', + $strshow . get_string('pass_percent', 'gradereport_stats'), + get_string('stats:stat:passpercent', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsstandard_deviation', + $strshow . get_string('standarddeviation', 'gradereport_stats'), + get_string('stats:stat:standarddeviation', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshowgroups', + $strshow . get_string('showgroups', 'gradereport_stats'), + get_string('showgroups', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshowranges', + $strshow . get_string('showranges', 'gradereport_stats'), + get_string('showranges', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshownumgrades', + $strshow . get_string('shownumgrades', 'gradereport_stats'), + get_string('shownumgrades', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshowscaleitems', + $strshow . get_string('showscaleitems', 'gradereport_stats'), + get_string('showscaleitems', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshowvalueitems', + $strshow . get_string('showvalueitems', 'gradereport_stats'), + get_string('showvalueitems', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsshowinverted', + $strshow . get_string('showinverted', 'gradereport_stats'), + get_string('showinverted', 'gradereport_stats'), 1)) +; +$settings->add(new admin_setting_configcheckbox('grade_report_statsincompleasmin', + $strshow . get_string('incompleasmin', 'gradereport_stats'), + get_string('incompleasmin', 'gradereport_stats'), 0)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsusehidden', + $strshow . get_string('usehidden', 'gradereport_stats'), + get_string('usehidden', 'gradereport_stats'), 1)); + +$settings->add(new admin_setting_configcheckbox('grade_report_statsuselocked', + $strshow . get_string('uselocked', 'gradereport_stats'), + get_string('uselocked', 'gradereport_stats'), 1)); + +?> diff --git a/grade/report/stats/statistics/stat_highest.php b/grade/report/stats/statistics/stat_highest.php new file mode 100755 index 0000000000..eb353aad50 --- /dev/null +++ b/grade/report/stats/statistics/stat_highest.php @@ -0,0 +1,41 @@ +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** +* Stats class for finding the highest grade for an item in a course. +*/ +class highest extends stats { + public function __construct() { + parent::__construct(get_string('highest', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:highest'; + } + + public function report_data($final_grades, $item=null){ + return max($final_grades); + } +} + +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_lowest.php b/grade/report/stats/statistics/stat_lowest.php new file mode 100755 index 0000000000..44100e9abe --- /dev/null +++ b/grade/report/stats/statistics/stat_lowest.php @@ -0,0 +1,41 @@ +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** +* Stats class for finding the lowest grade for an item in a course. +*/ +class lowest extends stats { + public function __construct() { + parent::__construct(get_string('lowest', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:lowest'; + } + + public function report_data($final_grades, $item=null){ + return min($final_grades); + } +} + +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_mean.php b/grade/report/stats/statistics/stat_mean.php new file mode 100755 index 0000000000..1ad200f1d5 --- /dev/null +++ b/grade/report/stats/statistics/stat_mean.php @@ -0,0 +1,41 @@ +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** + * Stats class for finding the mean of the set of grades for an item in a course. + */ +class mean extends stats { + public function __construct() { + parent::__construct(get_string('mean', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:mean'; + } + + public function report_data($final_grades, $item=null){ + return (array_sum($final_grades) / count($final_grades)); + } +} + +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_median.php b/grade/report/stats/statistics/stat_median.php new file mode 100755 index 0000000000..1b15862ef0 --- /dev/null +++ b/grade/report/stats/statistics/stat_median.php @@ -0,0 +1,42 @@ +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** + * Stats class for finding the median of a set of grades for an item in a course. + */ +class median extends stats { + public function __construct() { + parent::__construct(get_string('median', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:median'; + } + + public function report_data($final_grades, $item=null){ + $midpoint = (count($final_grades) - 1) / 2; + return ($final_grades[floor($midpoint)] + $final_grades[ceil($midpoint)]) / 2; + } +} + +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_mode.php b/grade/report/stats/statistics/stat_mode.php new file mode 100755 index 0000000000..7bafd80359 --- /dev/null +++ b/grade/report/stats/statistics/stat_mode.php @@ -0,0 +1,64 @@ + +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** + * Stats class for finding the mode of a set of grades in an item in a course. + * NOTE: To pervent students from being able to find a list of all grades when + * there are no grades the same (or to many in the same) the mode will not be + * shown when there are over $maxmode number of modes. + */ +class mode extends stats { + public static $maxmode = 5; + + public function __construct() { + parent::__construct(get_string('mode', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:mode'; + } + + public function report_data($final_grades, $item=null){ + $occurrences = array(); + $modes = array(); + + foreach($final_grades as $grade) { + if(!array_key_exists(sprintf('%f', round($grade,2)), $occurrences)) { + $occurrences[sprintf('%f', round($grade,2))] = 1; + } else { + $occurrences[sprintf('%f', round($grade,2))]++; + } + } + + arsort($occurrences); + $modes = array_keys($occurrences, current($occurrences)); + + if(count($modes) <= mode::$maxmode) { + return $modes; + } else { + return null; + } + } +} +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_pass_percent.php b/grade/report/stats/statistics/stat_pass_percent.php new file mode 100755 index 0000000000..023a32a2fc --- /dev/null +++ b/grade/report/stats/statistics/stat_pass_percent.php @@ -0,0 +1,52 @@ + dirroot . '/grade/report/stats/statistics/stats.php'); +require_once($CFG->dirroot . '/lib/grade/grade_grade.php'); + +/** + * Stats class for finding the percent of grades above the set + * gradepass for the item in a course. + * NOTE: This statistic depends on gradepass being set for an item, + * by defualt it is set to 0, witch is also noramly what the mingrade is + * so it will show the pass percent as 100% if everything is left as default. + */ +class pass_percent extends stats { + public function __construct() { + parent::__construct(get_string('pass_percent', 'gradereport_stats'), GRADE_DISPLAY_TYPE_PERCENTAGE); + $this->capability = 'gradereport/stats:stat:passpercent'; + } + + public function report_data($final_grades, $item=null){ + $numpass = 0; + foreach($final_grades as $grade) { + if($grade >= $item->gradepass) { + $numpass++; + } + } + + return grade_grade::standardise_score($numpass / count($final_grades), 0, 1, $item->grademin, $item->grademax); + } +} +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stat_standard_deviation.php b/grade/report/stats/statistics/stat_standard_deviation.php new file mode 100755 index 0000000000..c52955dda3 --- /dev/null +++ b/grade/report/stats/statistics/stat_standard_deviation.php @@ -0,0 +1,49 @@ +dirroot . '/grade/report/stats/statistics/stats.php'); + +/** + * Stats class for finding the standard deviation of the grades for an item in a course. + */ +class standard_deviation extends stats { + public function __construct() { + parent::__construct(get_string('standarddeviation', 'gradereport_stats')); + $this->capability = 'gradereport/stats:stat:standarddeviation'; + } + + public function report_data($final_grades, $item=null){ + $sum = 0; + $n = count($final_grades); + $avg = array_sum($final_grades) / $n; + + foreach($final_grades as $grade) { + $sum += pow($grade - $avg, 2); + } + + return sqrt($sum / $n); + } +} + +?> \ No newline at end of file diff --git a/grade/report/stats/statistics/stats.php b/grade/report/stats/statistics/stats.php new file mode 100755 index 0000000000..afa941b802 --- /dev/null +++ b/grade/report/stats/statistics/stats.php @@ -0,0 +1,82 @@ +name = $name; + $this->displaytype = $displaytype; + $this->decimals = $decimals; + + if($name == null) { + $this->name = get_string('statistic', 'gradereport_stats'); + } + } + + /** + * Abstract method that is called to make the statistic and + * do all the processing for it. + * @param array $final_grades ordered array of final grades. + * @iparam object $item the gradeable item for witch the grades are a part of. + * @returns an array or floating point value of the statistic. + */ + abstract public function report_data($final_grades, $item=null); +} + +?> \ No newline at end of file diff --git a/grade/report/stats/styles.php b/grade/report/stats/styles.php new file mode 100755 index 0000000000..5c79006928 --- /dev/null +++ b/grade/report/stats/styles.php @@ -0,0 +1,254 @@ +.flexible th { + white-space:nowrap; +} + +.gradestable th.user img { + width: 20px; +} + +.gradestable th.user, .gradestable th.range { + white-space: nowrap; +} + +.grade-report-stats table .catlevel1 { + background-color: #ffffff; +} +.grade-report-stats table .catlevel2 { + background-color: #eeeeee; +} +.grade-report-stats table .catlevel3 { + background-color: #dddddd; +} + +.grade-report-stats table td.overridden { + background-color: #EFD9B3; +} + +.grade-report-stats table tr.avg td.cell { + background-color: #efefff; +} + +.grade-report-stats table tr.odd td.cell { + background-color: #efefef; +} + +.grade-report-stats table tr.even td.overridden { + background-color: #F3E4C0; +} + +.grade-report-stats table tr.odd td.overridden { + background-color: #EFD9A4; +} + +.grade-report-stats table tr.even td.excluded { + background-color: #EABFFF; +} + +.grade-report-stats table tr.odd td.excluded { + background-color: #E5AFFF; +} + +.grade-report-stats table th th.heading th.heading.c0 th.header th.header.c0 { + border-width:1px; +} + +.grade-report-stats table tr.odd th.header { + + border-width:1px; +} + +.grade-report-stats table td.vmarked, .grade-report-stats table tr.odd td.vmarked { + background-color: #ffcc33; +} + +.grade-report-stats table td.hmarked, .grade-report-stats table tr.odd td.hmarked { + background-color: #ffff99; +} + +.grade-report-stats table td.hmarked.vmarked, .grade-report-stats table tr.odd td.hmarked.vmarked{ + background-color: #ffcc99; +} + +.grade-report-stats table tr.groupavg td.cell { + background-color: #efffef; +} + +.grade-report-stats table tr.groupavg td.cell { + font-weight: bold; + color: #006400; +} + +.grade-report-stats table tr.avg td.cell { + font-weight: bold; + color: #00008B; +} + +.grade-report-stats table td.cat, +.grade-report-stats table td.course { + font-weight: bold; +} + +.grade-report-stats table { + font-size: 80%; + white-space: nowrap; + border-color: #D3D3D3; +} + +.grade-report-stats table { + border-width:1px; + border-style:solid; + margin-top: 20px; + border-color: #D3D3D3; +} + +.grade-report-stats #overDiv table { + margin: 0; +} + +.grade-report-stats #overDiv table td.feedback { + border: 0px; +} +.grade-report-stats #overDiv .feedback { + background-color: #AABBFF; + color: #000000; + font-family: Verdana; + font-size: 70%; + font-weight: normal; +} + +.grade-report-stats #overDiv .caption { + background-color: #5566CC; + color: #CCCCFF; + font-family: Arial; + font-size: 70%; + font-weight: bold; +} + +.grade-report-stats div.submit { + margin-top: 20px; + text-align: center; +} + +.grade-report-stats table td { + border-width:1px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table tr.heading { + border-width:0px 0px 0px 0px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table .heading td { + border-width:0px 0px 0px 0px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table th.category { + border-width:1px 1px 1px 1px; + border-style:solid; + border-color: #D3D3D3; + vertical-align: top; +} + +.grade-report-stats table th.user { + border-width:0px 0px 1px 0px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table th.useridnumber { + border-width:0px 0px 1px 1px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table th.categoryitem, +.grade-report-stats table th.courseitem, +.grade-report-stats table td.topleft { + border-width:0px 1px 0px 1px; + border-style:solid; + vertical-align: top; + border-color: #D3D3D3; +} + +.grade-report-stats table#participants th { + vertical-align: top; +} + +.grade-report-stats table td.fillerfirst { + border-width:0px 0px 0px 1px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table td.fillerlast { + border-width:0px 1px 0px 0px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table th.item { + border-width:1px 1px 1px 1px; + border-style:solid; + border-color: #D3D3D3; + vertical-align: top; +} + +.grade-report-stats div.gradertoggle { + display: inline; + margin-left: 20px; +} + +.grade-report-stats table { + margin-left:auto; + margin-right:auto; +} + +.grade-report-stats table th.user { + text-align:left; +} + +.grade-report-stats table td.useridnumber { + text-align:left; +} + +.grade-report-stats table td { + text-align:right; +} + +.grade-report-stats table th.range { + border-width:1px 1px 1px 1px; + border-style:solid; + border-color: #D3D3D3; +} + +.grade-report-stats table .userpic { + display: inline; + margin-right: 10px; +} + +.grade-report-stats table .quickfeedback { + border: #000000 1px dashed; +} + +.grade-report-stats #siteconfiglink { + text-align: right; +} + +.grade-report-stats table .hidden, +.grade-report-stats table .hidden a { + color:#aaaaaa; +} + +.grade-report-stats table .datesubmitted { + font-size: 0.7em; +} + +.grade-report-stats table td.cell { + padding-left: 5px; + padding-right: 5px; +} diff --git a/grade/report/stats/tabs.php b/grade/report/stats/tabs.php new file mode 100755 index 0000000000..abc8b665c9 --- /dev/null +++ b/grade/report/stats/tabs.php @@ -0,0 +1,49 @@ +id); + $row[] = new tabobject('statsreport', + $CFG->wwwroot.'/grade/report/stats/index.php?id='.$courseid, + get_string('modulename', 'gradereport_stats')); + + $row[] = new tabobject('preferences', + $CFG->wwwroot.'/grade/report/stats/preferences.php?id='.$courseid, + get_string('myreportpreferences', 'grades')); + + /// A bit of a hack to make the printable tab open a new window. + $row[] = new tabobject('printable', + '#" onClick="javascript:window.open(\'' . $CFG->wwwroot. '/grade/report/stats/print.php?id=' . $courseid . '\')', + get_string('printable', 'gradereport_stats')); + + $tabs[] = $row; + echo '
'; + print_tabs($tabs, $currenttab); + echo '
'; +?> \ No newline at end of file diff --git a/grade/report/stats/version.php b/grade/report/stats/version.php new file mode 100755 index 0000000000..6aa9c25528 --- /dev/null +++ b/grade/report/stats/version.php @@ -0,0 +1,28 @@ +version = 2008061400; +$plugin->requires = 2007101000; + +?>