generator.php 56.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
<?php  // $Id$
/**
 * Random course generator. By Nicolas Connault and friends.
 *
 * To use go to .../admin/generator.php?web_interface=1 in your browser.
 *
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package generator
 *//** */

nicolasconnault's avatar
nicolasconnault committed
11
12
13
14
require_once(dirname(__FILE__).'/../config.php');
require_once($CFG->libdir . '/formslib.php');
require_once($CFG->dirroot .'/course/lib.php');
require_once($CFG->dirroot .'/mod/resource/lib.php');
nicolasconnault's avatar
nicolasconnault committed
15
16
17
18

define('GENERATOR_RANDOM', 0);
define('GENERATOR_SEQUENCE', 1);

19
/**
nicolasconnault's avatar
nicolasconnault committed
20
 * Controller class for data generation
21
 */
nicolasconnault's avatar
nicolasconnault committed
22
23
24
class generator {
    public $modules_to_ignore = array('hotpot', 'lams', 'journal', 'scorm', 'exercise', 'dialogue');
    public $modules_list = array('forum' => 'forum',
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
                                 'assignment' => 'assignment',
                                 'chat' => 'chat',
                                 'data' => 'data',
                                 'glossary' => 'glossary',
                                 'quiz' => 'quiz',
                                 'comments' => 'comments',
                                 'feedback' => 'feedback',
                                 'label' => 'label',
                                 'lesson' => 'lesson',
                                 'chat' => 'chat',
                                 'choice' => 'choice',
                                 'resource' => 'resource',
                                 'survey' => 'survey',
                                 'wiki' => 'wiki',
                                 'workshop' => 'workshop');
40

nicolasconnault's avatar
nicolasconnault committed
41
42
43
44
45
46
47
48
49
    public $resource_types = array('text', 'file', 'html', 'repository', 'directory', 'ims');
    public $glossary_formats = array('continuous', 'encyclopedia', 'entrylist', 'faq', 'fullwithauthor', 'fullwithoutauthor', 'dictionary');
    public $assignment_types = array('upload', 'uploadsingle', 'online', 'offline');
    public $forum_types = array('single', 'eachuser', 'qanda', 'general');

    public $resource_type_counter = 0;
    public $assignment_type_counter = 0;
    public $forum_type_counter = 0;

nicolasconnault's avatar
nicolasconnault committed
50
51
    public $settings = array();
    public $eolchar = '<br />';
52
    public $do_generation = false;
nicolasconnault's avatar
nicolasconnault committed
53
    public $starttime;
54
    public $original_db;
nicolasconnault's avatar
nicolasconnault committed
55
56

