Proyectos de Subversion Moodle

Rev

| 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
 * Test plan generator.
19
 *
20
 * @package tool_generator
21
 * @copyright 2013 David Monllaó
22
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
/**
28
 * Generates the files required by JMeter.
29
 *
30
 * @package tool_generator
31
 * @copyright 2013 David Monllaó
32
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33
 */
34
class tool_generator_testplan_backend extends tool_generator_backend {
35
 
36
    /**
37
     * @var The URL to the repository of the external project.
38
     */
39
    protected static $repourl = 'https://github.com/moodlehq/moodle-performance-comparison';
40
 
41
    /**
42
     * @var Number of users depending on the selected size.
43
     */
44
    protected static $users = array(1, 30, 100, 1000, 5000, 10000);
45
 
46
    /**
47
     * @var Number of loops depending on the selected size.
48
     */
49
    protected static $loops = array(5, 5, 5, 6, 6, 7);
50
 
51
    /**
52
     * @var Rampup period depending on the selected size.
53
     */
54
    protected static $rampups = array(1, 6, 40, 100, 500, 800);
55
 
56
    /**
57
     * Gets a list of size choices supported by this backend.
58
     *
59
     * @return array List of size (int) => text description for display
60
     */
61
    public static function get_size_choices() {
62
 
63
        $options = array();
64
        for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
65
            $a = new stdClass();
66
            $a->users = self::$users[$size];
67
            $a->loops = self::$loops[$size];
68
            $a->rampup = self::$rampups[$size];
69
            $options[$size] = get_string('testplansize_' . $size, 'tool_generator', $a);
70
        }
71
        return $options;
72
    }
73
 
74
    /**
75
     * Getter for moodle-performance-comparison project URL.
76
     *
77
     * @return string
78
     */
79
    public static function get_repourl() {
80
        return self::$repourl;
81
    }
82
 
83
    /**
84
     * Creates the test plan file.
85
     *
86
     * @param int $courseid The target course id
87
     * @param int $size The test plan size
88
     * @return stored_file
89
     */
90
    public static function create_testplan_file($courseid, $size) {
91
        $jmxcontents = self::generate_test_plan($courseid, $size);
92
 
93
        $fs = get_file_storage();
94
        $filerecord = self::get_file_record('testplan', 'jmx');
95
        return $fs->create_file_from_string($filerecord, $jmxcontents);
96
    }
97
 
98
    /**
99
     * Creates the users data file.
100
     *
101
     * @param int $courseid The target course id
102
     * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
103
     * @param int|null $size of the test plan. Used to limit the number of users exported
104
     *                 to match the threads in the plan. For BC, defaults to null that means all enrolled users.
105
     * @return stored_file
106
     */
107
    public static function create_users_file($courseid, $updateuserspassword, ?int $size = null) {
108
        $csvcontents = self::generate_users_file($courseid, $updateuserspassword, $size);
109
 
110
        $fs = get_file_storage();
111
        $filerecord = self::get_file_record('users', 'csv');
112
        return $fs->create_file_from_string($filerecord, $csvcontents);
113
    }
114
 
115
    /**
116
     * Generates the test plan according to the target course contents.
117
     *
118
     * @param int $targetcourseid The target course id
119
     * @param int $size The test plan size
120
     * @return string The test plan as a string
121
     */
122
    protected static function generate_test_plan($targetcourseid, $size) {
123
        global $CFG;
124
 
125
        // Getting the template.
126
        $template = file_get_contents(__DIR__ . '/../testplan.template.jmx');
127
 
128
        // Getting the course modules data.
129
        $coursedata = self::get_course_test_data($targetcourseid);
130
 
131
        // Host and path to the site.
132
        $urlcomponents = parse_url($CFG->wwwroot);
133
        if (empty($urlcomponents['path'])) {
134
            $urlcomponents['path'] = '';
135
        }
136
 
137
        $replacements = array(
138
            $CFG->version,
139
            self::$users[$size],
140
            self::$loops[$size],
141
            self::$rampups[$size],
142
            $urlcomponents['host'],
143
            $urlcomponents['path'],
144
            get_string('shortsize_' . $size, 'tool_generator'),
145
            $targetcourseid,
146
            $coursedata->pageid,
147
            $coursedata->forumid,
148
            $coursedata->forumdiscussionid,
149
            $coursedata->forumreplyid
150
        );
151
 
152
        $placeholders = array(
153
            '{{MOODLEVERSION_PLACEHOLDER}}',
154
            '{{USERS_PLACEHOLDER}}',
155
            '{{LOOPS_PLACEHOLDER}}',
156
            '{{RAMPUP_PLACEHOLDER}}',
157
            '{{HOST_PLACEHOLDER}}',
158
            '{{SITEPATH_PLACEHOLDER}}',
159
            '{{SIZE_PLACEHOLDER}}',
160
            '{{COURSEID_PLACEHOLDER}}',
161
            '{{PAGEACTIVITYID_PLACEHOLDER}}',
162
            '{{FORUMACTIVITYID_PLACEHOLDER}}',
163
            '{{FORUMDISCUSSIONID_PLACEHOLDER}}',
164
            '{{FORUMREPLYID_PLACEHOLDER}}'
165
        );
166
 
167
        // Fill the template with the target course values.
168
        return str_replace($placeholders, $replacements, $template);
169
    }
170
 
