Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace gradereport_summary\local\entities;
  18  
  19  use core_reportbuilder\local\filters\select;
  20  use grade_item;
  21  use grade_plugin_return;
  22  use grade_report_summary;
  23  use lang_string;
  24  use stdClass;
  25  use core_reportbuilder\local\entities\base;
  26  use core_reportbuilder\local\report\column;
  27  use core_reportbuilder\local\report\filter;
  28  
  29  defined('MOODLE_INTERNAL') || die;
  30  
  31  require_once($CFG->dirroot . '/grade/report/summary/lib.php');
  32  require_once($CFG->dirroot . '/grade/lib.php');
  33  
  34  /**
  35   * Grade summary entity class implementation
  36   *
  37   * @package    gradereport_summary
  38   * @copyright  2022 Ilya Tregubov <ilya@moodle.com>
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class grade_items extends base {
  42  
  43      /** @var stdClass Course */
  44      public $course;
  45  
  46      /** @var grade_report_summary Grade report. */
  47      public $report;
  48  
  49      /** @var array Ungraded grade items counts with sql info. */
  50      public $ungradedcounts;
  51  
  52      /**
  53       * Entity constructor
  54       *
  55       * @param stdClass $course
  56       */
  57      public function __construct(stdClass $course) {
  58          $this->course = $course;
  59      }
  60  
  61      /**
  62       * Database tables that this entity uses and their default aliases
  63       *
  64       * @return array
  65       */
  66      protected function get_default_table_aliases(): array {
  67          return ['grade_items' => 'gi'];
  68      }
  69  
  70      /**
  71       * The default title for this entity in the list of columns/conditions/filters in the report builder
  72       *
  73       * @return lang_string
  74       */
  75      protected function get_default_entity_title(): lang_string {
  76          return new lang_string('gradeitem', 'grades');
  77      }
  78  
  79      /**
  80       * Initialise the entity
  81       *
  82       * @return base
  83       */
  84      public function initialise(): base {
  85          $context = \context_course::instance($this->course->id);
  86  
  87          $gpr = new grade_plugin_return(
  88              [
  89                  'type' => 'report',
  90                  'plugin' => 'summary',
  91                  'course' => $this->course,
  92              ]
  93          );
  94  
  95          $this->report = new grade_report_summary($this->course->id, $gpr, $context);
  96          $this->ungradedcounts = $this->report->ungraded_counts();
  97  
  98          $columns = $this->get_all_columns();
  99          foreach ($columns as $column) {
 100              $this->add_column($column);
 101          }
 102  
 103          $filters = $this->get_all_filters();
 104          foreach ($filters as $filter) {
 105              $this->add_filter($filter);
 106          }
 107  
 108          return $this;
 109      }
 110  
 111      /**
 112       * Returns list of all available columns
 113       *
 114       * @return column[]
 115       */
 116      protected function get_all_columns(): array {
 117  
 118          $tablealias = $this->get_table_alias('grade_items');
 119          $selectsql = "$tablealias.id, $tablealias.itemname, $tablealias.iteminstance, $tablealias.calculation,
 120           $tablealias.itemnumber, $tablealias.itemmodule, $tablealias.hidden, $tablealias.courseid";
 121  
 122          // Grade item name column.
 123          $columns[] = (new column(
 124              'name',
 125              null,
 126              $this->get_entity_name()
 127          ))
 128              ->add_joins($this->get_joins())
 129              ->set_type(column::TYPE_TEXT)
 130              ->add_fields($selectsql)
 131              ->add_callback(static function($value, $row): string {
 132                  global $PAGE, $CFG;
 133  
 134                  $renderer = new \core_renderer($PAGE, RENDERER_TARGET_GENERAL);
 135                  if ($row->itemmodule) {
 136                      $modinfo = get_fast_modinfo($row->courseid);
 137                      $instances = $modinfo->get_instances();
 138                      $cm = $instances[$row->itemmodule][$row->iteminstance];
 139  
 140                      if (file_exists($CFG->dirroot . '/mod/' . $row->itemmodule . '/grade.php')) {
 141                          $args = ['id' => $cm->id, 'itemnumber' => $row->itemnumber];
 142                          $url = new \moodle_url('/mod/' . $row->itemmodule . '/grade.php', $args);
 143                      } else {
 144                          $url = new \moodle_url('/mod/' . $row->itemmodule . '/view.php', ['id' => $cm->id]);
 145                      }
 146  
 147                      $imagedata = $renderer->pix_icon('monologo', '', $row->itemmodule, ['class' => 'activityicon']);
 148                      $purposeclass = plugin_supports('mod', $row->itemmodule, FEATURE_MOD_PURPOSE);
 149                      $purposeclass .= ' activityiconcontainer';
 150                      $purposeclass .= ' modicon_' . $row->itemmodule;
 151                      $imagedata = \html_writer::tag('div', $imagedata, ['class' => $purposeclass]);
 152  
 153                      $dimmed = '';
 154                      if ($row->hidden) {
 155                          $dimmed = ' dimmed_text';
 156                      }
 157                      $html = \html_writer::start_div('page-context-header' . $dimmed);
 158                      // Image data.
 159                      $html .= \html_writer::div($imagedata, 'page-header-image mr-2');
 160                      $prefix = \html_writer::div(get_string('pluginname', "mod_{$row->itemmodule}"),
 161                          'text-muted text-uppercase small line-height-3');
 162                      $name = $prefix . \html_writer::link($url, format_string($cm->name, true));
 163                      $html .= \html_writer::tag('div', $name, ['class' => 'page-header-headings']);
 164                  } else {
 165                      // Manual grade item.
 166                      $gradeitem = grade_item::fetch(['id' => $row->id, 'courseid' => $row->courseid]);
 167                      if ($row->calculation) {
 168                          $imagedata = $renderer->pix_icon('i/agg_sum', '');
 169                      } else {
 170                          $imagedata = $renderer->pix_icon('i/manual_item', '');
 171                      }
 172                      $imagedata = \html_writer::tag('div', $imagedata);
 173  
 174                      $html = \html_writer::start_div('page-context-header');
 175                      // Image data.
 176                      $html .= \html_writer::div($imagedata, 'page-header-image mr-2');
 177                      $html .= \html_writer::tag('div', $gradeitem->get_name(), ['class' => 'page-header-headings']);
 178                  }
 179                  return $html;
 180  
 181              });
 182  
 183          $report = [
 184              'report' => $this->report,
 185              'ungradedcounts' => $this->ungradedcounts
 186          ];
 187  
 188          // Average column.
 189          $columns[] = (new column(
 190              'average',
 191              new lang_string('average', 'grades'),
 192              $this->get_entity_name()
 193          ))
 194              ->add_joins($this->get_joins())
 195              ->set_type(column::TYPE_TEXT)
 196              ->add_field("$tablealias.id")
 197              ->add_callback(static function($value) use ($report): string {
 198  
 199                  $gradeitem = grade_item::fetch(['id' => $value]);
 200                  if (!empty($gradeitem->avg)) {
 201                      $averageformatted = '-';
 202                  }
 203  
 204                  if ($gradeitem->needsupdate) {
 205                      $averageformatted = get_string('error');
 206                  }
 207  
 208                  if (empty($averageformatted)) {
 209                      $ungradedcounts = $report['ungradedcounts'];
 210                      $aggr = $report['report']->calculate_average($gradeitem, $ungradedcounts);
 211  
 212                      if (empty($aggr['average'])) {
 213                          $averageformatted = '-';
 214                      } else {
 215                          $averagesdisplaytype = $ungradedcounts['report']['averagesdisplaytype'];
 216                          $averagesdecimalpoints = $ungradedcounts['report']['averagesdecimalpoints'];
 217                          $shownumberofgrades = $ungradedcounts['report']['shownumberofgrades'];
 218  
 219                          // Determine which display type to use for this average.
 220                          // No ==0 here, please resave the report and user preferences.
 221                          if ($averagesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) {
 222                              $displaytype = $gradeitem->get_displaytype();
 223                          } else {
 224                              $displaytype = $averagesdisplaytype;
 225                          }
 226  
 227                          // Override grade_item setting if a display preference (not inherit) was set for the averages.
 228                          if ($averagesdecimalpoints == GRADE_REPORT_PREFERENCE_INHERIT) {
 229                              $decimalpoints = $gradeitem->get_decimals();
 230                          } else {
 231                              $decimalpoints = $averagesdecimalpoints;
 232                          }
 233  
 234                          $gradehtml = grade_format_gradevalue($aggr['average'],
 235                              $gradeitem, true, $displaytype, $decimalpoints);
 236  
 237                          if ($shownumberofgrades) {
 238                              $numberofgrades = $aggr['meancount'];
 239                              $gradehtml .= " (" . $numberofgrades . ")";
 240                          }
 241                          $averageformatted = $gradehtml;
 242  
 243                      }
 244                  }
 245                  return $averageformatted;
 246              });
 247  
 248          return $columns;
 249      }
 250  
 251      /**
 252       * Return list of all available filters
 253       *
 254       * @return filter[]
 255       */
 256      protected function get_all_filters(): array {
 257          $tablealias = $this->get_table_alias('grade_items');
 258  
 259          // Activity type filter (for performance only load options on demand).
 260          $filters[] = (new filter(
 261              select::class,
 262              'name',
 263              new lang_string('activitytype', 'format_singleactivity'),
 264              $this->get_entity_name(),
 265              "coalesce({$tablealias}.itemmodule,{$tablealias}.itemtype)"
 266          ))
 267              ->add_joins($this->get_joins())
 268              ->set_options_callback([$this->report, 'item_types']);
 269  
 270          return $filters;
 271      }
 272  }