Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
/**
18
 * Contains the class containing unit tests for the quiz notify attempt manual grading completed cron task.
19
 *
20
 * @package   mod_quiz
21
 * @copyright 2021 The Open University
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace mod_quiz;
26
 
27
use advanced_testcase;
28
use context_module;
11 efrain 29
use core\context;
1 efrain 30
use mod_quiz\task\quiz_notify_attempt_manual_grading_completed;
31
use question_engine;
11 efrain 32
use question_usage_by_activity;
1 efrain 33
use stdClass;
34
 
35
 
36
/**
37
 * Class containing unit tests for the quiz notify attempt manual grading completed cron task.
38
 *
39
 * @package mod_quiz
40
 * @copyright 2021 The Open University
41
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42
 */
1441 ariadna 43
final class quiz_notify_attempt_manual_grading_completed_test extends advanced_testcase {
11 efrain 44
    /** @var stdClass $course Test course to contain quiz. */
45
    protected stdClass $course;
1 efrain 46
 
11 efrain 47
    /** @var stdClass $quiz A test quiz. */
48
    protected stdClass $quiz;
1 efrain 49
 
50
    /** @var context The quiz context. */
11 efrain 51
    protected context $context;
1 efrain 52
 
53
    /** @var stdClass The course_module. */
11 efrain 54
    protected stdClass $cm;
1 efrain 55
 
56
    /** @var stdClass The student test. */
11 efrain 57
    protected stdClass $student;
1 efrain 58
 
11 efrain 59
    /** @var stdClass The student role. */
60
    protected stdClass $studentrole;
1 efrain 61
 
62
    /** @var quiz_settings Object containing the quiz settings. */
11 efrain 63
    protected quiz_settings $quizobj;
1 efrain 64
 
65
    /** @var question_usage_by_activity The question usage for this quiz attempt. */
11 efrain 66
    protected question_usage_by_activity $quba;
1 efrain 67
 
68
    /**
69
     * Standard test setup.
70
     *
11 efrain 71
     * Create a course with a quiz and a student.
72
     * The quiz has a truefalse question and an essay question.
1 efrain 73
     *
74
     * Also create some bits of a quiz attempt to be used later.
75
     */
76
    public function setUp(): void {
77
        global $DB;
1441 ariadna 78
        parent::setUp();
1 efrain 79
 
80
        $this->resetAfterTest();
81
        $this->setAdminUser();
82
 
83
        // Setup test data.
84
        $this->course = $this->getDataGenerator()->create_course();
85
 
11 efrain 86
        // Create a user enrolled as a student.
1 efrain 87
        $this->student = self::getDataGenerator()->create_user();
11 efrain 88
        $this->studentrole = $DB->get_record('role', ['shortname' => 'student']);
89
        $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id);
1 efrain 90
 
91
        // Make a quiz.
92
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
93
        $this->quiz = $quizgenerator->create_instance(['course' => $this->course->id, 'questionsperpage' => 0,
94
            'grade' => 100.0, 'sumgrades' => 2]);
11 efrain 95
        $this->context = context_module::instance($this->quiz->cmid);
96
        $this->cm = get_coursemodule_from_instance('quiz', $this->quiz->id);
1 efrain 97
 
98
        // Create a truefalse question and an essay question.
11 efrain 99
        /** @var \core_question_generator $questiongenerator */
1 efrain 100
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
101
        $cat = $questiongenerator->create_question_category();
102
        $truefalse = $questiongenerator->create_question('truefalse', null, ['category' => $cat->id]);
103
        $essay = $questiongenerator->create_question('essay', null, ['category' => $cat->id]);
104
 
105
        // Add them to the quiz.
106
        quiz_add_quiz_question($truefalse->id, $this->quiz);
107
        quiz_add_quiz_question($essay->id, $this->quiz);
108
 
109
        $this->quizobj = quiz_settings::create($this->quiz->id);
110
        $this->quba = question_engine::make_questions_usage_by_activity('mod_quiz', $this->quizobj->get_context());
111
        $this->quba->set_preferred_behaviour($this->quizobj->get_quiz()->preferredbehaviour);
112
    }
113
 
114
    /**
115
     * Test SQL querry get list attempt in condition.
116
     */
