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.
<?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/>.

< /** < * This file defines the quiz grades table. < * < * @package quiz_overview < * @copyright 2008 Jamie Pratt < * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later < */ < < < defined('MOODLE_INTERNAL') || die(); < < require_once($CFG->dirroot . '/mod/quiz/report/attemptsreport_table.php'); <
> use mod_quiz\local\reports\attempts_report_table; > use mod_quiz\quiz_attempt;
/** * This is a table subclass for displaying the quiz grades report. * * @copyright 2008 Jamie Pratt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */
< class quiz_overview_table extends quiz_attempts_report_table {
> class quiz_overview_table extends attempts_report_table {
< protected $regradedqs = array();
> /** @var array used to store information about which questoins have been regraded. */ > protected $regradedqs = [];
/** * Constructor
< * @param object $quiz
> * @param stdClass $quiz
* @param context $context * @param string $qmsubselect * @param quiz_overview_options $options * @param \core\dml\sql_join $groupstudentsjoins * @param \core\dml\sql_join $studentsjoins * @param array $questions * @param moodle_url $reporturl */ public function __construct($quiz, $context, $qmsubselect, quiz_overview_options $options, \core\dml\sql_join $groupstudentsjoins, \core\dml\sql_join $studentsjoins, $questions, $reporturl) { parent::__construct('mod-quiz-report-overview-report', $quiz , $context, $qmsubselect, $options, $groupstudentsjoins, $studentsjoins, $questions, $reporturl); } public function build_table() { global $DB; if (!$this->rawdata) { return; } $this->strtimeformat = str_replace(',', ' ', get_string('strftimedatetime')); parent::build_table(); // End of adding the data from attempts. Now add averages at bottom. $this->add_separator(); if (!empty($this->groupstudentsjoins->joins)) { $hasgroupstudents = $DB->record_exists_sql(" SELECT 1 FROM {user} u {$this->groupstudentsjoins->joins} WHERE {$this->groupstudentsjoins->wheres} ", $this->groupstudentsjoins->params); if ($hasgroupstudents) { $this->add_average_row(get_string('groupavg', 'grades'), $this->groupstudentsjoins); } } if (!empty($this->studentsjoins->joins)) { $hasstudents = $DB->record_exists_sql(" SELECT 1 FROM {user} u {$this->studentsjoins->joins} WHERE {$this->studentsjoins->wheres} " , $this->studentsjoins->params); if ($hasstudents) { $this->add_average_row(get_string('overallaverage', 'grades'), $this->studentsjoins); } } } /** * Calculate the average overall and question scores for a set of attempts at the quiz. * * @param string $label the title ot use for this row. * @param \core\dml\sql_join $usersjoins to indicate a set of users. * @return array of table cells that make up the average row. */ public function compute_average_row($label, \core\dml\sql_join $usersjoins) { global $DB; list($fields, $from, $where, $params) = $this->base_sql($usersjoins); $record = $DB->get_record_sql(" SELECT AVG(quizaouter.sumgrades) AS grade, COUNT(quizaouter.sumgrades) AS numaveraged FROM {quiz_attempts} quizaouter JOIN ( SELECT DISTINCT quiza.id FROM $from WHERE $where ) relevant_attempt_ids ON quizaouter.id = relevant_attempt_ids.id ", $params); $record->grade = quiz_rescale_grade($record->grade, $this->quiz, false); if ($this->is_downloading()) { $namekey = 'lastname'; } else { $namekey = 'fullname'; }
< $averagerow = array(
> $averagerow = [
$namekey => $label, 'sumgrades' => $this->format_average($record), 'feedbacktext' => strip_tags(quiz_report_feedback_for_grade( $record->grade, $this->quiz->id, $this->context))
< );
> ];
if ($this->options->slotmarks) { $dm = new question_engine_data_mapper(); $qubaids = new qubaid_join("{quiz_attempts} quizaouter JOIN ( SELECT DISTINCT quiza.id FROM $from WHERE $where ) relevant_attempt_ids ON quizaouter.id = relevant_attempt_ids.id", 'quizaouter.uniqueid', '1 = 1', $params); $avggradebyq = $dm->load_average_marks($qubaids, array_keys($this->questions)); $averagerow += $this->format_average_grade_for_questions($avggradebyq); } return $averagerow; } /** * Add an average grade row for a set of users. * * @param string $label the title ot use for this row. * @param \core\dml\sql_join $usersjoins (joins, wheres, params) for the users to average over. */ protected function add_average_row($label, \core\dml\sql_join $usersjoins) { $averagerow = $this->compute_average_row($label, $usersjoins); $this->add_data_keyed($averagerow); } /** * Helper userd by {@link add_average_row()}. * @param array $gradeaverages the raw grades. * @return array the (partial) row of data. */ protected function format_average_grade_for_questions($gradeaverages) {
< $row = array();
> $row = [];
if (!$gradeaverages) {
< $gradeaverages = array();
> $gradeaverages = [];
} foreach ($this->questions as $question) { if (isset($gradeaverages[$question->slot]) && $question->maxmark > 0) { $record = $gradeaverages[$question->slot]; $record->grade = quiz_rescale_grade( $record->averagefraction * $question->maxmark, $this->quiz, false); } else { $record = new stdClass(); $record->grade = null; $record->numaveraged = 0; } $row['qsgrade' . $question->slot] = $this->format_average($record, true); } return $row; } /** * Format an entry in an average row.
< * @param object $record with fields grade and numaveraged.
> * @param stdClass $record with fields grade and numaveraged.
* @param bool $question true if this is a question score, false if it is an overall score. * @return string HTML fragment for an average score (with number of things included in the average). */ protected function format_average($record, $question = false) { if (is_null($record->grade)) { $average = '-'; } else if ($question) { $average = quiz_format_question_grade($this->quiz, $record->grade); } else { $average = quiz_format_grade($this->quiz, $record->grade); } if ($this->download) { return $average; } else if (is_null($record->numaveraged) || $record->numaveraged == 0) { return html_writer::tag('span', html_writer::tag('span',
< $average, array('class' => 'average')), array('class' => 'avgcell'));
> $average, ['class' => 'average']), ['class' => 'avgcell']);
} else { return html_writer::tag('span', html_writer::tag('span',
< $average, array('class' => 'average')) . ' ' . html_writer::tag('span', < '(' . $record->numaveraged . ')', array('class' => 'count')), < array('class' => 'avgcell'));
> $average, ['class' => 'average']) . ' ' . html_writer::tag('span', > '(' . $record->numaveraged . ')', ['class' => 'count']), > ['class' => 'avgcell']);
} } protected function submit_buttons() { if (has_capability('mod/quiz:regrade', $this->context)) { $regradebuttonparams = [ 'type' => 'submit', 'class' => 'btn btn-secondary mr-1', 'name' => 'regrade', 'value' => get_string('regradeselected', 'quiz_overview'), 'data-action' => 'toggle', 'data-togglegroup' => $this->togglegroup, 'data-toggle' => 'action', 'disabled' => true ]; echo html_writer::empty_tag('input', $regradebuttonparams); } parent::submit_buttons(); } public function col_sumgrades($attempt) { if ($attempt->state != quiz_attempt::FINISHED) { return '-'; } $grade = quiz_rescale_grade($attempt->sumgrades, $this->quiz); if ($this->is_downloading()) { return $grade; } if (isset($this->regradedqs[$attempt->usageid])) { $newsumgrade = 0; $oldsumgrade = 0; foreach ($this->questions as $question) { if (isset($this->regradedqs[$attempt->usageid][$question->slot])) { $newsumgrade += $this->regradedqs[$attempt->usageid] [$question->slot]->newfraction * $question->maxmark; $oldsumgrade += $this->regradedqs[$attempt->usageid] [$question->slot]->oldfraction * $question->maxmark; } else { $newsumgrade += $this->lateststeps[$attempt->usageid] [$question->slot]->fraction * $question->maxmark; $oldsumgrade += $this->lateststeps[$attempt->usageid] [$question->slot]->fraction * $question->maxmark; } } $newsumgrade = quiz_rescale_grade($newsumgrade, $this->quiz); $oldsumgrade = quiz_rescale_grade($oldsumgrade, $this->quiz); $grade = html_writer::tag('del', $oldsumgrade) . '/' . html_writer::empty_tag('br') . $newsumgrade; } return html_writer::link(new moodle_url('/mod/quiz/review.php',
< array('attempt' => $attempt->attempt)), $grade, < array('title' => get_string('reviewattempt', 'quiz')));
> ['attempt' => $attempt->attempt]), $grade, > ['title' => get_string('reviewattempt', 'quiz')]);
} /** * @param string $colname the name of the column.
< * @param object $attempt the row of data - see the SQL in display() in
> * @param stdClass $attempt the row of data - see the SQL in display() in
* mod/quiz/report/overview/report.php to see what fields are present, * and what they are called. * @return string the contents of the cell. */ public function other_cols($colname, $attempt) { if (!preg_match('/^qsgrade(\d+)$/', $colname, $matches)) { return parent::other_cols($colname, $attempt); } $slot = $matches[1]; $question = $this->questions[$slot]; if (!isset($this->lateststeps[$attempt->usageid][$slot])) { return '-'; } $stepdata = $this->lateststeps[$attempt->usageid][$slot]; $state = question_state::get($stepdata->state); if ($question->maxmark == 0) { $grade = '-'; } else if (is_null($stepdata->fraction)) { if ($state == question_state::$needsgrading) { $grade = get_string('requiresgrading', 'question'); } else { $grade = '-'; } } else { $grade = quiz_rescale_grade( $stepdata->fraction * $question->maxmark, $this->quiz, 'question'); } if ($this->is_downloading()) { return $grade; } if (isset($this->regradedqs[$attempt->usageid][$slot])) { $gradefromdb = $grade; $newgrade = quiz_rescale_grade( $this->regradedqs[$attempt->usageid][$slot]->newfraction * $question->maxmark, $this->quiz, 'question'); $oldgrade = quiz_rescale_grade( $this->regradedqs[$attempt->usageid][$slot]->oldfraction * $question->maxmark, $this->quiz, 'question'); $grade = html_writer::tag('del', $oldgrade) . '/' . html_writer::empty_tag('br') . $newgrade; } return $this->make_review_link($grade, $attempt, $slot); } public function col_regraded($attempt) { if ($attempt->regraded == '') { return ''; } else if ($attempt->regraded == 0) { return get_string('needed', 'quiz_overview'); } else if ($attempt->regraded == 1) { return get_string('done', 'quiz_overview'); } } protected function update_sql_after_count($fields, $from, $where, $params) { $fields .= ", COALESCE(( SELECT MAX(qqr.regraded) FROM {quiz_overview_regrades} qqr WHERE qqr.questionusageid = quiza.uniqueid ), -1) AS regraded"; if ($this->options->onlyregraded) { $where .= " AND COALESCE(( SELECT MAX(qqr.regraded) FROM {quiz_overview_regrades} qqr WHERE qqr.questionusageid = quiza.uniqueid ), -1) <> -1"; } return [$fields, $from, $where, $params]; } protected function requires_latest_steps_loaded() { return $this->options->slotmarks; } protected function is_latest_step_column($column) { if (preg_match('/^qsgrade([0-9]+)/', $column, $matches)) { return $matches[1]; } return false; } protected function get_required_latest_state_fields($slot, $alias) { return "$alias.fraction * $alias.maxmark AS qsgrade$slot"; } public function query_db($pagesize, $useinitialsbar = true) { parent::query_db($pagesize, $useinitialsbar); if ($this->options->slotmarks && has_capability('mod/quiz:regrade', $this->context)) { $this->regradedqs = $this->get_regraded_questions(); } } /** * Get all the questions in all the attempts being displayed that need regrading. * @return array A two dimensional array $questionusageid => $slot => $regradeinfo. */ protected function get_regraded_questions() { global $DB; $qubaids = $this->get_qubaids_condition(); $regradedqs = $DB->get_records_select('quiz_overview_regrades', 'questionusageid ' . $qubaids->usage_id_in(), $qubaids->usage_id_in_params());
< return quiz_report_index_by_keys($regradedqs, array('questionusageid', 'slot'));
> return quiz_report_index_by_keys($regradedqs, ['questionusageid', 'slot']);
} }