Proyectos de Subversion Moodle

Rev

Rev 11 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 11 Rev 1441
Línea 23... Línea 23...
23
 */
23
 */
Línea 24... Línea 24...
24
 
24
 
25
use mod_quiz\local\reports\attempts_report;
25
use mod_quiz\local\reports\attempts_report;
26
use mod_quiz\question\bank\qbank_helper;
26
use mod_quiz\question\bank\qbank_helper;
27
use mod_quiz\quiz_attempt;
-
 
Línea 28... Línea 27...
28
use mod_quiz\quiz_settings;
27
use mod_quiz\quiz_attempt;
Línea 29... Línea 28...
29
 
28
 
30
defined('MOODLE_INTERNAL') || die();
29
defined('MOODLE_INTERNAL') || die();
Línea 41... Línea 40...
41
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42
 */
41
 */
43
class quiz_overview_report extends attempts_report {
42
class quiz_overview_report extends attempts_report {
Línea 44... Línea 43...
44
 
43
 
45
    public function display($quiz, $cm, $course) {
44
    public function display($quiz, $cm, $course) {
Línea 46... Línea 45...
46
        global $DB, $PAGE;
45
        global $DB, $PAGE, $OUTPUT;
47
 
46
 
Línea 48... Línea 47...
48
        list($currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins) = $this->init(
47
        list($currentgroup, $studentsjoins, $groupstudentsjoins, $allowedjoins) = $this->init(
Línea 91... Línea 90...
91
            $hasstudents = $DB->record_exists_sql($sql, $studentsjoins->params);
90
            $hasstudents = $DB->record_exists_sql($sql, $studentsjoins->params);
92
        }
91
        }
93
        if ($options->attempts == self::ALL_WITH) {
92
        if ($options->attempts == self::ALL_WITH) {
94
            // This option is only available to users who can access all groups in
93
            // This option is only available to users who can access all groups in
95
            // groups mode, so setting allowed to empty (which means all quiz attempts
94
            // groups mode, so setting allowed to empty (which means all quiz attempts
96
            // are accessible, is not a security porblem.
95
            // are accessible, is not a security problem.
97
            $allowedjoins = new \core\dml\sql_join();
96
            $allowedjoins = new \core\dml\sql_join();
98
        }
97
        }
Línea 99... Línea 98...
99
 
98
 
Línea 113... Línea 112...
113
 
112
 
114
        $hasstudents = $hasstudents && (!$currentgroup || $this->hasgroupstudents);
113
        $hasstudents = $hasstudents && (!$currentgroup || $this->hasgroupstudents);
115
        if ($hasquestions && ($hasstudents || $options->attempts == self::ALL_WITH)) {
114
        if ($hasquestions && ($hasstudents || $options->attempts == self::ALL_WITH)) {
116
            // Construct the SQL.
115
            // Construct the SQL.
117
            $table->setup_sql_queries($allowedjoins);
-
 
118
 
116
            $table->setup_sql_queries($allowedjoins);
119
            if (!$table->is_downloading()) {
-
 
120
                // Output the regrade buttons.
-
 
121
                if (has_capability('mod/quiz:regrade', $this->context)) {
-
 
122
                    $regradesneeded = $this->count_question_attempts_needing_regrade(
-
 
123
                            $quiz, $groupstudentsjoins);
-
 
124
                    if ($currentgroup) {
-
 
125
                        $a= new stdClass();
-
 
126
                        $a->groupname = format_string(groups_get_group_name($currentgroup), true, [
-
 
127
                            'context' => $this->context,
-
 
128
                        ]);
-
 
129
                        $a->coursestudents = get_string('participants');
-
 
130
                        $a->countregradeneeded = $regradesneeded;
-
 
131
                        $regradealldrydolabel =
-
 
132
                                get_string('regradealldrydogroup', 'quiz_overview', $a);
-
 
133
                        $regradealldrylabel =
-
 
134
                                get_string('regradealldrygroup', 'quiz_overview', $a);
-
 
135
                        $regradealllabel =
-
 
136
                                get_string('regradeallgroup', 'quiz_overview', $a);
-
 
137
                    } else {
-
 
138
                        $regradealldrydolabel =
-
 
139
                                get_string('regradealldrydo', 'quiz_overview', $regradesneeded);
-
 
140
                        $regradealldrylabel =
-
 
141
                                get_string('regradealldry', 'quiz_overview');
-
 
142
                        $regradealllabel =
-
 
143
                                get_string('regradeall', 'quiz_overview');
-
 
144
                    }
-
 
145
                    $displayurl = new moodle_url($options->get_url(), ['sesskey' => sesskey()]);
-
 
146
                    echo '<div class="regradebuttons">';
-
 
147
                    echo '<form action="'.$displayurl->out_omit_querystring().'">';
-
 
148
                    echo '<div>';
-
 
149
                    echo html_writer::input_hidden_params($displayurl);
-
 
150
                    echo '<input type="submit" class="btn btn-secondary" name="regradeall" value="'.$regradealllabel.'"/>';
-
 
151
                    echo '<input type="submit" class="btn btn-secondary ml-1" name="regradealldry" value="' .
-
 
152
                            $regradealldrylabel . '"/>';
-
 
153
                    if ($regradesneeded) {
-
 
154
                        echo '<input type="submit" class="btn btn-secondary ml-1" name="regradealldrydo" value="' .
-
 
155
                                $regradealldrydolabel . '"/>';
-
 
156
                    }
-
 
157
                    echo '</div>';
-
 
158
                    echo '</form>';
-
 
159
                    echo '</div>';
-
 
160
                }
117
            if (!$table->is_downloading()) {
161
                // Print information on the grading method.
118
                // Print information on the grading method.
162
                if ($strattempthighlight = quiz_report_highlighting_grading_method(
119
                if ($strattempthighlight = quiz_report_highlighting_grading_method(
163
                        $quiz, $this->qmsubselect, $options->onlygraded)) {
120
                        $quiz, $this->qmsubselect, $options->onlygraded)) {
164
                    echo '<div class="quizattemptcounts mt-3">' . $strattempthighlight . '</div>';
121
                    echo '<div class="quizattemptcounts mt-3">' . $strattempthighlight . '</div>';
Línea 180... Línea 137...
180
            $this->add_time_columns($columns, $headers);
137
            $this->add_time_columns($columns, $headers);
Línea 181... Línea 138...
181
 
138
 
182
            $this->add_grade_columns($quiz, $options->usercanseegrades, $columns, $headers, false);
139
            $this->add_grade_columns($quiz, $options->usercanseegrades, $columns, $headers, false);
Línea 183... Línea 140...
183
            $this->add_grade_item_columns($options->usercanseegrades, $columns, $headers);
140
            $this->add_grade_item_columns($options->usercanseegrades, $columns, $headers);
-
 
141
 
184
 
142
            $canregrade = has_capability('mod/quiz:regrade', $this->context);
185
            if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) &&
143
            if (!$table->is_downloading() && $canregrade &&
186
                    $this->has_regraded_questions($table->sql->from, $table->sql->where, $table->sql->params)) {
144
                    $this->has_regraded_questions($table->sql->from, $table->sql->where, $table->sql->params)) {
187
                $columns[] = 'regraded';
145
                $columns[] = 'regraded';
Línea 204... Línea 162...
204
 
162
 
205
            $this->set_up_table_columns($table, $columns, $headers, $this->get_base_url(), $options, false);
163
            $this->set_up_table_columns($table, $columns, $headers, $this->get_base_url(), $options, false);
Línea 206... Línea 164...
206
            $table->set_attribute('class', 'generaltable generalbox grades');
164
            $table->set_attribute('class', 'generaltable generalbox grades');
-
 
165
 
-
 
166
            $table->out($options->pagesize, true);
-
 
167
 
-
 
168
            if ($canregrade && !$table->is_downloading()) {
207
 
169
                $this->display_commit_regrade_if_required($quiz, $groupstudentsjoins, $options);
Línea 208... Línea 170...
208
            $table->out($options->pagesize, true);
170
            }
209
        }
171
        }
210
 
172
 
Línea 237... Línea 199...
237
                $graphname = get_string('overviewreportgraph', 'quiz_overview');
199
                $graphname = get_string('overviewreportgraph', 'quiz_overview');
238
                // Numerical range data should display in LTR even for RTL languages.
200
                // Numerical range data should display in LTR even for RTL languages.
239
                echo $output->chart($chart, $graphname, ['dir' => 'ltr']);
201
                echo $output->chart($chart, $graphname, ['dir' => 'ltr']);
240
            }
202
            }
241
        }
203
        }
-
 
204
 
242
        return true;
205
        return true;
243
    }
206
    }
Línea 244... Línea 207...
244
 
207
 
-
 
208
    /**
-
 
209
     * If a previous dry run regrade had been done, display a message to commit the changes.
-
 
210
     *
-
 
211
     * @param stdClass $quiz quiz settings.
-
 
212
     * @param \core\dml\sql_join $groupstudentsjoins which users' attempts should be considered.
-
 
213
     * @param quiz_overview_options $options report options.
-
 
214
     */
-
 
215
    protected function display_commit_regrade_if_required(
-
 
216
        stdClass $quiz,
-
 
217
        \core\dml\sql_join $groupstudentsjoins,
-
 
218
        quiz_overview_options $options,
-
 
219
    ) {
-
 
220
        global $OUTPUT;
-
 
221
 
-
 
222
        [$attemptcount, $slotcount] = $this->count_attempts_and_questions_needing_regrade($quiz, $groupstudentsjoins);
-
 
223
        if (!$attemptcount) {
-
 
224
            return;
-
 
225
        }
-
 
226
 
-
 
227
        $commitregradeurl = new moodle_url($options->get_url(), ['sesskey' => sesskey(), 'regradealldrydo' => 1]);
-
 
228
 
-
 
229
        // We can't use $OUTPUT->notification because is aggressively cleans the message, which strips the button.
-
 
230
        echo html_writer::div(get_string('regrade_regradeneedednotificationmessage', 'quiz_overview',
-
 
231
                ['attempts' => $attemptcount, 'questions' => $slotcount]) . ' ' .
-
 
232
            $OUTPUT->single_button($commitregradeurl, get_string('regrade_commitregrade', 'quiz_overview')),
-
 
233
            'alert alert-info alert-block fade in');
-
 
234
    }
-
 
235
 
-
 
236
    #[\Override]
-
 
237
    protected function process_actions($quiz, $cm, $currentgroup, \core\dml\sql_join $groupstudentsjoins,
-
 
238
            \core\dml\sql_join $allowedjoins, $redirecturl) {
-
 
239
        parent::process_actions($quiz, $cm, $currentgroup, $groupstudentsjoins, $allowedjoins, $redirecturl);
-
 
240
 
-
 
241
        // Process regrade actions.
-
 
242
        $this->process_regrade_actions($quiz, $cm, $currentgroup, $groupstudentsjoins, $redirecturl);
-
 
243
    }
-
 
244
 
245
    /**
245
    /**
246
     * Extends parent function processing any submitted actions.
246
     * Extends parent function processing any submitted actions.
247
     *
247
     *
248
     * @param stdClass $quiz
248
     * @param stdClass $quiz
249
     * @param stdClass $cm
249
     * @param stdClass $cm
250
     * @param int $currentgroup
250
     * @param int $currentgroup
251
     * @param \core\dml\sql_join $groupstudentsjoins (joins, wheres, params)
-
 
252
     * @param \core\dml\sql_join $allowedjoins (joins, wheres, params)
251
     * @param \core\dml\sql_join $groupstudentsjoins (joins, wheres, params)
253
     * @param moodle_url $redirecturl
252
     * @param moodle_url $redirecturl
254
     */
253
     */
255
    protected function process_actions($quiz, $cm, $currentgroup, \core\dml\sql_join $groupstudentsjoins,
254
    protected function process_regrade_actions($quiz, $cm, $currentgroup,
256
            \core\dml\sql_join $allowedjoins, $redirecturl) {
-
 
Línea 257... Línea 255...
257
        parent::process_actions($quiz, $cm, $currentgroup, $groupstudentsjoins, $allowedjoins, $redirecturl);
255
            \core\dml\sql_join $groupstudentsjoins, moodle_url $redirecturl) {
258
 
-
 
259
        if (empty($currentgroup) || $this->hasgroupstudents) {
-
 
260
            if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) {
-
 
261
                if ($attemptids = optional_param_array('attemptid', [], PARAM_INT)) {
-
 
262
                    $this->start_regrade($quiz, $cm);
256
 
263
                    $this->regrade_attempts($quiz, false, $groupstudentsjoins, $attemptids);
257
        if ($currentgroup && !$this->hasgroupstudents) {
-
 
258
            return;
264
                    $this->finish_regrade($redirecturl);
259
        }
265
                }
260
        if (!has_capability('mod/quiz:regrade', $this->context)) {
Línea -... Línea 261...
-
 
261
            return;
266
            }
262
        }
-
 
263
 
267
        }
264
        $dryrun = optional_param('dryrunregrade', 0, PARAM_BOOL);
-
 
265
        if ($dryrun || optional_param('regrade', 0, PARAM_BOOL)) {
268
 
266
 
-
 
267
            $attemptids = [];
-
 
268
            if (optional_param('regradeselectedattempts', 0, PARAM_BOOL)) {
269
        if (optional_param('regradeall', 0, PARAM_BOOL) && confirm_sesskey()) {
269
                $attemptids = optional_param_array('attemptid', [], PARAM_INT);
-
 
270
            }
-
 
271
 
-
 
272
            $slots = null;
Línea 270... Línea -...
270
            $this->start_regrade($quiz, $cm);
-
 
271
            $this->regrade_attempts($quiz, false, $groupstudentsjoins);
273
            if (optional_param('regradeselectedquestions', 0, PARAM_BOOL)) {
272
            $this->finish_regrade($redirecturl);
274
                $slots = optional_param_array('regradeslot', [], PARAM_INT);
273
 
275
            }
-
 
276
 
Línea -... Línea 277...
-
 
277
            $this->start_regrade($quiz, $cm);
274
        } else if (optional_param('regradealldry', 0, PARAM_BOOL) && confirm_sesskey()) {
278
            $this->regrade_attempts($quiz, $dryrun, $groupstudentsjoins, $attemptids, $slots);
275
            $this->start_regrade($quiz, $cm);
279
            $this->finish_regrade($redirecturl);
276
            $this->regrade_attempts($quiz, true, $groupstudentsjoins);
280
        }
277
            $this->finish_regrade($redirecturl);
281
 
278
 
282
        // Process commit of a previous dry run.
279
        } else if (optional_param('regradealldrydo', 0, PARAM_BOOL) && confirm_sesskey()) {
283
        if (optional_param('regradealldrydo', 0, PARAM_BOOL) && confirm_sesskey()) {
Línea 327... Línea 331...
327
     * Note, $attempt is not upgraded in the database. The caller needs to do that.
331
     * Note, $attempt is not upgraded in the database. The caller needs to do that.
328
     * However, $attempt->sumgrades is updated, if this is not a dry run.
332
     * However, $attempt->sumgrades is updated, if this is not a dry run.
329
     *
333
     *
330
     * @param stdClass $attempt the quiz attempt to regrade.
334
     * @param stdClass $attempt the quiz attempt to regrade.
331
     * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real.
335
     * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real.
332
     * @param array $slots if null, regrade all questions, otherwise, just regrade
336
     * @param array|null $slots if null, regrade all questions, otherwise, just regrade
333
     *      the questions with those slots.
337
     *      the questions with those slots.
334
     * @return array messages array with keys slot number, and values reasons why that slot cannot be regraded.
338
     * @return array messages array with keys slot number, and values reasons why that slot cannot be regraded.
335
     */
339
     */
336
    public function regrade_attempt($attempt, $dryrun = false, $slots = null): array {
340
    public function regrade_attempt($attempt, $dryrun = false, $slots = null): array {
337
        global $DB;
341
        global $DB;
Línea 406... Línea 410...
406
     * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real.
410
     * @param bool $dryrun if true, do a pretend regrade, otherwise do it for real.
407
     * @param \core\dml\sql_join|null $groupstudentsjoins empty for all attempts, otherwise regrade attempts
411
     * @param \core\dml\sql_join|null $groupstudentsjoins empty for all attempts, otherwise regrade attempts
408
     * for these users.
412
     * for these users.
409
     * @param array $attemptids blank for all attempts, otherwise only regrade
413
     * @param array $attemptids blank for all attempts, otherwise only regrade
410
     * attempts whose id is in this list.
414
     * attempts whose id is in this list.
-
 
415
     * @param array|null $slots if null, regrade all questions, otherwise, just regrade
-
 
416
     *      the questions with those slots.
411
     */
417
     */
412
    protected function regrade_attempts($quiz, $dryrun = false,
418
    protected function regrade_attempts($quiz, $dryrun = false,
413
            core\dml\sql_join $groupstudentsjoins = null, $attemptids = []) {
419
            ?\core\dml\sql_join $groupstudentsjoins = null, $attemptids = [], ?array $slots = null) {
414
        global $DB;
420
        global $DB;
415
        $this->unlock_session();
421
        $this->unlock_session();
Línea -... Línea 422...
-
 
422
 
416
 
423
        // Get the attempts to regrade.
417
        $userfieldsapi = \core_user\fields::for_name();
424
        $userfieldsapi = \core_user\fields::for_name();
418
        $sql = "SELECT quiza.*, " . $userfieldsapi->get_sql('u', false, '', '', false)->selects . "
425
        $sql = "SELECT quiza.*, " . $userfieldsapi->get_sql('u', false, '', '', false)->selects . "
419
                  FROM {quiz_attempts} quiza
426
                  FROM {quiz_attempts} quiza
420
                  JOIN {user} u ON u.id = quiza.userid";
427
                  JOIN {user} u ON u.id = quiza.userid";
Línea 437... Línea 444...
437
        $attempts = $DB->get_records_sql($sql, $params);
444
        $attempts = $DB->get_records_sql($sql, $params);
438
        if (!$attempts) {
445
        if (!$attempts) {
439
            return;
446
            return;
440
        }
447
        }
Línea -... Línea 448...
-
 
448
 
-
 
449
        // If only regrading some slots, put that information where regrade_batch_of_attempts expects.
-
 
450
        if ($slots) {
-
 
451
            foreach ($attempts as $attempt) {
-
 
452
                $attempt->regradeonlyslots = $slots;
-
 
453
            }
-
 
454
        }
441
 
455
 
442
        $this->regrade_batch_of_attempts($quiz, $attempts, $dryrun, $groupstudentsjoins);
456
        $this->regrade_batch_of_attempts($quiz, $attempts, $dryrun, $groupstudentsjoins, $slots);
Línea 443... Línea 457...
443
    }
457
    }
444
 
458
 
445
    /**
459
    /**
446
     * Regrade those questions in those attempts that are marked as needing regrading
460
     * Regrade the questions in the attempts that are marked as needing it in quiz_overview_regrades.
447
     * in the quiz_overview_regrades table.
461
     *
448
     * @param stdClass $quiz the quiz settings.
462
     * @param stdClass $quiz the quiz settings.
449
     * @param \core\dml\sql_join $groupstudentsjoins empty for all attempts, otherwise regrade attempts
463
     * @param \core\dml\sql_join $groupstudentsjoins empty for all attempts, otherwise regrade attempts
450
     * for these users.
464
     *      for these users.
451
     */
465
     */
452
    protected function regrade_attempts_needing_it($quiz, \core\dml\sql_join $groupstudentsjoins) {
466
    protected function regrade_attempts_needing_it($quiz, \core\dml\sql_join $groupstudentsjoins) {
Línea 525... Línea 539...
525
        foreach ($attempts as $attempt) {
539
        foreach ($attempts as $attempt) {
526
            $a['done']++;
540
            $a['done']++;
527
            $a['attemptnum'] = $attempt->attempt;
541
            $a['attemptnum'] = $attempt->attempt;
528
            $a['name'] = fullname($attempt);
542
            $a['name'] = fullname($attempt);
529
            $a['attemptid'] = $attempt->id;
543
            $a['attemptid'] = $attempt->id;
530
            if (!isset($attempt->regradeonlyslots)) {
-
 
531
                $attempt->regradeonlyslots = null;
-
 
532
            }
-
 
533
            $progressbar->update($a['done'], $a['count'],
544
            $progressbar->update($a['done'], $a['count'],
534
                    get_string('regradingattemptxofywithdetails', 'quiz_overview', $a));
545
                    get_string('regradingattemptxofywithdetails', 'quiz_overview', $a));
535
            $messages = $this->regrade_attempt($attempt, $dryrun, $attempt->regradeonlyslots);
546
            $messages = $this->regrade_attempt($attempt, $dryrun, $attempt->regradeonlyslots ?? null);
536
            if ($messages) {
547
            if ($messages) {
537
                $items = [];
548
                $items = [];
538
                foreach ($messages as $slot => $message) {
549
                foreach ($messages as $slot => $message) {
539
                    $items[] = get_string('regradingattemptissue', 'quiz_overview',
550
                    $items[] = get_string('regradingattemptissue', 'quiz_overview',
540
                            ['slot' => $slot, 'reason' => $message]);
551
                            ['slot' => $slot, 'reason' => $message]);
Línea 551... Línea 562...
551
            $this->update_overall_grades($quiz);
562
            $this->update_overall_grades($quiz);
552
        }
563
        }
553
    }
564
    }
Línea 554... Línea 565...
554
 
565
 
555
    /**
566
    /**
556
     * Count the number of attempts in need of a regrade.
567
     * Count the number of attempts and questions in need of regrading after the last dry run.
557
     *
568
     *
558
     * @param stdClass $quiz the quiz settings.
569
     * @param stdClass $quiz the quiz settings.
559
     * @param \core\dml\sql_join $groupstudentsjoins (joins, wheres, params) If this is given, only data relating
-
 
560
     * to these users is cleared.
570
     * @param \core\dml\sql_join $groupstudentsjoins which users' attempts should be considered.
561
     * @return int the number of attempts.
571
     * @return array of two elements: the number of different attempts and questions needed to be regraded.
562
     */
572
     */
-
 
573
    protected function count_attempts_and_questions_needing_regrade($quiz,
563
    protected function count_question_attempts_needing_regrade($quiz, \core\dml\sql_join $groupstudentsjoins) {
574
            \core\dml\sql_join $groupstudentsjoins): array {
Línea 564... Línea 575...
564
        global $DB;
575
        global $DB;
565
 
576
 
566
        $userjoin = '';
577
        $userjoin = '';
Línea 572... Línea 583...
572
            $usertest = "{$groupstudentsjoins->wheres} AND u.id = quiza.userid AND ";
583
            $usertest = "{$groupstudentsjoins->wheres} AND u.id = quiza.userid AND ";
573
            $params = $groupstudentsjoins->params;
584
            $params = $groupstudentsjoins->params;
574
        }
585
        }
Línea 575... Línea 586...
575
 
586
 
576
        $params['cquiz'] = $quiz->id;
587
        $params['cquiz'] = $quiz->id;
-
 
588
        $sql = "SELECT COUNT(DISTINCT quiza.id) AS attemptcount,
577
        $sql = "SELECT COUNT(DISTINCT quiza.id)
589
                       COUNT(DISTINCT qqr.slot) AS slotcount
578
                  FROM {quiz_attempts} quiza
590
                  FROM {quiz_attempts} quiza
579
                  JOIN {quiz_overview_regrades} qqr ON quiza.uniqueid = qqr.questionusageid
591
                  JOIN {quiz_overview_regrades} qqr ON quiza.uniqueid = qqr.questionusageid
580
                $userjoin
592
                $userjoin
581
                 WHERE
593
                 WHERE
582
                      $usertest
594
                      $usertest
583
                      quiza.quiz = :cquiz AND
595
                      quiza.quiz = :cquiz AND
584
                      quiza.preview = 0 AND
596
                      quiza.preview = 0 AND
585
                      qqr.regraded = 0";
597
                      qqr.regraded = 0";
-
 
598
        $counts = $DB->get_record_sql($sql, $params);
586
        return $DB->count_records_sql($sql, $params);
599
        return [$counts->attemptcount, $counts->slotcount];
Línea 587... Línea 600...
587
    }
600
    }
588
 
601
 
589
    /**
602
    /**