    public function __construct($settings = array(), $generate=false) {
nicolasconnault's avatar
nicolasconnault committed
57
        global $CFG;
nicolasconnault's avatar
nicolasconnault committed
58

59
        $this->starttime = time()+microtime();
nicolasconnault's avatar
nicolasconnault committed
60
61
62
63
64
65
66
67

        $arguments = array(
             array('short'=>'u', 'long'=>'username',
                   'help' => 'Your moodle username', 'type'=>'STRING', 'default' => ''),
             array('short'=>'pw', 'long'=>'password',
                   'help' => 'Your moodle password', 'type'=>'STRING', 'default' => ''),
             array('short'=>'P', 'long' => 'database_prefix',
                   'help' => 'Database prefix to use: tables must already exist or the script will abort!',
68
                   'type'=>'STRING', 'default' => 'tst_'),
nicolasconnault's avatar
nicolasconnault committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
             array('short'=>'c', 'long' => 'pre_cleanup', 'help' => 'Delete previously generated data'),
             array('short'=>'C', 'long' => 'post_cleanup',
                   'help' => 'Deletes all generated data at the end of the script (for benchmarking of generation only)'),
             array('short'=>'t', 'long' => 'time_limit',
                   'help' => 'Optional time limit after which to abort the generation, 0 = no limit. Default=0',
                   'type'=>'SECONDS', 'default' => 0),
             array('short'=>'v', 'long' => 'verbose', 'help' => 'Display extra information about the data generation'),
             array('short'=>'q', 'long' => 'quiet', 'help' => 'Inhibits all outputs'),
             array('short'=>'i', 'long' => 'ignore_errors', 'help' => 'Continue script execution when errors occur'),
             array('short'=>'N', 'long' => 'no_data', 'help' => 'Generate nothing (used for cleaning up only)'),
             array('short'=>'T', 'long' => 'tiny',
                   'help' => 'Generates a tiny data set (1 of each course, module, user and section)',
                   'default' => 0),
             array('short'=>'nc', 'long' => 'number_of_courses',
                   'help' => 'The number of courses to generate. Default=1',
                   'type'=>'NUMBER', 'default' => 1),
             array('short'=>'ns', 'long' => 'number_of_students',
                   'help' => 'The number of students to generate. Default=250',
                   'type'=>'NUMBER', 'default' => 250),
             array('short'=>'sc', 'long' => 'students_per_course',
                   'help' => 'The number of students to enrol in each course. Default=20',
                   'type'=>'NUMBER', 'default' => 20),
             array('short'=>'nsec', 'long' => 'number_of_sections',
                   'help' => 'The number of sections to generate in each course. Default=10',
                   'type'=>'NUMBER', 'default' => 10),
             array('short'=>'nmod', 'long' => 'number_of_modules',
                   'help' => 'The number of modules to generate in each section. Default=10',
                   'type'=>'NUMBER', 'default' => 10),
             array('short'=>'mods', 'long' => 'modules_list',
                   'help' => 'The list of modules you want to generate', 'default' => $this->modules_list,
                   'type' => 'mod1,mod2...'),
100
             array('short'=>'rt', 'long' => 'resource_type',
nicolasconnault's avatar
nicolasconnault committed
101
102
103
                   'help' => 'The specific type of resource you want to generate. Defaults to all',
                   'default' => $this->resource_types,
                   'type' => 'SELECT'),
104
             array('short'=>'at', 'long' => 'assignment_type',
nicolasconnault's avatar
nicolasconnault committed
105
106
107
108
109
110
111
112
113
114
115
                   'help' => 'The specific type of assignment you want to generate. Defaults to all',
                   'default' => $this->assignment_types,
                   'type' => 'SELECT'),
             array('short'=>'ft', 'long' => 'forum_type',
                   'help' => 'The specific type of forum you want to generate. Defaults to all',
                   'default' => $this->forum_types,
                   'type' => 'SELECT'),
             array('short'=>'gf', 'long' => 'glossary_format',
                   'help' => 'The specific format of glossary you want to generate. Defaults to all',
                   'default' => $this->glossary_formats,
                   'type' => 'SELECT'),
nicolasconnault's avatar
nicolasconnault committed
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
             array('short'=>'ag', 'long' => 'assignment_grades',
                   'help' => 'Generate random grades for each student/assignment tuple', 'default' => true),
             array('short'=>'qg', 'long' => 'quiz_grades',
                   'help' => 'Generate random grades for each student/quiz tuple', 'default' => true),
             array('short'=>'eg', 'long' => 'entries_per_glossary',
                   'help' => 'The number of definitions to generate per glossary. Default=0',
                   'type'=>'NUMBER', 'default' => 1),
             array('short'=>'nq', 'long' => 'questions_per_course',
                   'help' => 'The number of questions to generate per course. Default=20',
                   'type'=>'NUMBER', 'default' => 20),
             array('short'=>'qq', 'long' => 'questions_per_quiz',
                   'help' => 'The number of questions to assign to each quiz. Default=5',
                   'type'=>'NUMBER', 'default' => 5),
             array('short'=>'df', 'long' => 'discussions_per_forum',
                   'help' => 'The number of discussions to generate for each forum. Default=5',
                   'type'=>'NUMBER', 'default' => 5),
             array('short'=>'pd', 'long' => 'posts_per_discussion',
                   'help' => 'The number of posts to generate for each forum discussion. Default=15',
                   'type'=>'NUMBER', 'default' => 15),
135
136
137
138
139
140
             array('short'=>'fd', 'long' => 'fields_per_database',
                   'help' => 'The number of fields to generate for each database. Default=4',
                   'type'=>'NUMBER', 'default' => 4),
             array('short'=>'drs', 'long' => 'database_records_per_student',
                   'help' => 'The number of records to generate for each student/database tuple. Default=1',
                   'type'=>'NUMBER', 'default' => 1),
141
142
143
             array('short'=>'mc', 'long' => 'messages_per_chat',
                   'help' => 'The number of messages to generate for each chat module. Default=10',
                   'type'=>'NUMBER', 'default' => 10),
nicolasconnault's avatar
nicolasconnault committed
144
            );
145

nicolasconnault's avatar
nicolasconnault committed
146
147
        foreach ($arguments as $args_array) {
            $this->settings[$args_array['long']] = new generator_argument($args_array);
148
        }
149

nicolasconnault's avatar
nicolasconnault committed
150
151
        foreach ($settings as $setting => $value) {
            $this->settings[$setting]->value = $value;
152
153
        }

nicolasconnault's avatar
nicolasconnault committed
154
155
        if ($generate) {
            $this->generate_data();
156
        }
nicolasconnault's avatar
nicolasconnault committed
157
    }
158

nicolasconnault's avatar
nicolasconnault committed
159
160
    public function connect() {
        global $DB, $CFG;
161
162
163
164
        $this->original_db = clone($DB);

        $class = get_class($DB);
        $DB = new $class();
165
        $DB->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $this->get('database_prefix'));
166
167
    }