11 efrain 117
    public function test_get_list_of_attempts_within_conditions(): void {
1 efrain 118
        global $DB;
119
 
120
        $timenow = time();
121
 
122
        // Create an attempt to be completely graded (one hour ago).
11 efrain 123
        $attempt1 = quiz_create_attempt($this->quizobj, 1, false, $timenow - HOURSECS, false, $this->student->id);
1 efrain 124
        quiz_start_new_attempt($this->quizobj, $this->quba, $attempt1, 1, $timenow - HOURSECS);
125
        quiz_attempt_save_started($this->quizobj, $this->quba, $attempt1);
126
 
127
        // Process some responses from the student (30 mins ago) and submit (20 mins ago).
128
        $attemptobj1 = quiz_attempt::create($attempt1->id);
129
        $tosubmit = [2 => ['answer' => 'Student 1 answer', 'answerformat' => FORMAT_HTML]];
130
        $attemptobj1->process_submitted_actions($timenow - 30 * MINSECS, false, $tosubmit);
1441 ariadna 131
        $attemptobj1->process_submit($timenow - 20 * MINSECS, false);
132
        $attemptobj1->process_grade_submission($timenow - 10 * MINSECS);
1 efrain 133
 
134
        // Finish the attempt of student (now).
135
        $attemptobj1->get_question_usage()->manual_grade(2, 'Good!', 1, FORMAT_HTML);
136
        question_engine::save_questions_usage_by_activity($attemptobj1->get_question_usage());
137
 
138
        $update = new stdClass();
139
        $update->id = $attemptobj1->get_attemptid();
140
        $update->timemodified = $timenow;
141
        $update->sumgrades = $attemptobj1->get_question_usage()->get_total_mark();
11 efrain 142
        $update->gradednotificationsenttime = null; // Processfinish detects the notification is not required, so sets this.
143
        // Unset to allow testing of our logic.
1 efrain 144
        $DB->update_record('quiz_attempts', $update);
145
        $attemptobj1->get_quizobj()->get_grade_calculator()->recompute_final_grade();
146
 
147
        // Not quite time to send yet.
148
        $task = new quiz_notify_attempt_manual_grading_completed();
149
        $task->set_time_for_testing($timenow + 5 * HOURSECS - 1);
150
        $attempts = $task->get_list_of_attempts();
151
        $this->assertEquals(0, iterator_count($attempts));
152
 
153
        // After time to send.
154
        $task->set_time_for_testing($timenow + 5 * HOURSECS + 1);
155
        $attempts = $task->get_list_of_attempts();
156
        $this->assertEquals(1, iterator_count($attempts));
157
    }
158
 
159
    /**
160
     * Test SQL query does not return attempts if the grading is not complete yet.
161
     */
11 efrain 162
    public function test_get_list_of_attempts_without_manual_graded(): void {
1 efrain 163
 
164
        $timenow = time();
165
 
166
        // Create an attempt which won't be graded (1 hour ago).
11 efrain 167
        $attempt2 = quiz_create_attempt($this->quizobj, 2, false, $timenow - HOURSECS, false, $this->student->id);
1 efrain 168
        quiz_start_new_attempt($this->quizobj, $this->quba, $attempt2, 2, $timenow - HOURSECS);
169
        quiz_attempt_save_started($this->quizobj, $this->quba, $attempt2);
170
 
171
        // Process some responses from the student (30 mins ago) and submit (now).
172
        $attemptobj2 = quiz_attempt::create($attempt2->id);
173
        $tosubmit = [2 => ['answer' => 'Answer of student 2.', 'answerformat' => FORMAT_HTML]];
174
        $attemptobj2->process_submitted_actions($timenow - 30 * MINSECS, false, $tosubmit);
1441 ariadna 175
        $attemptobj2->process_submit($timenow, false);
176
        $attemptobj2->process_grade_submission($timenow);
1 efrain 177
 
178
        // After time to notify, except attempt not graded, so it won't appear.
179
        $task = new quiz_notify_attempt_manual_grading_completed();
180
        $task->set_time_for_testing($timenow + 5 * HOURSECS + 1);
181
        $attempts = $task->get_list_of_attempts();
182
 
183
        $this->assertEquals(0, iterator_count($attempts));
184
    }
185
 
186
    /**
187
     * Test notify manual grading completed task which the user attempt has not capability.
188
     */
