From 1d1d29985f7c87ffd974724000f201ed60d088bc Mon Sep 17 00:00:00 2001 From: nicolasconnault Date: Fri, 13 Feb 2009 08:04:10 +0000 Subject: [PATCH] MDL-18163 Implemented stats report from LSU gradebook --- grade/report/grader/quickedit_item.php | 115 ++++ grade/report/stats.tar.bz2 | Bin 0 -> 14164 bytes grade/report/stats/README.txt | 104 ++++ grade/report/stats/arrayview.php | 47 ++ grade/report/stats/db/access.php | 115 ++++ grade/report/stats/index.php | 94 +++ .../stats/lang/en_utf8/gradereport_stats.php | 79 +++ grade/report/stats/lib.php | 573 ++++++++++++++++++ grade/report/stats/preferences.php | 95 +++ grade/report/stats/preferences_form.php | 181 ++++++ grade/report/stats/print.php | 80 +++ grade/report/stats/settings.php | 101 +++ .../report/stats/statistics/stat_highest.php | 41 ++ grade/report/stats/statistics/stat_lowest.php | 41 ++ grade/report/stats/statistics/stat_mean.php | 41 ++ grade/report/stats/statistics/stat_median.php | 42 ++ grade/report/stats/statistics/stat_mode.php | 64 ++ .../stats/statistics/stat_pass_percent.php | 52 ++ .../statistics/stat_standard_deviation.php | 49 ++ grade/report/stats/statistics/stats.php | 82 +++ grade/report/stats/styles.php | 254 ++++++++ grade/report/stats/tabs.php | 49 ++ grade/report/stats/version.php | 28 + 23 files changed, 2327 insertions(+) create mode 100644 grade/report/grader/quickedit_item.php create mode 100644 grade/report/stats.tar.bz2 create mode 100644 grade/report/stats/README.txt create mode 100755 grade/report/stats/arrayview.php create mode 100755 grade/report/stats/db/access.php create mode 100755 grade/report/stats/index.php create mode 100755 grade/report/stats/lang/en_utf8/gradereport_stats.php create mode 100755 grade/report/stats/lib.php create mode 100755 grade/report/stats/preferences.php create mode 100755 grade/report/stats/preferences_form.php create mode 100644 grade/report/stats/print.php create mode 100644 grade/report/stats/settings.php create mode 100755 grade/report/stats/statistics/stat_highest.php create mode 100755 grade/report/stats/statistics/stat_lowest.php create mode 100755 grade/report/stats/statistics/stat_mean.php create mode 100755 grade/report/stats/statistics/stat_median.php create mode 100755 grade/report/stats/statistics/stat_mode.php create mode 100755 grade/report/stats/statistics/stat_pass_percent.php create mode 100755 grade/report/stats/statistics/stat_standard_deviation.php create mode 100755 grade/report/stats/statistics/stats.php create mode 100755 grade/report/stats/styles.php create mode 100755 grade/report/stats/tabs.php create mode 100755 grade/report/stats/version.php 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 0000000000000000000000000000000000000000..85028e6bf4bce1145f0269d8875c1f8ebb95eddb GIT binary patch literal 14164 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'; +} +?> 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; + +?> -- 2.39.5