nicolasconnault's avatar
nicolasconnault committed
168
169
    public function generate_users() {
        global $DB, $CFG;
170
171
172
173

        /**
         * USER GENERATION
         */
nicolasconnault's avatar
nicolasconnault committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
        $this->verbose("Generating ".$this->get('number_of_students')." students...");
        $lastnames = array('SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON',
            'MOORE','TAYLOR','ANDERSON','THOMAS','JACKSON','WHITE','HARRIS','MARTIN','THOMPSON',
            'GARCIA','MARTINEZ','ROBINSON','CLARK','RODRIGUEZ','LEWIS','LEE','WALKER','HALL',
            'ALLEN','YOUNG','HERNANDEZ','KING','WRIGHT','LOPEZ','HILL','SCOTT','GREEN','ADAMS',
            'BAKER','GONZALEZ','NELSON','CARTER','MITCHELL','PEREZ','ROBERTS','TURNER','PHILLIPS',
            'CAMPBELL','PARKER','EVANS','EDWARDS','COLLINS','STEWART','SANCHEZ','MORRIS','ROGERS',
            'REED','COOK','MORGAN','BELL','MURPHY','BAILEY','RIVERA','COOPER','RICHARDSON','COX',
            'HOWARD','WARD','TORRES','PETERSON','GRAY','RAMIREZ','JAMES','WATSON','BROOKS','KELLY',
            'SANDERS','PRICE','BENNETT','WOOD','BARNES','ROSS','HENDERSON','COLEMAN','JENKINS','PERRY',
            'POWELL','LONG','PATTERSON','HUGHES','FLORES','WASHINGTON','BUTLER','SIMMONS','FOSTER',
            'GONZALES','BRYANT','ALEXANDER','RUSSELL','GRIFFIN','DIAZ','HAYES','MYERS','FORD','HAMILTON',
            'GRAHAM','SULLIVAN','WALLACE','WOODS','COLE','WEST','JORDAN','OWENS','REYNOLDS','FISHER',
            'ELLIS','HARRISON','GIBSON','MCDONALD','CRUZ','MARSHALL','ORTIZ','GOMEZ','MURRAY','FREEMAN',
            'WELLS','WEBB','SIMPSON','STEVENS','TUCKER','PORTER','HUNTER','HICKS','CRAWFORD','HENRY',
            'BOYD','MASON','MORALES','KENNEDY','WARREN','DIXON','RAMOS','REYES','BURNS','GORDON','SHAW',
            'HOLMES','RICE','ROBERTSON','HUNT','BLACK','DANIELS','PALMER','MILLS','NICHOLS','GRANT',
            'KNIGHT','FERGUSON','ROSE','STONE','HAWKINS','DUNN','PERKINS','HUDSON','SPENCER','GARDNER',
            'STEPHENS','PAYNE','PIERCE','BERRY','MATTHEWS','ARNOLD','WAGNER','WILLIS','RAY','WATKINS',
            'OLSON','CARROLL','DUNCAN','SNYDER','HART','CUNNINGHAM','BRADLEY','LANE','ANDREWS','RUIZ',
            'HARPER','FOX','RILEY','ARMSTRONG','CARPENTER','WEAVER','GREENE','LAWRENCE','ELLIOTT','CHAVEZ',
            'SIMS','AUSTIN','PETERS','KELLEY','FRANKLIN','LAWSON','FIELDS','GUTIERREZ','RYAN','SCHMIDT',
            'CARR','VASQUEZ','CASTILLO','WHEELER','CHAPMAN','OLIVER','MONTGOMERY','RICHARDS','WILLIAMSON',
            'JOHNSTON','BANKS','MEYER','BISHOP','MCCOY','HOWELL','ALVAREZ','MORRISON','HANSEN','FERNANDEZ',
            'GARZA','HARVEY','LITTLE','BURTON','STANLEY','NGUYEN','GEORGE','JACOBS','REID','KIM','FULLER',
            'LYNCH','DEAN','GILBERT','GARRETT','ROMERO','WELCH','LARSON','FRAZIER','BURKE','HANSON','DAY',
            'MENDOZA','MORENO','BOWMAN','MEDINA','FOWLER');
        $firstnames = array( 'JAMES','JOHN','ROBERT','MARY','MICHAEL','WILLIAM','DAVID','RICHARD',
            'CHARLES','JOSEPH','THOMAS','PATRICIA','LINDA','CHRISTOPHER','BARBARA','DANIEL','PAUL',
            'MARK','ELIZABETH','JENNIFER','DONALD','GEORGE','MARIA','KENNETH','SUSAN','STEVEN','EDWARD',
            'MARGARET','BRIAN','DOROTHY','RONALD','ANTHONY','LISA','KEVIN','NANCY','KAREN','BETTY',
            'HELEN','JASON','MATTHEW','GARY','TIMOTHY','SANDRA','JOSE','LARRY','JEFFREY','DONNA',
            'FRANK','CAROL','RUTH','SCOTT','ERIC','STEPHEN','ANDREW','SHARON','MICHELLE','LAURA',
            'SARAH','KIMBERLY','DEBORAH','JESSICA','RAYMOND','SHIRLEY','CYNTHIA','ANGELA','MELISSA',
            'BRENDA','AMY','GREGORY','ANNA','JOSHUA','JERRY','REBECCA','VIRGINIA','KATHLEEN','PAMELA',
            'DENNIS','MARTHA','DEBRA','AMANDA','STEPHANIE','WALTER','PATRICK','CAROLYN','CHRISTINE',
            'PETER','MARIE','JANET','CATHERINE','HAROLD','FRANCES','DOUGLAS','HENRY','ANN','JOYCE',
            'DIANE','ALICE','JULIE','CARL','HEATHER');
212
213
214
215
216
217
218
        $users_count = 0;
        $users = array();

        shuffle($lastnames);
        shuffle($firstnames);

        $next_user_id = $DB->get_field_sql("SELECT MAX(id) FROM {user}") + 1;
nicolasconnault's avatar
nicolasconnault committed
219
220

        for ($i = 0; $i < $this->get('number_of_students'); $i++) {
221
222
223
224
225
226

            $lastname = trim(ucfirst(strtolower($lastnames[rand(0, count($lastnames) - 1)])));
            $firstname = $firstnames[rand(0, count($firstnames) - 1)];

            $user = new stdClass();
            $user->firstname = trim(ucfirst(strtolower($firstname)));
227
            $user->username = strtolower(substr($firstname, 0, 7) . substr($lastname, 0, 7)) . $next_user_id++;
228
229
230
231
232
233
234
235
236
237
238
            $user->lastname = $lastname;
            $user->email = $user->username . '@example.com';
            $user->mnethostid = 1;
            $user->city = 'Test City';
            $user->country = 'AU';
            $user->password = md5('password');
            $user->auth        = 'manual';
            $user->confirmed   = 1;
            $user->lang        = $CFG->lang;
            $user->timemodified= time();

239
240
241
242
243
244
            $user->id = $DB->insert_record("user", $user);
            $users_count++;
            $users[] = $user->id;
            $next_user_id = $user->id + 1;
            $this->verbose("Inserted $user->firstname $user->lastname into DB "
                ."(username=$user->username, password=password).");
245
246
        }

nicolasconnault's avatar
nicolasconnault committed
247
248
        if (!$this->get('quiet')) {
            echo "$users_count users correctly inserted in the database.{$this->eolchar}";
249
        }
nicolasconnault's avatar
nicolasconnault committed
250
251
        return $users;
    }
252

