PageRenderTime 26ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/quiz/report/overview/overview_table.php

https://github.com/kpike/moodle
PHP | 349 lines | 256 code | 41 blank | 52 comment | 61 complexity | 36123a2ac496f16cee6e60efb2726320 MD5 | raw file
  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. * This file defines the quiz grades table.
  18. *
  19. * @package quiz
  20. * @subpackage overview
  21. * @copyright 2008 Jamie Pratt
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. defined('MOODLE_INTERNAL') || die();
  25. /**
  26. * This is a table subclass for displaying the quiz grades report.
  27. *
  28. * @copyright 2008 Jamie Pratt
  29. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30. */
  31. class quiz_report_overview_table extends quiz_attempt_report_table {
  32. protected $candelete;
  33. protected $regradedqs = array();
  34. public function __construct($quiz, $context, $qmsubselect, $groupstudents,
  35. $students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions) {
  36. parent::__construct('mod-quiz-report-overview-report', $quiz , $context,
  37. $qmsubselect, $groupstudents, $students, $questions, $candelete,
  38. $reporturl, $displayoptions);
  39. $this->detailedmarks = $detailedmarks;
  40. }
  41. public function build_table() {
  42. global $DB;
  43. if ($this->rawdata) {
  44. $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
  45. parent::build_table();
  46. //end of adding data from attempts data to table / download
  47. //now add averages at bottom of table :
  48. $params = array($this->quiz->id);
  49. $averagesql = '
  50. SELECT AVG(qg.grade) AS grade, COUNT(qg.grade) AS numaveraged
  51. FROM {quiz_grades} qg
  52. WHERE quiz = ?';
  53. $this->add_separator();
  54. if ($this->is_downloading()) {
  55. $namekey = 'lastname';
  56. } else {
  57. $namekey = 'fullname';
  58. }
  59. if ($this->groupstudents) {
  60. list($usql, $uparams) = $DB->get_in_or_equal($this->groupstudents);
  61. $record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql,
  62. array_merge($params, $uparams));
  63. $groupaveragerow = array(
  64. $namekey => get_string('groupavg', 'grades'),
  65. 'sumgrades' => $this->format_average($record),
  66. 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
  67. $record->grade, $this->quiz->id, $this->context)));
  68. if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
  69. $avggradebyq = $this->load_average_question_grades($this->groupstudents);
  70. $groupaveragerow += $this->format_average_grade_for_questions($avggradebyq);
  71. }
  72. $this->add_data_keyed($groupaveragerow);
  73. }
  74. if ($this->students) {
  75. list($usql, $uparams) = $DB->get_in_or_equal($this->students);
  76. $record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql,
  77. array_merge($params, $uparams));
  78. $overallaveragerow = array(
  79. $namekey => get_string('overallaverage', 'grades'),
  80. 'sumgrades' => $this->format_average($record),
  81. 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
  82. $record->grade, $this->quiz->id, $this->context)));
  83. if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
  84. $avggradebyq = $this->load_average_question_grades($this->students);
  85. $overallaveragerow += $this->format_average_grade_for_questions($avggradebyq);
  86. }
  87. $this->add_data_keyed($overallaveragerow);
  88. }
  89. }
  90. }
  91. protected function format_average_grade_for_questions($gradeaverages) {
  92. $row = array();
  93. if (!$gradeaverages) {
  94. $gradeaverages = array();
  95. }
  96. foreach ($this->questions as $question) {
  97. if (isset($gradeaverages[$question->slot]) && $question->maxmark > 0) {
  98. $record = $gradeaverages[$question->slot];
  99. $record->grade = quiz_rescale_grade(
  100. $record->averagefraction * $question->maxmark, $this->quiz, false);
  101. } else {
  102. $record = new stdClass();
  103. $record->grade = null;
  104. $record->numaveraged = null;
  105. }
  106. $row['qsgrade' . $question->slot] = $this->format_average($record, true);
  107. }
  108. return $row;
  109. }
  110. /**
  111. * Format an entry in an average row.
  112. * @param object $record with fields grade and numaveraged
  113. */
  114. protected function format_average($record, $question = false) {
  115. if (is_null($record->grade)) {
  116. $average = '-';
  117. } else if ($question) {
  118. $average = quiz_format_question_grade($this->quiz, $record->grade);
  119. } else {
  120. $average = quiz_format_grade($this->quiz, $record->grade);
  121. }
  122. if ($this->download) {
  123. return $average;
  124. } else if (is_null($record->numaveraged)) {
  125. return html_writer::tag('span', html_writer::tag('span',
  126. $average, array('class' => 'average')), array('class' => 'avgcell'));
  127. } else {
  128. return html_writer::tag('span', html_writer::tag('span',
  129. $average, array('class' => 'average')) . ' ' . html_writer::tag('span',
  130. '(' . $record->numaveraged . ')', array('class' => 'count')),
  131. array('class' => 'avgcell'));
  132. }
  133. }
  134. public function wrap_html_start() {
  135. if ($this->is_downloading() || !$this->candelete) {
  136. return;
  137. }
  138. // Start form
  139. $url = new moodle_url($this->reporturl, $this->displayoptions +
  140. array('sesskey' => sesskey()));
  141. echo '<div id="tablecontainer" class="overview-tablecontainer">';
  142. echo '<form id="attemptsform" method="post" action="' .
  143. $this->reporturl->out_omit_querystring() . '">';
  144. echo '<div style="display: none;">';
  145. echo html_writer::input_hidden_params($url);
  146. echo '</div>';
  147. echo '<div>';
  148. }
  149. public function wrap_html_finish() {
  150. if ($this->is_downloading() || !$this->candelete) {
  151. return;
  152. }
  153. // TODO add back are you sure, and convert to html_writer.
  154. echo '<div id="commands">';
  155. echo '<a href="javascript:select_all_in(\'DIV\', null, \'tablecontainer\');">' .
  156. get_string('selectall', 'quiz') . '</a> / ';
  157. echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'tablecontainer\');">' .
  158. get_string('selectnone', 'quiz') . '</a> ';
  159. echo '&nbsp;&nbsp;';
  160. if (has_capability('mod/quiz:regrade', $this->context)) {
  161. echo '<input type="submit" name="regrade" value="' .
  162. get_string('regradeselected', 'quiz_overview') . '"/>';
  163. }
  164. echo '<input type="submit" name="delete" value="' .
  165. get_string('deleteselected', 'quiz_overview') . '"/>';
  166. echo '</div>';
  167. // Close form
  168. echo '</div>';
  169. echo '</form></div>';
  170. }
  171. public function col_sumgrades($attempt) {
  172. if (!$attempt->timefinish) {
  173. return '-';
  174. }
  175. $grade = quiz_rescale_grade($attempt->sumgrades, $this->quiz);
  176. if ($this->is_downloading()) {
  177. return $grade;
  178. }
  179. if (isset($this->regradedqs[$attempt->usageid])) {
  180. $newsumgrade = 0;
  181. $oldsumgrade = 0;
  182. foreach ($this->questions as $question) {
  183. if (isset($this->regradedqs[$attempt->usageid][$question->slot])) {
  184. $newsumgrade += $this->regradedqs[$attempt->usageid]
  185. [$question->slot]->newfraction * $question->maxmark;
  186. $oldsumgrade += $this->regradedqs[$attempt->usageid]
  187. [$question->slot]->oldfraction * $question->maxmark;
  188. } else {
  189. $newsumgrade += $this->lateststeps[$attempt->usageid]
  190. [$question->slot]->fraction * $question->maxmark;
  191. $oldsumgrade += $this->lateststeps[$attempt->usageid]
  192. [$question->slot]->fraction * $question->maxmark;
  193. }
  194. }
  195. $newsumgrade = quiz_rescale_grade($newsumgrade, $this->quiz);
  196. $oldsumgrade = quiz_rescale_grade($oldsumgrade, $this->quiz);
  197. $grade = html_writer::tag('del', $oldsumgrade) . '/' .
  198. html_writer::empty_tag('br') . $newsumgrade;
  199. }
  200. return html_writer::link(new moodle_url('/mod/quiz/review.php',
  201. array('attempt' => $attempt->attempt)), $grade,
  202. array('title' => get_string('reviewattempt', 'quiz')));
  203. }
  204. /**
  205. * @param string $colname the name of the column.
  206. * @param object $attempt the row of data - see the SQL in display() in
  207. * mod/quiz/report/overview/report.php to see what fields are present,
  208. * and what they are called.
  209. * @return string the contents of the cell.
  210. */
  211. public function other_cols($colname, $attempt) {
  212. if (!preg_match('/^qsgrade(\d+)$/', $colname, $matches)) {
  213. return null;
  214. }
  215. $slot = $matches[1];
  216. $question = $this->questions[$slot];
  217. if (!isset($this->lateststeps[$attempt->usageid][$slot])) {
  218. return '-';
  219. }
  220. $stepdata = $this->lateststeps[$attempt->usageid][$slot];
  221. $state = question_state::get($stepdata->state);
  222. if ($question->maxmark == 0) {
  223. $grade = '-';
  224. } else if (is_null($stepdata->fraction)) {
  225. if ($state == question_state::$needsgrading) {
  226. $grade = get_string('requiresgrading', 'question');
  227. } else {
  228. $grade = '-';
  229. }
  230. } else {
  231. $grade = quiz_rescale_grade(
  232. $stepdata->fraction * $question->maxmark, $this->quiz, 'question');
  233. }
  234. if ($this->is_downloading()) {
  235. return $grade;
  236. }
  237. if (isset($this->regradedqs[$attempt->usageid][$slot])) {
  238. $gradefromdb = $grade;
  239. $newgrade = quiz_rescale_grade(
  240. $this->regradedqs[$attempt->usageid][$slot]->newfraction * $question->maxmark,
  241. $this->quiz, 'question');
  242. $oldgrade = quiz_rescale_grade(
  243. $this->regradedqs[$attempt->usageid][$slot]->oldfraction * $question->maxmark,
  244. $this->quiz, 'question');
  245. $grade = html_writer::tag('del', $oldgrade) . '/' .
  246. html_writer::empty_tag('br') . $newgrade;
  247. }
  248. return $this->make_review_link($grade, $attempt, $slot);
  249. }
  250. public function col_regraded($attempt) {
  251. if ($attempt->regraded == '') {
  252. return '';
  253. } else if ($attempt->regraded == 0) {
  254. return get_string('needed', 'quiz_overview');
  255. } else if ($attempt->regraded == 1) {
  256. return get_string('done', 'quiz_overview');
  257. }
  258. }
  259. protected function requires_latest_steps_loaded() {
  260. return $this->detailedmarks;
  261. }
  262. protected function is_latest_step_column($column) {
  263. if (preg_match('/^qsgrade([0-9]+)/', $column, $matches)) {
  264. return $matches[1];
  265. }
  266. return false;
  267. }
  268. protected function get_required_latest_state_fields($slot, $alias) {
  269. return "$alias.fraction * $alias.maxmark AS qsgrade$slot";
  270. }
  271. public function query_db($pagesize, $useinitialsbar = true) {
  272. parent::query_db($pagesize, $useinitialsbar);
  273. if ($this->detailedmarks && has_capability('mod/quiz:regrade', $this->context)) {
  274. $this->regradedqs = $this->get_regraded_questions();
  275. }
  276. }
  277. /**
  278. * Load the average grade for each question, averaged over particular users.
  279. * @param array $userids the user ids to average over.
  280. */
  281. protected function load_average_question_grades($userids) {
  282. global $DB;
  283. $qmfilter = '';
  284. if ($this->quiz->attempts != 1) {
  285. $qmfilter = '(' . quiz_report_qm_filter_select($this->quiz, 'quiza') . ') AND ';
  286. }
  287. list($usql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'u');
  288. $params['quizid'] = $this->quiz->id;
  289. $qubaids = new qubaid_join(
  290. '{quiz_attempts} quiza',
  291. 'quiza.uniqueid',
  292. "quiza.userid $usql AND quiza.quiz = :quizid",
  293. $params);
  294. $dm = new question_engine_data_mapper();
  295. return $dm->load_average_marks($qubaids, array_keys($this->questions));
  296. }
  297. /**
  298. * Get all the questions in all the attempts being displayed that need regrading.
  299. * @return array A two dimensional array $questionusageid => $slot => $regradeinfo.
  300. */
  301. protected function get_regraded_questions() {
  302. global $DB;
  303. $qubaids = $this->get_qubaids_condition();
  304. $regradedqs = $DB->get_records_select('quiz_overview_regrades',
  305. 'questionusageid ' . $qubaids->usage_id_in(), $qubaids->usage_id_in_params());
  306. return quiz_report_index_by_keys($regradedqs, array('questionusageid', 'slot'));
  307. }
  308. }