mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
Merge branch 'MDL-75156-master' of https://github.com/ilyatregubov/moodle
This commit is contained in:
commit
ba4fb93377
@ -852,11 +852,12 @@ class grade_plugin_info {
|
||||
* @param actionbar|null $actionbar The actions bar which will be displayed on the page if $shownavigation is set
|
||||
* to true. If $actionbar is not explicitly defined, the general action bar
|
||||
* (\core_grades\output\general_action_bar) will be used by default.
|
||||
* @param boolean $showtitle If set to false just show course full name as a title.
|
||||
* @return string HTML code or nothing if $return == false
|
||||
*/
|
||||
function print_grade_page_head(int $courseid, string $active_type, ?string $active_plugin = null, $heading = false,
|
||||
bool $return = false, $buttons = false, bool $shownavigation = true, ?string $headerhelpidentifier = null,
|
||||
?string $headerhelpcomponent = null, ?stdClass $user = null, ?action_bar $actionbar = null) {
|
||||
?string $headerhelpcomponent = null, ?stdClass $user = null, ?action_bar $actionbar = null, $showtitle = true) {
|
||||
global $CFG, $OUTPUT, $PAGE;
|
||||
|
||||
// Put a warning on all gradebook pages if the course has modules currently scheduled for background deletion.
|
||||
@ -877,7 +878,9 @@ function print_grade_page_head(int $courseid, string $active_type, ?string $acti
|
||||
$stractive_plugin = ($active_plugin) ? $plugin_info['strings']['active_plugin_str'] : $heading;
|
||||
$stractive_type = $plugin_info['strings'][$active_type];
|
||||
|
||||
if (empty($plugin_info[$active_type]->id) || !empty($plugin_info[$active_type]->parent)) {
|
||||
if (!$showtitle) {
|
||||
$title = $PAGE->course->fullname;
|
||||
} else if (empty($plugin_info[$active_type]->id) || !empty($plugin_info[$active_type]->parent)) {
|
||||
$title = $PAGE->course->fullname.': ' . $stractive_type . ': ' . $stractive_plugin;
|
||||
} else {
|
||||
$title = $PAGE->course->fullname.': ' . $stractive_plugin;
|
||||
@ -916,6 +919,10 @@ function print_grade_page_head(int $courseid, string $active_type, ?string $acti
|
||||
$heading = $stractive_plugin;
|
||||
}
|
||||
|
||||
if (!$showtitle) {
|
||||
$heading = '';
|
||||
}
|
||||
|
||||
if ($shownavigation) {
|
||||
$renderer = $PAGE->get_renderer('core_grades');
|
||||
// If the navigation action bar is not explicitly defined, use the general (default) action bar.
|
||||
|
@ -577,5 +577,183 @@ abstract class grade_report {
|
||||
$result = $this->blank_hidden_total_and_adjust_bounds($courseid, $course_item, $finalgrade);
|
||||
return $result['grade'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate average grade for a given grade item.
|
||||
* Based on calculate_averages function from grade/report/user/lib.php
|
||||
*
|
||||
* @param grade_item $gradeitem Grade item
|
||||
* @param array $info Ungraded grade items counts and report preferences.
|
||||
* @return array Average grade and meancount.
|
||||
*/
|
||||
public static function calculate_average(grade_item $gradeitem, array $info): array {
|
||||
|
||||
$meanselection = $info['report']['meanselection'];
|
||||
$totalcount = $info['report']['totalcount'];
|
||||
$ungradedcounts = $info['ungradedcounts'];
|
||||
$sumarray = $info['sumarray'];
|
||||
|
||||
if (empty($sumarray[$gradeitem->id])) {
|
||||
$sumarray[$gradeitem->id] = 0;
|
||||
}
|
||||
|
||||
if (empty($ungradedcounts[$gradeitem->id])) {
|
||||
$ungradedcounts = 0;
|
||||
} else {
|
||||
$ungradedcounts = $ungradedcounts[$gradeitem->id]->count;
|
||||
}
|
||||
|
||||
// If they want the averages to include all grade items.
|
||||
if ($meanselection == GRADE_REPORT_MEAN_GRADED) {
|
||||
$meancount = $totalcount - $ungradedcounts;
|
||||
} else {
|
||||
// Bump up the sum by the number of ungraded items * grademin.
|
||||
$sumarray[$gradeitem->id] += ($ungradedcounts * $gradeitem->grademin);
|
||||
$meancount = $totalcount;
|
||||
}
|
||||
|
||||
$aggr['meancount'] = $meancount;
|
||||
|
||||
if (empty($sumarray[$gradeitem->id]) || $meancount == 0) {
|
||||
$aggr['average'] = null;
|
||||
} else {
|
||||
$sum = $sumarray[$gradeitem->id];
|
||||
$aggr['average'] = $sum / $meancount;
|
||||
}
|
||||
return $aggr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ungraded grade items info and sum of all grade items in a course.
|
||||
* Based on calculate_averages function from grade/report/user/lib.php
|
||||
*
|
||||
* @return array Ungraded grade items counts with report preferences.
|
||||
*/
|
||||
public function ungraded_counts(): array {
|
||||
global $DB;
|
||||
|
||||
$groupid = null;
|
||||
if (isset($this->gpr->groupid)) {
|
||||
$groupid = $this->gpr->groupid;
|
||||
}
|
||||
|
||||
$info = [];
|
||||
$info['report'] = [
|
||||
'averagesdisplaytype' => $this->get_pref('averagesdisplaytype'),
|
||||
'averagesdecimalpoints' => $this->get_pref('averagesdecimalpoints'),
|
||||
'meanselection' => $this->get_pref('meanselection'),
|
||||
'shownumberofgrades' => $this->get_pref('shownumberofgrades'),
|
||||
'totalcount' => $this->get_numusers(!is_null($groupid)),
|
||||
];
|
||||
|
||||
// We want to query both the current context and parent contexts.
|
||||
list($relatedctxsql, $relatedctxparams) =
|
||||
$DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx');
|
||||
|
||||
// Limit to users with a gradeable role ie students.
|
||||
list($gradebookrolessql, $gradebookrolesparams) =
|
||||
$DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0');
|
||||
|
||||
// Limit to users with an active enrolment.
|
||||
$defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
|
||||
$showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
|
||||
$showonlyactiveenrol = $showonlyactiveenrol ||
|
||||
!has_capability('moodle/course:viewsuspendedusers', $this->context);
|
||||
list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context, '', 0, $showonlyactiveenrol);
|
||||
|
||||
$params = array_merge($this->groupwheresql_params, $gradebookrolesparams, $enrolledparams, $relatedctxparams);
|
||||
$params['courseid'] = $this->courseid;
|
||||
|
||||
// Aggregate on whole course only.
|
||||
if (empty($groupid)) {
|
||||
$this->groupsql = null;
|
||||
$this->groupwheresql = null;
|
||||
}
|
||||
|
||||
// Empty grades must be evaluated as grademin, NOT always 0.
|
||||
// This query returns a count of ungraded grades (NULL finalgrade OR no matching record in grade_grades table).
|
||||
// No join condition when joining grade_items and user to get a grade item row for every user.
|
||||
// Then left join with grade_grades and look for rows with null final grade
|
||||
// (which includes grade items with no grade_grade).
|
||||
$sql = "SELECT gi.id, COUNT(u.id) AS count
|
||||
FROM {grade_items} gi
|
||||
JOIN {user} u ON u.deleted = 0
|
||||
JOIN ($enrolledsql) je ON je.id = u.id
|
||||
JOIN (
|
||||
SELECT DISTINCT ra.userid
|
||||
FROM {role_assignments} ra
|
||||
WHERE ra.roleid $gradebookrolessql
|
||||
AND ra.contextid $relatedctxsql
|
||||
) rainner ON rainner.userid = u.id
|
||||
LEFT JOIN {grade_grades} gg
|
||||
ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)
|
||||
$this->groupsql
|
||||
WHERE gi.courseid = :courseid
|
||||
AND gg.finalgrade IS NULL
|
||||
$this->groupwheresql
|
||||
GROUP BY gi.id";
|
||||
$info['ungradedcounts'] = $DB->get_records_sql($sql, $params);
|
||||
|
||||
// Find sums of all grade items in course.
|
||||
$sql = "SELECT gg.itemid, SUM(gg.finalgrade) AS sum
|
||||
FROM {grade_items} gi
|
||||
JOIN {grade_grades} gg ON gg.itemid = gi.id
|
||||
JOIN {user} u ON u.id = gg.userid
|
||||
JOIN ($enrolledsql) je ON je.id = gg.userid
|
||||
JOIN (
|
||||
SELECT DISTINCT ra.userid
|
||||
FROM {role_assignments} ra
|
||||
WHERE ra.roleid $gradebookrolessql
|
||||
AND ra.contextid $relatedctxsql
|
||||
) rainner ON rainner.userid = u.id
|
||||
$this->groupsql
|
||||
WHERE gi.courseid = :courseid
|
||||
AND u.deleted = 0
|
||||
AND gg.finalgrade IS NOT NULL
|
||||
AND gg.hidden = 0
|
||||
$this->groupwheresql
|
||||
GROUP BY gg.itemid";
|
||||
|
||||
$sumarray = [];
|
||||
$sums = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($sums as $itemid => $csum) {
|
||||
$sumarray[$itemid] = $csum->sum;
|
||||
}
|
||||
$sums->close();
|
||||
$info['sumarray'] = $sumarray;
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grade item type names in a course to use in filter dropdown.
|
||||
*
|
||||
* @return array Item types.
|
||||
*/
|
||||
public function item_types(): array {
|
||||
global $DB, $CFG;
|
||||
|
||||
$modnames = [];
|
||||
$sql = "(SELECT gi.itemmodule
|
||||
FROM {grade_items} gi
|
||||
WHERE gi.courseid = :courseid1
|
||||
AND gi.itemmodule IS NOT NULL)
|
||||
UNION
|
||||
(SELECT gi1.itemtype
|
||||
FROM {grade_items} gi1
|
||||
WHERE gi1.courseid = :courseid2
|
||||
AND gi1.itemtype = 'manual')";
|
||||
|
||||
$itemtypes = $DB->get_records_sql($sql, ['courseid1' => $this->courseid, 'courseid2' => $this->courseid]);
|
||||
foreach ($itemtypes as $itemtype => $value) {
|
||||
if (file_exists("$CFG->dirroot/mod/$itemtype/lib.php")) {
|
||||
$modnames[$itemtype] = get_string("modulename", $itemtype, null, true);
|
||||
} else if ($itemtype == 'manual') {
|
||||
$modnames[$itemtype] = get_string('manualitem', 'grades', null, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $modnames;
|
||||
}
|
||||
}
|
||||
|
||||
|
273
grade/report/summary/classes/local/entities/grade_items.php
Normal file
273
grade/report/summary/classes/local/entities/grade_items.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace gradereport_summary\local\entities;
|
||||
|
||||
use core_reportbuilder\local\filters\select;
|
||||
use grade_item;
|
||||
use grade_plugin_return;
|
||||
use grade_report_summary;
|
||||
use lang_string;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\report\column;
|
||||
use core_reportbuilder\local\report\filter;
|
||||
use core_grades\local\helpers\helpers;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
require_once($CFG->dirroot . '/grade/report/summary/lib.php');
|
||||
require_once($CFG->dirroot . '/grade/lib.php');
|
||||
|
||||
/**
|
||||
* Grade summary entity class implementation
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class grade_items extends base {
|
||||
|
||||
/** @var array Grade report. */
|
||||
public $report;
|
||||
|
||||
/** @var array Ungraded grade items counts with sql info. */
|
||||
public $ungradedcounts;
|
||||
|
||||
/**
|
||||
* Database tables that this entity uses and their default aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default_table_aliases(): array {
|
||||
return ['grade_items' => 'gi'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default title for this entity in the list of columns/conditions/filters in the report builder
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
protected function get_default_entity_title(): lang_string {
|
||||
return new lang_string('gradeitem', 'grades');
|
||||
}
|
||||
|
||||
/**
|
||||
* The default machine-readable name for this entity that will be used in the internal names of the columns/filters
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_default_entity_name(): string {
|
||||
return 'grade_items';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the entity
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public function initialise(): base {
|
||||
global $COURSE;
|
||||
|
||||
$context = \context_course::instance($COURSE->id);
|
||||
|
||||
$gpr = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $COURSE,
|
||||
]
|
||||
);
|
||||
|
||||
$this->report = new grade_report_summary($COURSE->id, $gpr, $context);
|
||||
$this->ungradedcounts = $this->report->ungraded_counts();
|
||||
|
||||
$columns = $this->get_all_columns();
|
||||
foreach ($columns as $column) {
|
||||
$this->add_column($column);
|
||||
}
|
||||
|
||||
$filters = $this->get_all_filters();
|
||||
foreach ($filters as $filter) {
|
||||
$this->add_filter($filter);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all available columns
|
||||
*
|
||||
* @return column[]
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
|
||||
$tablealias = $this->get_table_alias('grade_items');
|
||||
$selectsql = "$tablealias.id, $tablealias.itemname, $tablealias.iteminstance, $tablealias.calculation,
|
||||
$tablealias.itemnumber, $tablealias.itemmodule, $tablealias.hidden, $tablealias.courseid";
|
||||
|
||||
// Grade item name column.
|
||||
$columns[] = (new column(
|
||||
'name',
|
||||
null,
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields($selectsql)
|
||||
->add_callback(static function($value, $row): string {
|
||||
global $PAGE, $CFG;
|
||||
|
||||
$renderer = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
|
||||
if ($row->itemmodule) {
|
||||
$modinfo = get_fast_modinfo($row->courseid);
|
||||
$instances = $modinfo->get_instances();
|
||||
$cm = $instances[$row->itemmodule][$row->iteminstance];
|
||||
|
||||
if (file_exists($CFG->dirroot . '/mod/' . $row->itemmodule . '/grade.php')) {
|
||||
$args = ['id' => $cm->id, 'itemnumber' => $row->itemnumber];
|
||||
$url = new \moodle_url('/mod/' . $row->itemmodule . '/grade.php', $args);
|
||||
} else {
|
||||
$url = new \moodle_url('/mod/' . $row->itemmodule . '/view.php', ['id' => $cm->id]);
|
||||
}
|
||||
|
||||
$imagedata = $renderer->pix_icon('monologo', '', $row->itemmodule, ['class' => 'activityicon']);
|
||||
$purposeclass = plugin_supports('mod', $row->itemmodule, FEATURE_MOD_PURPOSE);
|
||||
$purposeclass .= ' activityiconcontainer';
|
||||
$purposeclass .= ' modicon_' . $row->itemmodule;
|
||||
$imagedata = \html_writer::tag('div', $imagedata, ['class' => $purposeclass]);
|
||||
|
||||
$dimmed = '';
|
||||
if ($row->hidden) {
|
||||
$dimmed = ' dimmed_text';
|
||||
}
|
||||
$html = \html_writer::start_div('page-context-header' . $dimmed);
|
||||
// Image data.
|
||||
$html .= \html_writer::div($imagedata, 'page-header-image mr-2');
|
||||
$prefix = \html_writer::div($row->itemmodule, 'text-muted text-uppercase small line-height-3');
|
||||
$name = $prefix . \html_writer::link($url, format_string($cm->name, true));
|
||||
$html .= \html_writer::tag('div', $name, ['class' => 'page-header-headings']);
|
||||
} else {
|
||||
// Manual grade item.
|
||||
$gradeitem = grade_item::fetch(['id' => $row->id, 'courseid' => $row->courseid]);
|
||||
if ($row->calculation) {
|
||||
$imagedata = $renderer->pix_icon('i/agg_sum', '');
|
||||
} else {
|
||||
$imagedata = $renderer->pix_icon('i/manual_item', '');
|
||||
}
|
||||
$imagedata = \html_writer::tag('div', $imagedata);
|
||||
|
||||
$html = \html_writer::start_div('page-context-header');
|
||||
// Image data.
|
||||
$html .= \html_writer::div($imagedata, 'page-header-image mr-2');
|
||||
$html .= \html_writer::tag('div', $gradeitem->get_name(), ['class' => 'page-header-headings']);
|
||||
}
|
||||
return $html;
|
||||
|
||||
});
|
||||
|
||||
$report = [
|
||||
'report' => $this->report,
|
||||
'ungradedcounts' => $this->ungradedcounts
|
||||
];
|
||||
|
||||
// Average column.
|
||||
$columns[] = (new column(
|
||||
'average',
|
||||
new lang_string('average', 'grades'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("$tablealias.id")
|
||||
->add_callback(static function($value) use ($report): string {
|
||||
|
||||
$gradeitem = grade_item::fetch(['id' => $value]);
|
||||
if (!empty($gradeitem->avg)) {
|
||||
$averageformatted = '-';
|
||||
}
|
||||
|
||||
if ($gradeitem->needsupdate) {
|
||||
$averageformatted = get_string('error');
|
||||
}
|
||||
|
||||
if (empty($averageformatted)) {
|
||||
$ungradedcounts = $report['ungradedcounts'];
|
||||
$aggr = $report['report']->calculate_average($gradeitem, $ungradedcounts);
|
||||
|
||||
if (empty($aggr['average'])) {
|
||||
$averageformatted = '-';
|
||||
} else {
|
||||
$averagesdisplaytype = $ungradedcounts['report']['averagesdisplaytype'];
|
||||
$averagesdecimalpoints = $ungradedcounts['report']['averagesdecimalpoints'];
|
||||
$shownumberofgrades = $ungradedcounts['report']['shownumberofgrades'];
|
||||
|
||||
// Determine which display type to use for this average.
|
||||
// No ==0 here, please resave the report and user preferences.
|
||||
if ($averagesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) {
|
||||
$displaytype = $gradeitem->get_displaytype();
|
||||
} else {
|
||||
$displaytype = $averagesdisplaytype;
|
||||
}
|
||||
|
||||
// Override grade_item setting if a display preference (not inherit) was set for the averages.
|
||||
if ($averagesdecimalpoints == GRADE_REPORT_PREFERENCE_INHERIT) {
|
||||
$decimalpoints = $gradeitem->get_decimals();
|
||||
} else {
|
||||
$decimalpoints = $averagesdecimalpoints;
|
||||
}
|
||||
|
||||
$gradehtml = grade_format_gradevalue($aggr['average'],
|
||||
$gradeitem, true, $displaytype, $decimalpoints);
|
||||
|
||||
if ($shownumberofgrades) {
|
||||
$numberofgrades = $aggr['meancount'];
|
||||
$gradehtml .= " (" . $numberofgrades . ")";
|
||||
}
|
||||
$averageformatted = $gradehtml;
|
||||
|
||||
}
|
||||
}
|
||||
return $averageformatted;
|
||||
});
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all available filters
|
||||
*
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
$filters = [];
|
||||
|
||||
$itemtypes = $this->report->item_types();
|
||||
$tablealias = $this->get_table_alias('grade_items');
|
||||
|
||||
// Activity type filter.
|
||||
$filters[] = (new filter(
|
||||
select::class,
|
||||
'name',
|
||||
new lang_string('activitytype', 'format_singleactivity'),
|
||||
$this->get_entity_name(),
|
||||
"coalesce({$tablealias}.itemmodule,{$tablealias}.itemtype)"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options($itemtypes);
|
||||
|
||||
return $filters;
|
||||
}
|
||||
}
|
115
grade/report/summary/classes/local/systemreports/summary.php
Normal file
115
grade/report/summary/classes/local/systemreports/summary.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace gradereport_summary\local\systemreports;
|
||||
|
||||
use gradereport_summary\local\entities\grade_items;
|
||||
use core_reportbuilder\local\helpers\database;
|
||||
use core_reportbuilder\system_report;
|
||||
|
||||
/**
|
||||
* Grade summary system report class implementation
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class summary extends system_report {
|
||||
|
||||
/**
|
||||
* Initialise report, we need to set the main table, load our entities and set columns/filters
|
||||
*/
|
||||
protected function initialise(): void {
|
||||
global $COURSE;
|
||||
|
||||
// Our main entity, it contains all of the column definitions that we need.
|
||||
$entitymain = new grade_items();
|
||||
$context = $this->get_context();
|
||||
$COURSE = get_course($context->instanceid);
|
||||
|
||||
$entitymainalias = $entitymain->get_table_alias('grade_items');
|
||||
|
||||
$this->set_main_table('grade_items', $entitymainalias);
|
||||
$this->add_entity($entitymain);
|
||||
|
||||
// Any columns required by actions should be defined here to ensure they're always available.
|
||||
$this->add_base_fields("{$entitymainalias}.id");
|
||||
|
||||
$courseid = $this->get_context()->instanceid;
|
||||
|
||||
$param1 = database::generate_param_name();
|
||||
$param2 = database::generate_param_name();
|
||||
$param3 = database::generate_param_name();
|
||||
|
||||
// Exclude grade categories.
|
||||
// For now exclude course total as well.
|
||||
$wheresql = "$entitymainalias.courseid = :$param1";
|
||||
$wheresql .= " AND $entitymainalias.itemtype <> 'course'";
|
||||
|
||||
// Not showing category items.
|
||||
$wheresql .= " AND $entitymainalias.itemtype <> 'category'";
|
||||
|
||||
// Only value and scale grade types may be aggregated.
|
||||
$wheresql .= " AND ($entitymainalias.gradetype = :$param2 OR $entitymainalias.gradetype = :$param3)";
|
||||
|
||||
$this->add_base_condition_sql($wheresql,
|
||||
[$param1 => $courseid, $param2 => GRADE_TYPE_VALUE, $param3 => GRADE_TYPE_SCALE]);
|
||||
|
||||
// Now we can call our helper methods to add the content we want to include in the report.
|
||||
$this->add_columns();
|
||||
$this->add_filters();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates access to view this report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_view(): bool {
|
||||
return has_capability('gradereport/summary:view', $this->get_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the columns we want to display in the report
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
*/
|
||||
public function add_columns(): void {
|
||||
$columns = [
|
||||
'grade_items:name',
|
||||
'grade_items:average',
|
||||
];
|
||||
|
||||
$this->add_columns_from_entities($columns);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters we want to display in the report
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
*/
|
||||
protected function add_filters(): void {
|
||||
$filters = [
|
||||
'grade_items:name',
|
||||
];
|
||||
|
||||
$this->add_filters_from_entities($filters);
|
||||
}
|
||||
}
|
44
grade/report/summary/classes/privacy/provider.php
Normal file
44
grade/report/summary/classes/privacy/provider.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for gradereport_summary.
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace gradereport_summary\privacy;
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for gradereport_summary implementing null_provider.
|
||||
*
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() : string {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
38
grade/report/summary/db/access.php
Normal file
38
grade/report/summary/db/access.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The gradebook summary view - Database file
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$capabilities = [
|
||||
'gradereport/summary:view' => [
|
||||
'riskbitmask' => RISK_PERSONAL,
|
||||
'captype' => 'read',
|
||||
'contextlevel' => CONTEXT_COURSE,
|
||||
'archetypes' => [
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
'teacher' => CAP_ALLOW,
|
||||
'manager' => CAP_ALLOW,
|
||||
]
|
||||
]
|
||||
];
|
55
grade/report/summary/index.php
Normal file
55
grade/report/summary/index.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Grade summary.
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
require_once($CFG->dirroot.'/grade/lib.php');
|
||||
|
||||
use core_reportbuilder\system_report_factory;
|
||||
use gradereport_summary\local\systemreports\summary;
|
||||
|
||||
$courseid = required_param('id', PARAM_INT);
|
||||
|
||||
if (!$course = $DB->get_record('course', ['id' => $courseid])) {
|
||||
throw new \moodle_exception('invalidcourseid');
|
||||
}
|
||||
require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
$PAGE->set_url('/grade/report/summary/index.php', ['id' => $courseid]);
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_pagelayout('report');
|
||||
$PAGE->add_body_class('limitedwidth');
|
||||
|
||||
require_capability('gradereport/summary:view', $context);
|
||||
require_capability('moodle/grade:viewall', $context);
|
||||
|
||||
print_grade_page_head($courseid, 'report', 'summary', false,
|
||||
false, false, true, null, null,
|
||||
null, null, false);
|
||||
|
||||
$report = system_report_factory::create(summary::class, context_course::instance($courseid));
|
||||
|
||||
echo $report->output();
|
||||
echo $OUTPUT->footer();
|
28
grade/report/summary/lang/en/gradereport_summary.php
Normal file
28
grade/report/summary/lang/en/gradereport_summary.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Strings for Summary view
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// General Strings.
|
||||
$string['pluginname'] = 'Grade summary';
|
||||
$string['summary:view'] = 'View grade summary report';
|
||||
$string['privacy:metadata'] = 'The Grade summary report only shows data stored in other locations.';
|
77
grade/report/summary/lib.php
Normal file
77
grade/report/summary/lib.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
/**
|
||||
* Definition of the summary report class
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once($CFG->dirroot . '/grade/report/lib.php');
|
||||
|
||||
/**
|
||||
* Class providing an API for the summary report building.
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @uses grade_report
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class grade_report_summary extends grade_report {
|
||||
|
||||
/**
|
||||
* Capability check caching
|
||||
*
|
||||
* @var boolean $canviewhidden
|
||||
*/
|
||||
public $canviewhidden;
|
||||
|
||||
/**
|
||||
* Constructor. Sets local copies of user preferences and initialises grade_tree.
|
||||
*
|
||||
* @param int $courseid
|
||||
* @param object $gpr grade plugin return tracking object
|
||||
* @param string $context
|
||||
*/
|
||||
public function __construct($courseid, $gpr, $context) {
|
||||
parent::__construct($courseid, $gpr, $context);
|
||||
|
||||
$this->canviewhidden = has_capability('moodle/grade:viewhidden', context_course::instance($this->course->id));
|
||||
$this->setup_groups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single action against a category, grade_item or grade. Not used in summary report.
|
||||
*
|
||||
* @param string $target eid ({type}{id}, e.g. c4 for category4)
|
||||
* @param string $action Which action to take (edit, delete etc...)
|
||||
*/
|
||||
public function process_action($target, $action) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles form data sent by this report for this report. Not used in summary report.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function process_data($data) {
|
||||
}
|
||||
|
||||
}
|
29
grade/report/summary/version.php
Normal file
29
grade/report/summary/version.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Standard version file
|
||||
*
|
||||
* @package gradereport_summary
|
||||
* @copyright 2022 Ilya Tregubov <ilya@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'gradereport_summary'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->version = 2022041903;
|
||||
$plugin->requires = 2022041200;
|
@ -24,6 +24,12 @@
|
||||
*/
|
||||
namespace core_grades;
|
||||
|
||||
use assign;
|
||||
use cm_info;
|
||||
use grade_item;
|
||||
use grade_plugin_return;
|
||||
use grade_report_summary;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
@ -170,4 +176,539 @@ class lib_test extends \advanced_testcase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that ungraded_counts calculates count and sum of grades correctly when there are graded users.
|
||||
*
|
||||
* @covers \grade_report::ungraded_counts
|
||||
*/
|
||||
public function test_ungraded_counts_count_sumgrades() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
||||
$teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher'], '*', MUST_EXIST);
|
||||
|
||||
// Custom roles (gradable and non gradable).
|
||||
$gradeblerole = create_role('New student role', 'gradable',
|
||||
'Gradable role', 'student');
|
||||
$nongradeblerole = create_role('New student role', 'nongradable',
|
||||
'Non gradable role', 'student');
|
||||
|
||||
// Set up gradable roles.
|
||||
set_config('gradebookroles', $studentrole->id . ',' . $gradeblerole);
|
||||
|
||||
// Create users.
|
||||
|
||||
// These will be gradable users.
|
||||
$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
|
||||
$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
|
||||
$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
|
||||
$student5 = $this->getDataGenerator()->create_user(['username' => 'student5']);
|
||||
|
||||
// These will be non-gradable users.
|
||||
$student4 = $this->getDataGenerator()->create_user(['username' => 'student4']);
|
||||
$student6 = $this->getDataGenerator()->create_user(['username' => 'student6']);
|
||||
$teacher = $this->getDataGenerator()->create_user(['username' => 'teacher']);
|
||||
|
||||
// Enrol students.
|
||||
$this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student3->id, $course1->id, $gradeblerole);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($student5->id, $course1->id, $nongradeblerole);
|
||||
$this->getDataGenerator()->enrol_user($student6->id, $course1->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id);
|
||||
|
||||
// User that is enrolled in a different course.
|
||||
$this->getDataGenerator()->enrol_user($student4->id, $course2->id, $studentrole->id);
|
||||
|
||||
// Mark user as deleted.
|
||||
$student6->deleted = 1;
|
||||
$DB->update_record('user', $student6);
|
||||
|
||||
// Create grade items in course 1.
|
||||
$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);
|
||||
$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);
|
||||
$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]);
|
||||
|
||||
$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([
|
||||
'itemname' => 'Grade item1',
|
||||
'idnumber' => 'git1',
|
||||
'courseid' => $course1->id,
|
||||
]));
|
||||
|
||||
// Create grade items in course 2.
|
||||
$assign3 = $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);
|
||||
|
||||
// Grade users in first course.
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course1);
|
||||
$grade = $assigninstance->get_user_grade($student1->id, true);
|
||||
$grade->grade = 40;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course1);
|
||||
$grade = $assigninstance->get_user_grade($student3->id, true);
|
||||
$grade->grade = 50;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
// Override grade for assignment in gradebook.
|
||||
$gi = \grade_item::fetch([
|
||||
'itemtype' => 'mod',
|
||||
'itemmodule' => 'assign',
|
||||
'iteminstance' => $cm->instance,
|
||||
'courseid' => $course1->id
|
||||
]);
|
||||
$gi->update_final_grade($student3->id, 55);
|
||||
|
||||
// Grade user in second course.
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign3->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course2);
|
||||
$grade = $assigninstance->get_user_grade($student4->id, true);
|
||||
$grade->grade = 40;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$manuaitem->update_final_grade($student1->id, 1);
|
||||
$manuaitem->update_final_grade($student3->id, 2);
|
||||
|
||||
// Trigger a regrade.
|
||||
grade_force_full_regrading($course1->id);
|
||||
grade_force_full_regrading($course2->id);
|
||||
grade_regrade_final_grades($course1->id);
|
||||
grade_regrade_final_grades($course2->id);
|
||||
|
||||
// Initialise reports.
|
||||
$context1 = \context_course::instance($course1->id);
|
||||
$context2 = \context_course::instance($course2->id);
|
||||
|
||||
$gpr1 = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course1,
|
||||
]
|
||||
);
|
||||
|
||||
$gpr2 = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course2,
|
||||
]
|
||||
);
|
||||
|
||||
$report1 = new grade_report_summary($course1->id, $gpr1, $context1);
|
||||
$report2 = new grade_report_summary($course2->id, $gpr2, $context2);
|
||||
|
||||
$ungradedcounts = [];
|
||||
$ungradedcounts[$course1->id] = $report1->ungraded_counts();
|
||||
$ungradedcounts[$course2->id] = $report2->ungraded_counts();
|
||||
|
||||
foreach ($ungradedcounts as $key => $ungradedcount) {
|
||||
$gradeitems = grade_item::fetch_all(['courseid' => $key]);
|
||||
if ($key == $course1->id) {
|
||||
$gradeitemkeys = array_keys($gradeitems);
|
||||
$ungradedcountskeys = array_keys($ungradedcount['ungradedcounts']);
|
||||
|
||||
// For each grade item there is some student that is not graded yet in course 1.
|
||||
$this->assertEmpty(array_diff_key($gradeitemkeys, $ungradedcountskeys));
|
||||
|
||||
// Only quiz does not have any grades, the remaning 4 grade items should have some.
|
||||
// We can do more and match by gradeitem id numbers. But feels like overengeneering.
|
||||
$this->assertEquals(4, count($ungradedcount['sumarray']));
|
||||
} else {
|
||||
|
||||
// In course 2 there is one student, and he is graded.
|
||||
$this->assertEmpty($ungradedcount['ungradedcounts']);
|
||||
|
||||
// There are 2 grade items and they both have some grades.
|
||||
$this->assertEquals(2, count($ungradedcount['sumarray']));
|
||||
}
|
||||
|
||||
foreach ($gradeitems as $gradeitem) {
|
||||
$sumgrades = null;
|
||||
if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) {
|
||||
$ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id];
|
||||
if ($gradeitem->itemtype === 'course') {
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
} else if ($gradeitem->itemmodule === 'assign') {
|
||||
$this->assertEquals(2, $ungradeditem->count);
|
||||
} else if ($gradeitem->itemmodule === 'quiz') {
|
||||
$this->assertEquals(3, $ungradeditem->count);
|
||||
} else if ($gradeitem->itemtype === 'manual') {
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) {
|
||||
$sumgrades = $ungradedcount['sumarray'][$gradeitem->id];
|
||||
if ($gradeitem->itemtype === 'course') {
|
||||
if ($key == $course1->id) {
|
||||
$this->assertEquals('98.00000', $sumgrades); // 40 + 55 + 1 + 2
|
||||
} else {
|
||||
$this->assertEquals('40.00000', $sumgrades);
|
||||
}
|
||||
} else if ($gradeitem->itemmodule === 'assign') {
|
||||
if (($gradeitem->itemname === $assign1->name) || ($gradeitem->itemname === $assign3->name)) {
|
||||
$this->assertEquals('40.00000', $sumgrades);
|
||||
} else {
|
||||
$this->assertEquals('55.00000', $sumgrades);
|
||||
}
|
||||
} else if ($gradeitem->itemtype === 'manual') {
|
||||
$this->assertEquals('3.00000', $sumgrades);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that ungraded_counts calculates count and sum of grades correctly for groups when there are graded users.
|
||||
*
|
||||
* @covers \grade_report::ungraded_counts
|
||||
*/
|
||||
public function test_ungraded_count_sumgrades_groups() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
||||
|
||||
// Create users.
|
||||
|
||||
$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
|
||||
$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
|
||||
$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
|
||||
|
||||
// Enrol students.
|
||||
$this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
|
||||
|
||||
$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
|
||||
$group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
|
||||
$this->getDataGenerator()->create_group_member(['userid' => $student1->id, 'groupid' => $group1->id]);
|
||||
$this->getDataGenerator()->create_group_member(['userid' => $student2->id, 'groupid' => $group2->id]);
|
||||
$this->getDataGenerator()->create_group_member(['userid' => $student3->id, 'groupid' => $group2->id]);
|
||||
$DB->set_field('course', 'groupmode', SEPARATEGROUPS, ['id' => $course->id]);
|
||||
|
||||
// Create grade items in course 1.
|
||||
$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
|
||||
|
||||
$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([
|
||||
'itemname' => 'Grade item1',
|
||||
'idnumber' => 'git1',
|
||||
'courseid' => $course->id,
|
||||
]));
|
||||
|
||||
// Grade users in first course.
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course);
|
||||
$grade = $assigninstance->get_user_grade($student1->id, true);
|
||||
$grade->grade = 40;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course);
|
||||
$grade = $assigninstance->get_user_grade($student3->id, true);
|
||||
$grade->grade = 50;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$manuaitem->update_final_grade($student1->id, 1);
|
||||
$manuaitem->update_final_grade($student3->id, 2);
|
||||
|
||||
// Trigger a regrade.
|
||||
grade_force_full_regrading($course->id);
|
||||
grade_regrade_final_grades($course->id);
|
||||
|
||||
// Initialise report.
|
||||
$context = \context_course::instance($course->id);
|
||||
|
||||
$gpr1 = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course,
|
||||
'groupid' => $group1->id,
|
||||
]
|
||||
);
|
||||
|
||||
$gpr2 = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course,
|
||||
'groupid' => $group2->id,
|
||||
]
|
||||
);
|
||||
|
||||
$report1 = new grade_report_summary($course->id, $gpr1, $context);
|
||||
$report2 = new grade_report_summary($course->id, $gpr2, $context);
|
||||
|
||||
$ungradedcounts = [];
|
||||
$ungradedcounts[$group1->id] = $report1->ungraded_counts();
|
||||
$ungradedcounts[$group2->id] = $report2->ungraded_counts();
|
||||
|
||||
$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);
|
||||
|
||||
// In group1 there is 1 student and assign1 and quiz1 are not graded for him.
|
||||
$this->assertEquals(2, count($ungradedcounts[$group1->id]['ungradedcounts']));
|
||||
|
||||
// In group1 manual grade item, assign1 and course total have some grades.
|
||||
$this->assertEquals(3, count($ungradedcounts[$group1->id]['sumarray']));
|
||||
|
||||
// In group2 student2 has no grades at all so all 5 grade items should present.
|
||||
$this->assertEquals(5, count($ungradedcounts[$group2->id]['ungradedcounts']));
|
||||
|
||||
// In group2 manual grade item, assign2 and course total have some grades.
|
||||
$this->assertEquals(3, count($ungradedcounts[$group2->id]['sumarray']));
|
||||
|
||||
foreach ($gradeitems as $gradeitem) {
|
||||
$sumgrades = null;
|
||||
|
||||
foreach ($ungradedcounts as $key => $ungradedcount) {
|
||||
if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) {
|
||||
$ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id];
|
||||
if ($key == $group1->id) {
|
||||
// Both assign2 and quiz1 are not graded for student1.
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
} else {
|
||||
if ($gradeitem->itemtype === 'course') {
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
} else if ($gradeitem->itemmodule === 'assign') {
|
||||
if ($gradeitem->itemname === $assign1->name) {
|
||||
// In group2 assign1 is not graded for anyone.
|
||||
$this->assertEquals(2, $ungradeditem->count);
|
||||
} else {
|
||||
// In group2 assign2 is graded for student3.
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
}
|
||||
} else if ($gradeitem->itemmodule === 'quiz') {
|
||||
$this->assertEquals(2, $ungradeditem->count);
|
||||
} else if ($gradeitem->itemtype === 'manual') {
|
||||
$this->assertEquals(1, $ungradeditem->count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) {
|
||||
$sumgrades = $ungradedcount['sumarray'][$gradeitem->id];
|
||||
if ($key == $group1->id) {
|
||||
if ($gradeitem->itemtype === 'course') {
|
||||
$this->assertEquals('41.00000', $sumgrades);
|
||||
} else if ($gradeitem->itemmodule === 'assign') {
|
||||
$this->assertEquals('40.00000', $sumgrades);
|
||||
} else if ($gradeitem->itemtype === 'manual') {
|
||||
$this->assertEquals('1.00000', $sumgrades);
|
||||
}
|
||||
} else {
|
||||
if ($gradeitem->itemtype === 'course') {
|
||||
$this->assertEquals('52.00000', $sumgrades);
|
||||
} else if ($gradeitem->itemmodule === 'assign') {
|
||||
$this->assertEquals('50.00000', $sumgrades);
|
||||
} else if ($gradeitem->itemtype === 'manual') {
|
||||
$this->assertEquals('2.00000', $sumgrades);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for calculate_average.
|
||||
* @dataProvider calculate_average_data()
|
||||
* @param int $meanselection Whether to inlcude all grades or non-empty grades in aggregation.
|
||||
* @param array $expectedmeancount expected meancount value
|
||||
* @param array $expectedaverage expceted average value
|
||||
*
|
||||
* @covers \grade_report::calculate_average
|
||||
*/
|
||||
public function test_calculate_average(int $meanselection, array $expectedmeancount, array $expectedaverage) {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);
|
||||
$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);
|
||||
$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
||||
|
||||
// Enrol students.
|
||||
$this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
|
||||
|
||||
// Create activities.
|
||||
$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
|
||||
$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
|
||||
|
||||
// Grade users.
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course);
|
||||
$grade = $assigninstance->get_user_grade($student1->id, true);
|
||||
$grade->grade = 40;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$grade = $assigninstance->get_user_grade($student2->id, true);
|
||||
$grade->grade = 30;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));
|
||||
$assigninstance = new assign($cm->context, $cm, $course);
|
||||
$grade = $assigninstance->get_user_grade($student3->id, true);
|
||||
$grade->grade = 50;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
$grade = $assigninstance->get_user_grade($student1->id, true);
|
||||
$grade->grade = 100;
|
||||
$assigninstance->update_grade($grade);
|
||||
|
||||
// Make a manual grade items.
|
||||
$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item([
|
||||
'itemname' => 'Grade item1',
|
||||
'idnumber' => 'git1',
|
||||
'courseid' => $course->id,
|
||||
]));
|
||||
$manuaitem->update_final_grade($student1->id, 1);
|
||||
$manuaitem->update_final_grade($student3->id, 2);
|
||||
|
||||
// Initialise report.
|
||||
$context = \context_course::instance($course->id);
|
||||
|
||||
$gpr = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course,
|
||||
]
|
||||
);
|
||||
|
||||
$report = new grade_report_summary($course->id, $gpr, $context);
|
||||
|
||||
$ungradedcounts = $report->ungraded_counts();
|
||||
$ungradedcounts['report']['meanselection'] = $meanselection;
|
||||
|
||||
$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);
|
||||
|
||||
foreach ($gradeitems as $gradeitem) {
|
||||
$name = $gradeitem->itemname . ' ' . $gradeitem->itemtype;
|
||||
$aggr = $report->calculate_average($gradeitem, $ungradedcounts);
|
||||
|
||||
$this->assertEquals($expectedmeancount[$name], $aggr['meancount']);
|
||||
$this->assertEquals($expectedaverage[$name], $aggr['average']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_calculate_average
|
||||
*
|
||||
* @return array of testing scenarios
|
||||
*/
|
||||
public function calculate_average_data() : array {
|
||||
return [
|
||||
'Non-empty grades' => [
|
||||
'meanselection' => 1,
|
||||
'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 2, 'Assignment 2 mod' => 2,
|
||||
'Quiz 1 mod' => 0, 'Grade item1 manual' => 2],
|
||||
'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 35.0,
|
||||
'Assignment 2 mod' => 75.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.5],
|
||||
],
|
||||
'All grades' => [
|
||||
'meanselection' => 0,
|
||||
'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 3, 'Assignment 2 mod' => 3,
|
||||
'Quiz 1 mod' => 3, 'Grade item1 manual' => 3],
|
||||
'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 23.333333333333332,
|
||||
'Assignment 2 mod' => 50.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for item types.
|
||||
*
|
||||
* @covers \grade_report::item_types
|
||||
*/
|
||||
public function test_item_types() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Create activities.
|
||||
$this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);
|
||||
$this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);
|
||||
$this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]);
|
||||
|
||||
$this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);
|
||||
|
||||
// Create manual grade items.
|
||||
new \grade_item($this->getDataGenerator()->create_grade_item([
|
||||
'itemname' => 'Grade item1',
|
||||
'idnumber' => 'git1',
|
||||
'courseid' => $course1->id,
|
||||
]));
|
||||
|
||||
new \grade_item($this->getDataGenerator()->create_grade_item([
|
||||
'itemname' => 'Grade item2',
|
||||
'idnumber' => 'git2',
|
||||
'courseid' => $course2->id,
|
||||
]));
|
||||
|
||||
// Create a grade category (it should not be fetched by item_types).
|
||||
new \grade_category($this->getDataGenerator()
|
||||
->create_grade_category(['courseid' => $course1->id]), false);
|
||||
|
||||
// Initialise reports.
|
||||
$context = \context_course::instance($course1->id);
|
||||
|
||||
$gpr = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course1,
|
||||
]
|
||||
);
|
||||
|
||||
$report1 = new grade_report_summary($course1->id, $gpr, $context);
|
||||
|
||||
$context = \context_course::instance($course2->id);
|
||||
|
||||
$gpr = new grade_plugin_return(
|
||||
[
|
||||
'type' => 'report',
|
||||
'plugin' => 'summary',
|
||||
'course' => $course2,
|
||||
]
|
||||
);
|
||||
|
||||
$report2 = new grade_report_summary($course2->id, $gpr, $context);
|
||||
|
||||
$gradeitems1 = $report1->item_types();
|
||||
$gradeitems2 = $report2->item_types();
|
||||
|
||||
$this->assertEquals(3, count($gradeitems1));
|
||||
$this->assertEquals(2, count($gradeitems2));
|
||||
|
||||
$this->assertArrayHasKey('assign', $gradeitems1);
|
||||
$this->assertArrayHasKey('quiz', $gradeitems1);
|
||||
$this->assertArrayHasKey('manual', $gradeitems1);
|
||||
|
||||
$this->assertArrayHasKey('assign', $gradeitems2);
|
||||
$this->assertArrayHasKey('manual', $gradeitems2);
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ class general_action_bar_test extends advanced_testcase {
|
||||
'View' => [
|
||||
'Grader report',
|
||||
'Grade history',
|
||||
'Grade summary',
|
||||
'Overview report',
|
||||
'Single view',
|
||||
'User report',
|
||||
@ -125,6 +126,7 @@ class general_action_bar_test extends advanced_testcase {
|
||||
'View' => [
|
||||
'Grader report',
|
||||
'Grade history',
|
||||
'Grade summary',
|
||||
'Outcomes report',
|
||||
'Overview report',
|
||||
'Single view',
|
||||
@ -151,6 +153,7 @@ class general_action_bar_test extends advanced_testcase {
|
||||
'View' => [
|
||||
'Grader report',
|
||||
'Grade history',
|
||||
'Grade summary',
|
||||
'Overview report',
|
||||
'Single view',
|
||||
'User report',
|
||||
@ -175,6 +178,7 @@ class general_action_bar_test extends advanced_testcase {
|
||||
'View' => [
|
||||
'Grader report',
|
||||
'Grade history',
|
||||
'Grade summary',
|
||||
'Outcomes report',
|
||||
'Overview report',
|
||||
'Single view',
|
||||
@ -201,6 +205,7 @@ class general_action_bar_test extends advanced_testcase {
|
||||
'View' => [
|
||||
'Grader report',
|
||||
'Grade history',
|
||||
'Grade summary',
|
||||
'Outcomes report',
|
||||
'Overview report',
|
||||
'User report',
|
||||
|
@ -1893,7 +1893,7 @@ class core_plugin_manager {
|
||||
),
|
||||
|
||||
'gradereport' => array(
|
||||
'grader', 'history', 'outcomes', 'overview', 'user', 'singleview'
|
||||
'grader', 'history', 'outcomes', 'overview', 'user', 'singleview', 'summary'
|
||||
),
|
||||
|
||||
'gradingform' => array(
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2022100700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2022100700.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.1dev (Build: 20221007)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user