253
    public function generate_data() {
254
255
256
257
        if (!$this->do_generation) {
            return false;
        }

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
        set_time_limit($this->get('time_limit'));

        // Process tiny data set
        $tiny = $this->get('tiny');
        if (!empty($tiny)) {
            $this->verbose("Generating a tiny data set: 1 student in 1 course with 1 module in 1 section...");
            $this->set('number_of_courses',1);
            $this->set('number_of_students',1);
            $this->set('number_of_modules',1);
            $this->set('number_of_sections',1);
            $this->set('assignment_grades',false);
            $this->set('quiz_grades',false);
            $this->set('students_per_course',1);
            $this->set('questions_per_course',1);
            $this->set('questions_per_quiz',1);
        }

        if ($this->get('pre_cleanup')) {
            $this->verbose("Deleting previous test data...");
            $this->data_cleanup();

            if (!$this->get('quiet')) {
                echo "Previous test data has been deleted.{$this->eolchar}";
            }
        }


        if (!$this->get('no_data')) {
            $users = $this->generate_users();
            $courses = $this->generate_courses();
            $modules = $this->generate_modules($courses);
            $questions = $this->generate_questions($courses, $modules);
            $course_users = $this->generate_role_assignments($users, $courses);
            $this->generate_forum_posts($course_users, $modules);
            $this->generate_grades($course_users, $courses, $modules);
            $this->generate_module_content($course_users, $courses, $modules);
        }

        if ($this->get('post_cleanup')) {
            if (!$this->get('quiet')) {
                echo "Removing generated data..." . $this->eolchar;
            }
            $this->data_cleanup();
            if (!$this->get('quiet')) {
                echo "Generated data has been deleted." . $this->eolchar;
            }
        }

        /**
         * FINISHING SCRIPT
         */
        $stoptimer = time()+microtime();
310
        $timer = round($stoptimer-$this->starttime,4);
311
312
313
314
315
316
        if (!$this->get('quiet')) {
            echo "End of script! ($timer seconds taken){$this->eolchar}";
        }

    }

nicolasconnault's avatar
nicolasconnault committed
317
318
319
    public function generate_courses() {
        global $DB;

320
        $this->verbose("Generating " . $this->get('number_of_courses')." courses...");
321
322
323
324
325
326
327
328
329
330
331
332
333
        $base_course = new stdClass();
        $next_course_id = $DB->get_field_sql("SELECT MAX(id) FROM {course}") + 1;

        $base_course->MAX_FILE_SIZE = '2097152';
        $base_course->category = '1';
        $base_course->summary = 'Blah Blah';
        $base_course->format = 'weeks';
        $base_course->numsections = '10';
        $base_course->startdate = mktime();
        $base_course->id = '0';

        $courses_count = 0;
        $courses = array();
nicolasconnault's avatar
nicolasconnault committed
334
        for ($i = 1; $i <= $this->get('number_of_courses'); $i++) {
335
336
337
            $newcourse = fullclone($base_course);
            $newcourse->fullname = "Test course $next_course_id";
            $newcourse->shortname = "Test $next_course_id";
338
            $newcourse->idnumber = $next_course_id;
339
            if (!$course = create_course($newcourse)) {
nicolasconnault's avatar
nicolasconnault committed
340
341
                $this->verbose("Error inserting a new course in the database!");
                if (!$this->get('ignore_errors')) {
342
343
344
345
346
347
348
                    die();
                }
            } else {
                $courses_count++;
                $next_course_id++;
                $courses[] = $course->id;
                $next_course_id = $course->id + 1;
nicolasconnault's avatar
nicolasconnault committed
349
                $this->verbose("Inserted $course->fullname into DB (idnumber=$course->idnumber).");
350
351
352
            }
        }

nicolasconnault's avatar
nicolasconnault committed
353
354
        if (!$this->get('quiet')) {
            echo "$courses_count test courses correctly inserted into the database.{$this->eolchar}";
355
        }
nicolasconnault's avatar
nicolasconnault committed
356
357
        return $courses;
    }
358