171
    /**
172
     * Generates the user's credentials file with all the course's users
173
     *
174
     * @param int $targetcourseid
175
     * @param bool $updateuserspassword Updates the course users password to $CFG->tool_generator_users_password
176
     * @param int|null $size of the test plan. Used to limit the number of users exported
177
     *                 to match the threads in the plan. For BC, defaults to null that means all enrolled users.
178
     * @return string The users csv file contents.
179
     */
180
    protected static function generate_users_file($targetcourseid, $updateuserspassword, ?int $size = null) {
181
        global $CFG;
182
 
183
        $coursecontext = context_course::instance($targetcourseid);
184
 
185
        // If requested, get the number of users (threads) to use in the plan. We only need those in the exported file.
186
        $planusers = self::$users[$size] ?? 0;
187
        $users = get_enrolled_users($coursecontext, '', 0, 'u.id, u.username, u.auth', 'u.username ASC', 0, $planusers);
188
        if (!$users) {
189
            throw new \moodle_exception('coursewithoutusers', 'tool_generator');
190
        }
191
 
192
        $lines = array();
193
        foreach ($users as $user) {
194
 
195
            // Updating password to the one set in config.php.
196
            if ($updateuserspassword) {
197
                $userauth = get_auth_plugin($user->auth);
198
                if (!$userauth->user_update_password($user, $CFG->tool_generator_users_password)) {
199
                    throw new \moodle_exception('errorpasswordupdate', 'auth');
200
                }
201
            }
202
 
203
            // Here we already checked that $CFG->tool_generator_users_password is not null.
204
            $lines[] = $user->username . ',' . $CFG->tool_generator_users_password;
205
        }
206
 
207
        return implode(PHP_EOL, $lines);
208
    }
209
 
210
    /**
211
     * Returns a tool_generator file record
212
     *
213
     * @param string $filearea testplan or users
214
     * @param string $filetype The file extension jmx or csv
215
     * @return stdClass The file record to use when creating tool_generator files
216
     */
217
    protected static function get_file_record($filearea, $filetype) {
218
 
219
        $systemcontext = context_system::instance();
220
 
221
        $filerecord = new stdClass();
222
        $filerecord->contextid = $systemcontext->id;
223
        $filerecord->component = 'tool_generator';
224
        $filerecord->filearea = $filearea;
225
        $filerecord->itemid = 0;
226
        $filerecord->filepath = '/';
227
 
228
        // Random generated number to avoid concurrent execution problems.
229
        $filerecord->filename = $filearea . '_' . date('YmdHi', time()) . '_' . rand(1000, 9999) . '.' . $filetype;
230
 
231
        return $filerecord;
232
    }
233
 
234
    /**
235
     * Gets the data required to fill the test plan template with the database contents.
236
     *
237
     * @param int $targetcourseid The target course id
238
     * @return stdClass The ids required by the test plan
239
     */
240
    protected static function get_course_test_data($targetcourseid) {
241
        global $DB, $USER;
242
 
243
        $data = new stdClass();
244
 
245
        // Getting course contents info as the current user (will be an admin).
246
        $course = new stdClass();
247
        $course->id = $targetcourseid;
248
        $courseinfo = new course_modinfo($course, $USER->id);
249
 
250
        // Getting the first page module instance.
251
        if (!$pages = $courseinfo->get_instances_of('page')) {
252
            throw new \moodle_exception('error_nopageinstances', 'tool_generator');
253
        }
254
        $data->pageid = reset($pages)->id;
255
 
256
        // Getting the first forum module instance and it's first discussion and reply as well.
257
        if (!$forums = $courseinfo->get_instances_of('forum')) {
258
            throw new \moodle_exception('error_noforuminstances', 'tool_generator');
259
        }
260
        $forum = reset($forums);
261
 
262
        // Getting the first discussion (and reply).
263
        if (!$discussions = forum_get_discussions($forum, 'd.timemodified ASC', false, -1, 1)) {
264
            throw new \moodle_exception('error_noforumdiscussions', 'tool_generator');
265
        }
266
        $discussion = reset($discussions);
267
 
268
        $data->forumid = $forum->id;
269
        $data->forumdiscussionid = $discussion->discussion;
270
        $data->forumreplyid = $discussion->id;
271
 
272
        // According to the current test plan.
273
        return $data;
274
    }
275
 
276
    /**
277
     * Checks if the selected target course is ok.
278
     *
279
     * @param int|string $course
280
     * @param int $size
281
     * @return array Errors array or false if everything is ok
282
     */
283
    public static function has_selected_course_any_problem($course, $size) {
284
        global $DB;
285
 
286
        $errors = array();
287
 
288
        if (!is_numeric($course)) {
289
            if (!$course = $DB->get_field('course', 'id', array('shortname' => $course))) {
290
                $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
291
                return $errors;
292
            }
293
        }
294
 
295
        $coursecontext = context_course::instance($course, IGNORE_MISSING);
296
        if (!$coursecontext) {
297
            $errors['courseid'] = get_string('error_nonexistingcourse', 'tool_generator');
298
            return $errors;
299
        }
300
 
301
        if (!$users = get_enrolled_users($coursecontext, '', 0, 'u.id')) {
302
            $errors['courseid'] = get_string('coursewithoutusers', 'tool_generator');
303
        }
304
 
305
        // Checks that the selected course has enough users.
306
        $coursesizes = tool_generator_course_backend::get_users_per_size();
307
        if (count($users) < self::$users[$size]) {
308
            $errors['size'] = get_string('notenoughusers', 'tool_generator');
309
        }
310
 
311
        if (empty($errors)) {
312
            return false;
313
        }
314
 
315
        return $errors;
316
    }
317
}