11 efrain 189
    public function test_notify_manual_grading_completed_task_without_capability(): void {
1 efrain 190
        global $DB;
191
 
192
        // Create an attempt for a user without the capability.
193
        $timenow = time();
1441 ariadna 194
        $timestart = $timenow - HOURSECS;
195
        $timefinish = $timenow - 20 * MINSECS;
196
        $gradetime = $timenow - 10 * MINSECS;
11 efrain 197
        $attempt = quiz_create_attempt($this->quizobj, 3, false, $timenow, false, $this->student->id);
1441 ariadna 198
        quiz_start_new_attempt($this->quizobj, $this->quba, $attempt, 3, $timestart);
199
        quiz_attempt_save_started($this->quizobj, $this->quba, $attempt, $timestart);
1 efrain 200
 
201
        // Process some responses and submit.
202
        $attemptobj = quiz_attempt::create($attempt->id);
11 efrain 203
        $tosubmit = [2 => ['answer' => 'Essay answer.', 'answerformat' => FORMAT_HTML]];
1 efrain 204
        $attemptobj->process_submitted_actions($timenow - 30 * MINSECS, false, $tosubmit);
1441 ariadna 205
        $attemptobj->process_submit($timefinish, false);
206
        $attemptobj->process_grade_submission($gradetime);
1 efrain 207
 
208
        // Grade the attempt.
209
        $attemptobj->get_question_usage()->manual_grade(2, 'Good!', 1, FORMAT_HTML);
210
        question_engine::save_questions_usage_by_activity($attemptobj->get_question_usage());
211
 
212
        $update = new stdClass();
213
        $update->id = $attemptobj->get_attemptid();
214
        $update->timemodified = $timenow;
215
        $update->sumgrades = $attemptobj->get_question_usage()->get_total_mark();
11 efrain 216
        $update->gradednotificationsenttime = null; // Processfinish detects the notification is not required, so sets this.
217
        // Unset to allow testing of our logic.
1 efrain 218
        $DB->update_record('quiz_attempts', $update);
219
        $attemptobj->get_quizobj()->get_grade_calculator()->recompute_final_grade();
220
 
221
        // Run the quiz notify attempt manual graded task.
11 efrain 222
        $this->expectOutputRegex("~Not sending an email because user does not have mod/quiz:emailnotifyattemptgraded capability.~");
1 efrain 223
        $task = new quiz_notify_attempt_manual_grading_completed();
224
        $task->set_time_for_testing($timenow + 5 * HOURSECS + 1);
225
        $task->execute();
226
 
227
        $attemptobj = quiz_attempt::create($attempt->id);
1441 ariadna 228
        $this->assertEquals($timefinish, $attemptobj->get_attempt()->gradednotificationsenttime);
1 efrain 229
    }
230
 
231
    /**
232
     * Test notify manual grading completed task which the user attempt has capability.
233
     */
11 efrain 234
    public function test_notify_manual_grading_completed_task_with_capability(): void {
1 efrain 235
        global $DB;
236
 
11 efrain 237
        // Allow student to receive messages.
238
        assign_capability('mod/quiz:emailnotifyattemptgraded', CAP_ALLOW, $this->studentrole->id, $this->context, true);
239
 
1 efrain 240
        // Create an attempt with capability.
241
        $timenow = time();
11 efrain 242
        $attempt = quiz_create_attempt($this->quizobj, 4, false, $timenow, false, $this->student->id);
1 efrain 243
        quiz_start_new_attempt($this->quizobj, $this->quba, $attempt, 4, $timenow - HOURSECS);
244
        quiz_attempt_save_started($this->quizobj, $this->quba, $attempt);
245
 
246
        // Process some responses from the student.
247
        $attemptobj = quiz_attempt::create($attempt->id);
248
        $tosubmit = [2 => ['answer' => 'Answer of student.', 'answerformat' => FORMAT_HTML]];
249
        $attemptobj->process_submitted_actions($timenow - 30 * MINSECS, false, $tosubmit);
1441 ariadna 250
        $attemptobj->process_submit($timenow - 20 * MINSECS, false);
251
        $attemptobj->process_grade_submission($timenow - 10 * MINSECS);
1 efrain 252
 
253
        // Finish the attempt of student.
254
        $attemptobj->get_question_usage()->manual_grade(2, 'Good!', 1, FORMAT_HTML);
255
        question_engine::save_questions_usage_by_activity($attemptobj->get_question_usage());
256
 
257
        $update = new stdClass();
258
        $update->id = $attemptobj->get_attemptid();
259
        $update->timemodified = $timenow;
260
        $update->sumgrades = $attemptobj->get_question_usage()->get_total_mark();
261
        $DB->update_record('quiz_attempts', $update);
262
        $attemptobj->get_quizobj()->get_grade_calculator()->recompute_final_grade();
263
 
264
        // Run the quiz notify attempt manual graded task.
11 efrain 265
        $this->expectOutputRegex("~Sending email to user {$this->student->id}~");
1 efrain 266
        $task = new quiz_notify_attempt_manual_grading_completed();
267
        $task->set_time_for_testing($timenow + 5 * HOURSECS + 1);
268
        $task->execute();
269
 
270
        $attemptobj = quiz_attempt::create($attempt->id);
271
 
272
        $this->assertNotEquals(null, $attemptobj->get_attempt()->gradednotificationsenttime);
273
        $this->assertNotEquals($attemptobj->get_attempt()->timefinish, $attemptobj->get_attempt()->gradednotificationsenttime);
274
    }
275
}