nicolasconnault's avatar
nicolasconnault committed
359
360
    public function generate_modules($courses) {
        global $DB, $CFG;
361
362
        // Parse the modules-list variable

nicolasconnault's avatar
nicolasconnault committed
363
364
        $this->verbose("Generating " . $this->get('number_of_sections')." sections with "
            .$this->get('number_of_modules')." modules in each section, for each course...");
365
366
367
368
369
370
371
372

        list($modules_list_sql, $modules_params) =
            $DB->get_in_or_equal($this->get('modules_list'), SQL_PARAMS_NAMED, 'param0000', true);

        list($modules_ignored_sql, $ignore_params) =
            $DB->get_in_or_equal($this->modules_to_ignore, SQL_PARAMS_NAMED, 'param2000', false);

        $wheresql = "name $modules_list_sql AND name $modules_ignored_sql";
373
        $modules = $DB->get_records_select('modules', $wheresql, array_merge($modules_params, $ignore_params));
374
375
376
377
378

        foreach ($modules as $key => $module) {
            $module->count = 0;

            // Scorm, lams and hotpot are too complex to set up, remove them
nicolasconnault's avatar
nicolasconnault committed
379
380
            if (in_array($module->name, $this->modules_to_ignore) ||
                !in_array($module->name, $this->modules_list)) {
381
382
383
384
385
386
387
388
389
                unset($modules[$key]);
            }
        }

        // Dirty hack for renumbering the modules array's keys
        $first_module = reset($modules);
        array_shift($modules);
        array_unshift($modules, $first_module);

nicolasconnault's avatar
nicolasconnault committed
390
        $modules_array = array();
391
392
393
394
395
396

        if (count($courses) > 0) {
            $libraries = array();
            foreach ($courses as $courseid) {

                // Text resources
nicolasconnault's avatar
nicolasconnault committed
397
398
                for ($i = 1; $i <= $this->get('number_of_sections'); $i++) {
                    for ($j = 0; $j < $this->get('number_of_modules'); $j++) {
399
400
401
402

                        $module = new stdClass();

                        // If only one module is created, and we also need to add a question to a quiz, create only a quiz
nicolasconnault's avatar
nicolasconnault committed
403
404
405
                        if ($this->get('number_of_modules') == 1
                                    && $this->get('questions_per_quiz') > 0
                                    && !empty($modules[8])) {
406
407
408
409
410
411
412
413
                            $moduledata = $modules[8];
                        } else {
                            $moduledata = $modules[array_rand($modules)];
                        }

                        $libfile = "$CFG->dirroot/mod/$moduledata->name/lib.php";
                        if (file_exists($libfile)) {
                            if (!in_array($libfile, $libraries)) {
nicolasconnault's avatar
nicolasconnault committed
414
                                $this->verbose("Including library for $moduledata->name...");
415
416
417
418
                                $libraries[] = $libfile;
                                require_once($libfile);
                            }
                        } else {
nicolasconnault's avatar
nicolasconnault committed
419
420
                            $this->verbose("Could not load lib file for module $moduledata->name!");
                            if (!$this->get('ignore_errors')) {
421
422
423
424
425
                                die();
                            }
                        }

                        // Basically 2 types of text fields: description and content
nicolasconnault's avatar
nicolasconnault committed
426
427
428
429
                        $description = "This $moduledata->name has been randomly generated by a very useful script, "
                                     . "for the purpose of testing "
                                     . "the boundaries of Moodle in various contexts. Moodle should be able to scale to "
                                     . "any size without "
430
431
432
                                     . "its speed and ease of use being affected dramatically.";
                        $content = 'Very useful content, I am sure you would agree';

nicolasconnault's avatar
nicolasconnault committed
433
                        $module_type_index = 0;
434
435
                        $module->introformat = FORMAT_MOODLE;
                        $module->messageformat = FORMAT_MOODLE;
nicolasconnault's avatar
nicolasconnault committed
436

437
438
439
                        // Special module-specific config
                        switch ($moduledata->name) {
                            case 'assignment':
440
                                $module->intro = $description;
nicolasconnault's avatar
nicolasconnault committed
441
                                $module->assignmenttype = $this->get_module_type('assignment');
442
443
444
445
446
447
448
449
                                $module->timedue = mktime() + 89487321;
                                $module->grade = rand(50,100);
                                break;
                            case 'chat':
                                $module->intro = $description;
                                $module->schedule = 1;
                                $module->chattime = 60 * 60 * 4;
                                break;
450
451
452
453
                            case 'data':
                                $module->intro = $description;
                                $module->name = 'test';
                                break;
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
                            case 'choice':
                                $module->text = $content;
                                $module->option = array('Good choice', 'Bad choice', 'No choice');
                                $module->limit  = array(1, 5, 0);
                                break;
                            case 'comments':
                                $module->intro = $description;
                                $module->comments = $content;
                                break;
                            case 'feedback':
                                $module->intro = $description;
                                $module->comments = $content;
                                break;
                            case 'forum':
                                $module->intro = $description;
nicolasconnault's avatar
nicolasconnault committed
469
                                $module->type = $this->get_module_type('forum');
470
                                $module->forcesubscribe = rand(0, 1);
471
                                $module->format = 1;
472
473
474
                                break;
                            case 'glossary':
                                $module->intro = $description;
nicolasconnault's avatar
nicolasconnault committed
475
                                $module->displayformat = $this->glossary_formats[rand(0, count($this->glossary_formats) - 1)];
476
477
478
479
                                $module->cmidnumber = rand(0,999999);
                                break;
                            case 'label':
                                $module->content = $content;
480
                                $module->intro = $description;
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
                                break;
                            case 'lesson':
                                $module->lessondefault = 1;
                                $module->available = mktime();
                                $module->deadline = mktime() + 719891987;
                                $module->grade = 100;
                                break;
                            case 'quiz':
                                $module->intro = $description;
                                $module->feedbacktext = 'blah';
                                $module->feedback = 1;
                                $module->feedbackboundaries = array(2, 1);
                                $module->grade = 10;
                                $module->timeopen = time();
                                $module->timeclose = time() + 68854;
                                $module->shufflequestions = true;
                                $module->shuffleanswers = true;
                                $module->quizpassword = '';
                                break;
                            case 'resource':
nicolasconnault's avatar
nicolasconnault committed
501
                                $module->type = $this->get_module_type('resource');
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
                                $module->alltext = $content;
                                $module->summary = $description;
                                $module->windowpopup = rand(0,1);
                                $module->resizable = rand(0,1);
                                $module->scrollbars = rand(0,1);
                                $module->directories = rand(0,1);
                                $module->location = 'file.txt';
                                $module->menubar = rand(0,1);
                                $module->toolbar = rand(0,1);
                                $module->status = rand(0,1);
                                $module->width = rand(200,600);
                                $module->height = rand(200,600);
                                $module->directories = rand(0,1);
                                $module->param_navigationmenu = rand(0,1);
                                $module->param_navigationbuttons = rand(0,1);
                                $module->reference = 1;
                                $module->forcedownload = 1;
                                break;
                            case 'survey':
                                $module->template = rand(1,5);
                                $module->intro = $description;
                                break;
                            case 'wiki':
                                $module->summary = $description;
                                break;
                        }

529
                        $module->name = ucfirst($moduledata->name) . ' ' . $moduledata->count++;
530
531
532
533
534
535
536

                        $module->course = $courseid;
                        $module->section = $i;
                        $module->module = $moduledata->id;
                        $module->modulename = $moduledata->name;
                        $module->add = $moduledata->name;
                        $module->cmidnumber = '';
537
                        $module->coursemodule = '';
538
539
                        $add_instance_function = $moduledata->name . '_add_instance';

540
541
542
543
                        $section = get_course_section($i, $courseid);
                        $module->section = $section->id;
                        $module->coursemodule = add_course_module($module);
                        $module->section = $i;
544

545
                        if (function_exists($add_instance_function)) {
546
                            $this->verbose("Calling module function $add_instance_function");
547
548
                            $module->instance = $add_instance_function($module, '');
                        } else {
nicolasconnault's avatar
nicolasconnault committed
549
550
                            $this->verbose("Function $add_instance_function does not exist!");
                            if (!$this->get('ignore_errors')) {
551
552
553
554
555
556
557
558
                                die();
                            }
                        }

                        add_mod_to_section($module);

                        $module->cmidnumber = set_coursemodule_idnumber($module->coursemodule, '');

nicolasconnault's avatar
nicolasconnault committed
559
560
                        $this->verbose("A $moduledata->name module was added to section $i (id $module->section) "
                            ."of course $courseid.");
561
562
                        rebuild_course_cache($courseid);

nicolasconnault's avatar
nicolasconnault committed
563
564
565
566
567
568
                        $module_instance = $DB->get_field('course_modules', 'instance', array('id' => $module->coursemodule));
                        $module_record = $DB->get_record($moduledata->name, array('id' => $module_instance));
                        $module_record->instance = $module_instance;

                        if (empty($modules_array[$moduledata->name])) {
                            $modules_array[$moduledata->name] = array();
569
                        }
570

571
572
573
574
                        // TODO Find out why some $module_record end up empty here... (particularly quizzes)
                        if (!empty($module_record->instance)) {
                            $modules_array[$moduledata->name][] = $module_record;
                        }
575
576
577
578
                    }
                }
            }

nicolasconnault's avatar
nicolasconnault committed
579
580
581
            if (!$this->get('quiet')) {
                echo "Successfully generated " . $this->get('number_of_modules') * $this->get('number_of_sections')
                    . " modules in each course!{$this->eolchar}";
582
            }
nicolasconnault's avatar
nicolasconnault committed
583
584

            return $modules_array;
585
        }
nicolasconnault's avatar
nicolasconnault committed
586
587
        return null;
    }
588

nicolasconnault's avatar
nicolasconnault committed
589
590
591
592
    public function generate_questions($courses, $modules) {
        global $DB, $CFG;

        if (!is_null($this->get('questions_per_course')) && count($courses) > 0 && is_array($courses)) {
593
594
595
596
597
598
599
600
601
602
603
604
            require_once($CFG->libdir .'/questionlib.php');
            require_once($CFG->dirroot .'/mod/quiz/editlib.php');
            $questions = array();
            $questionsmenu = question_type_menu();
            $questiontypes = array();
            foreach ($questionsmenu as $qtype => $qname) {
                $questiontypes[] = $qtype;
            }

            // Add the questions
            foreach ($courses as $courseid) {
                $questions[$courseid] = array();
nicolasconnault's avatar
nicolasconnault committed
605
                for ($i = 0; $i < $this->get('questions_per_course'); $i++) {
606
607
608
                    $qtype = $questiontypes[array_rand($questiontypes)];

                    // Only the following types are supported right now. Hang around for more!
nicolasconnault's avatar
nicolasconnault committed
609
610
                    $supported_types = array('match', 'essay', 'multianswer', 'multichoice', 'shortanswer',
                            'numerical', 'truefalse', 'calculated');
611
612
613
614
615
616
617
618
619
620
621
622
623
                    $qtype = $supported_types[array_rand($supported_types)];

                    if ($qtype == 'calculated') {
                        continue;
                    }
                    $classname = "question_{$qtype}_qtype";
                    if ($qtype == 'multianswer') {
                        $classname = "embedded_cloze_qtype";
                    }

                    $question = new $classname();
                    $question->qtype = $qtype;
                    $questions[$courseid][] = $question->generate_test("question$qtype-$i", $courseid);
nicolasconnault's avatar
nicolasconnault committed
624
                    $this->verbose("Generated a question of type $qtype for course id $courseid.");
625
626
627
628
                }
            }

            // Assign questions to quizzes, if such exist
nicolasconnault's avatar
nicolasconnault committed
629
630
631
            if (!empty($modules['quiz']) && !empty($questions) && !is_null($this->get('questions_per_quiz'))) {
                $quizzes = $modules['quiz'];

632
                // Cannot assign more questions per quiz than are available, so determine which is the largest
nicolasconnault's avatar
nicolasconnault committed
633
                $questions_per_quiz = max(count($questions), $this->get('questions_per_quiz'));
634
635
636
637
638
639
640

                foreach ($quizzes as $quiz) {
                    $questions_added = array();
                    for ($i = 0; $i < $questions_per_quiz; $i++) {

                        // Add a random question to the quiz
                        do {
641
642
643
                            if (empty($quiz->course)) {
                                print_object($quizzes);die();
                            }
644
645
646
647
648
649
                            $random = rand(0, count($questions[$quiz->course]));
                        } while (in_array($random, $questions_added) || !array_key_exists($random, $questions[$quiz->course]));

                        if (!quiz_add_quiz_question($questions[$quiz->course][$random]->id, $quiz)) {

                            // Could not add question to quiz!! report error
650
651
652
                            if (!$this->get('quiet')) {
                                echo "WARNING: Could not add question id $random to quiz id $quiz->id{$this->eolchar}";
                            }
653
                        } else {
nicolasconnault's avatar
nicolasconnault committed
654
                            $this->verbose("Adding question id $random to quiz id $quiz->id.");
655
656
657
658
659
                            $questions_added[] = $random;
                        }
                    }
                }
            }
nicolasconnault's avatar
nicolasconnault committed
660
            return $questions;
661
        }
nicolasconnault's avatar
nicolasconnault committed
662
663
        return null;
    }
664

nicolasconnault's avatar
nicolasconnault committed
665
666
    public function generate_role_assignments($users, $courses) {
        global $CFG, $DB;
667
        $course_users = array();
nicolasconnault's avatar
nicolasconnault committed
668

669
        if (count($courses) > 0) {
nicolasconnault's avatar
nicolasconnault committed
670
            $this->verbose("Inserting student->course role assignments...");
671
672
673
674
            $assigned_count = 0;
            $assigned_users = array();

            foreach ($courses as $courseid) {
675
676
                $course_users[$courseid] = array();

677
678
                // Select $students_per_course for assignment to course
                shuffle($users);
nicolasconnault's avatar
nicolasconnault committed
679
                $users_to_assign = array_slice($users, 0, $this->get('students_per_course'));
680

681
                $context = get_context_instance(CONTEXT_COURSE, $courseid);
682
                foreach ($users_to_assign as $random_user) {
683
                    $success = role_assign(5, $random_user, 0, $context->id);
684
685
686

                    if ($success) {
                        $assigned_count++;
687
                        $course_users[$courseid][] = $random_user;
688
689
690
691
692
                        if (!isset($assigned_users[$random_user])) {
                            $assigned_users[$random_user] = 1;
                        } else {
                            $assigned_users[$random_user]++;
                        }
nicolasconnault's avatar
nicolasconnault committed
693
                        $this->verbose("Student $random_user was assigned to course $courseid.");
694
                    } else {
nicolasconnault's avatar
nicolasconnault committed
695
696
                        $this->verbose("Could not assign student $random_user to course $courseid!");
                        if (!$this->get('ignore_errors')) {
697
698
699
700
701
702
                            die();
                        }
                    }
                }
            }

nicolasconnault's avatar
nicolasconnault committed
703
704
            if (!$this->get('quiet')) {
                echo "$assigned_count user => course role assignments have been correctly performed.{$this->eolchar}";
705
            }
nicolasconnault's avatar
nicolasconnault committed
706
            return $course_users;
707
        }
nicolasconnault's avatar
nicolasconnault committed
708
709
        return null;
    }
710

nicolasconnault's avatar
nicolasconnault committed
711
712
713
714
715
716
717
    public function generate_forum_posts($course_users, $modules) {
        global $CFG, $DB, $USER;

        if (in_array('forum', $this->modules_list) &&
                $this->get('discussions_per_forum') &&
                $this->get('posts_per_discussion') &&
                isset($modules['forum'])) {
718
719
720
721

            $discussions_count = 0;
            $posts_count = 0;

nicolasconnault's avatar
nicolasconnault committed
722
            foreach ($modules['forum'] as $forum) {
723
724
                $forum_users = $course_users[$forum->course];

nicolasconnault's avatar
nicolasconnault committed
725
                for ($i = 0; $i < $this->get('discussions_per_forum'); $i++) {
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
                    $mform = new fake_form();

                    require_once($CFG->dirroot.'/mod/forum/lib.php');

                    $discussion = new stdClass();
                    $discussion->name = 'Test discussion';
                    $discussion->intro = 'This is just a test forum discussion';
                    $discussion->format = 1;
                    $discussion->forum = $forum->id;
                    $discussion->mailnow = false;
                    $discussion->course = $forum->course;

                    $message = '';
                    $super_global_user = clone($USER);
                    $user_id = $forum_users[array_rand($forum_users)];
                    $USER = $DB->get_record('user', array('id' => $user_id));

                    if ($discussion_id = forum_add_discussion($discussion, $mform, $message)) {
                        $discussion = $DB->get_record('forum_discussions', array('id' => $discussion_id));
                        $discussions_count++;

                        // Add posts to this discussion
                        $post_ids = array($discussion->firstpost);

nicolasconnault's avatar
nicolasconnault committed
750
                        for ($j = 0; $j < $this->get('posts_per_discussion'); $j++) {
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
                            $global_user = clone($USER);
                            $user_id = $forum_users[array_rand($forum_users)];
                            $USER = $DB->get_record('user', array('id' => $user_id));
                            $post = new stdClass();
                            $post->discussion = $discussion_id;
                            $post->subject = 'Re: test discussion';
                            $post->message = '<p>Nothing much to say, since this is just a test...</p>';
                            $post->format = 1;
                            $post->parent = $post_ids[array_rand($post_ids)];

                            if ($post_ids[] = forum_add_new_post($post, $mform, $message)) {
                                $posts_count++;
                            }
                            $USER = $global_user;
                        }
                    }

                    $USER = $super_global_user;

                    if ($forum->type == 'single') {
                        break;
                    }
                }
            }
nicolasconnault's avatar
nicolasconnault committed
775
776
            if ($discussions_count > 0 && !$this->get('quiet')) {
                echo "$discussions_count forum discussions have been generated.{$this->eolchar}";
777
            }
nicolasconnault's avatar
nicolasconnault committed
778
779
            if ($posts_count > 0 && !$this->get('quiet')) {
                echo "$posts_count forum posts have been generated.{$this->eolchar}";
780
            }
nicolasconnault's avatar
nicolasconnault committed
781
782

            return true;
783
        }
nicolasconnault's avatar
nicolasconnault committed
784
785
786
787
788
        return null;

    }

    public function generate_grades($course_users, $courses, $modules) {
789
        global $CFG, $DB, $USER;
790

791
792
793
        /**
         * ASSIGNMENT GRADES GENERATION
         */
794
        if ($this->get('assignment_grades') && isset($modules['assignment'])) {
795
            $grades_count = 0;
796
797
798
799
800
801
802
803
804
805
806
807
            foreach ($course_users as $courseid => $userid_array) {
                foreach ($userid_array as $userid) {
                    foreach ($modules['assignment'] as $assignment) {
                        if (in_array($assignment->course, $courses)) {
                            $maxgrade = $assignment->grade;
                            $random_grade = rand(0, $maxgrade);
                            $grade = new stdClass();
                            $grade->assignment = $assignment->id;
                            $grade->userid = $userid;
                            $grade->grade = $random_grade;
                            $grade->rawgrade = $random_grade;
                            $grade->teacher = $USER->id;
808
                            $grade->submissioncomment = 'comment';
809
810
811
812
813
814
                            $DB->insert_record('assignment_submissions', $grade);
                            grade_update('mod/assignment', $assignment->course, 'mod', 'assignment', $assignment->id, 0, $grade);
                            $this->verbose("A grade ($random_grade) has been given to user $userid "
                                        . "for assignment $assignment->id");
                            $grades_count++;
                        }
815
816
817
                    }
                }
            }
818
            if ($grades_count > 0) {
819
                $this->verbose("$grades_count assignment grades have been generated.{$this->eolchar}");
820
            }
821
822
823
824
825
        }

        /**
         * QUIZ GRADES GENERATION
         */
826
        if ($this->get('quiz_grades') && isset($modules['quiz'])) {
827
828
            $grades_count = 0;
            foreach ($course_users as $userid => $courses) {
nicolasconnault's avatar
nicolasconnault committed
829
                foreach ($modules['quiz'] as $quiz) {
830
831
832
833
834
835
836
837
838
839
                    if (in_array($quiz->course, $courses)) {
                        $maxgrade = $quiz->grade;
                        $random_grade = rand(0, $maxgrade);
                        $grade = new stdClass();
                        $grade->quiz = $quiz->id;
                        $grade->userid = $userid;
                        $grade->grade = $random_grade;
                        $grade->rawgrade = $random_grade;
                        $DB->insert_record('quiz_grades', $grade);
                        grade_update('mod/quiz', $courseid, 'mod', 'quiz', $quiz->id, 0, $grade);
nicolasconnault's avatar
nicolasconnault committed
840
                        $this->verbose("A grade ($random_grade) has been given to user $userid for quiz $quiz->id");
841
842
843
844
                        $grades_count++;
                    }
                }
            }
845
            if ($grades_count > 0 && !$this->get('quiet')) {
nicolasconnault's avatar
nicolasconnault committed
846
                echo "$grades_count quiz grades have been generated.{$this->eolchar}";
847
            }
848
        }
nicolasconnault's avatar
nicolasconnault committed
849
850
851
852
853
        return null;
    }

    public function generate_module_content($course_users, $courses, $modules) {
        global $USER, $DB, $CFG;
854
        $result = null;
855
856

        $entries_count = 0;
nicolasconnault's avatar
nicolasconnault committed
857
858
859
        if ($this->get('entries_per_glossary') && !empty($modules['glossary'])) {
            foreach ($modules['glossary'] as $glossary) {
                for ($i = 0; $i < $this->get('entries_per_glossary'); $i++) {
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
                    $entry = new stdClass();
                    $entry->glossaryid = $glossary->id;
                    $entry->userid = $USER->id;
                    $entry->concept = "Test concept";
                    $entry->definition = "A test concept is nothing to write home about: just a test concept.";
                    $entry->format = 1;
                    $entry->timecreated = time();
                    $entry->timemodified = time();
                    $entry->teacherentry = 0;
                    $entry->approved = 1;
                    if ($DB->insert_record('glossary_entries', $entry)) {
                        $entries_count++;
                    }
                }
            }
nicolasconnault's avatar
nicolasconnault committed
875
876
            if ($entries_count > 0 && !$this->get('quiet')) {
                echo "$entries_count glossary definitions have been generated.{$this->eolchar}";
877
            }
878
            $result = true;
879
        }
880
881

        $fields_count = 0;
882
        if (!empty($modules['data']) && $this->get('fields_per_database') && $this->get('database_records_per_student')) {
883
884
885
886
887
888
889
890
891
892
893
894
895
            $database_field_types = array('checkbox',
                                          'date',
                                          'file',
                                          'latlong',
                                          'menu',
                                          'multimenu',
                                          'number',
                                          'picture',
                                          'radiobutton',
                                          'text',
                                          'textarea',
                                          'url');

896

897
898
899
900
            $fields = array();

            foreach ($modules['data'] as $data) {

901
902
903
904
                for ($i = 0; $i < $this->get('fields_per_database'); $i++) {
                    $type = $database_field_types[array_rand($database_field_types)];
                    require_once($CFG->dirroot.'/mod/data/field/'.$type.'/field.class.php');
                    $newfield = 'data_field_'.$type;
905
906
                    $cm = get_coursemodule_from_instance('data', $data->id);
                    $newfield = new $newfield(0, $data, $cm);
907
908
909
910
                    $fields[$data->id][] = $newfield;
                    $newfield->insert_field();
                }

911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
                // Generate fields for each database (same fields for all, no arguing)
                for ($i = 0; $i < $this->get('fields_per_database'); $i++) {

                }

                // Generate database records for each student, if needed
                for ($i = 0; $i < $this->get('database_records_per_student'); $i++) {

                }
            }
            if ($fields_count > 0 && !$this->get('quiet')) {
                $datacount = count($modules['data']);
                echo "$fields_count database fields have been generated for each of the "
                   . "$datacount generated databases.{$this->eolchar}";
            }
            $result = true;
        }

929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
        $messages_count = 0;
        if (!empty($modules['chat']) && $this->get('messages_per_chat')) {

            // Insert all users into chat_users table, then a message from each user
            foreach ($modules['chat'] as $chat) {

                foreach ($course_users as $courseid => $users_array) {

                    foreach ($users_array as $userid) {
                        if ($messages_count < $this->get('messages_per_chat')) {
                            $chat_user = new stdClass();
                            $chat_user->chatid = $chat->id;
                            $chat_user->userid = $userid;
                            $chat_user->course = $courseid;
                            $DB->insert_record('chat_users', $chat_user);

                            $chat_message = new stdClass();
                            $chat_message->chatid = $chat->id;
                            $chat_message->userid = $userid;
                            $chat_message->message = "Hi, everyone!";
                            $DB->insert_record('chat_messages', $chat_message);

                            $messages_count++;
                        }
                    }
                }
            }

            if ($messages_count > 0 && !$this->get('quiet')) {
                $datacount = count($modules['chat']);
                echo "$messages_count messages have been generated for each of the "
                   . "$datacount generated chats.{$this->eolchar}";
            }
            $result = true;
        }

965
        return $result;
966
967
968
969
    }


    /**
nicolasconnault's avatar
nicolasconnault committed
970
971
     * If verbose is switched on, prints a string terminated by the global eolchar string.
     * @param string $string The string to STDOUT
972
     */
nicolasconnault's avatar
nicolasconnault committed
973
974
975
976
    public function verbose($string) {
        if ($this->get('verbose') && !$this->get('quiet')) {
            echo $string . $this->eolchar;
        }
977
978
    }

979

nicolasconnault's avatar
nicolasconnault committed
980
    /**
981
982
     * Attempts to delete all generated test data.
     * WARNING: THIS WILL COMPLETELY MESS UP A "REAL" SITE, AND IS INTENDED ONLY FOR DEVELOPMENT PURPOSES
nicolasconnault's avatar
nicolasconnault committed
983
984
985
986
987
988
989
990
     */
    function data_cleanup() {
        global $DB;

        if ($this->get('quiet')) {
            ob_start();
        }

991
        // TODO Cleanup code
992

nicolasconnault's avatar
nicolasconnault committed
993
994
995
996
997
998
999
1000
        if ($this->get('quiet')) {
            ob_end_clean();
        }
    }

    public function get($setting) {
        if (isset($this->settings[$setting])) {
            return $this->settings[$setting]->value;
For faster browsing, not all history is shown. View entire blame