[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/admin/tool/generator/classes/ -> course_backend.php (source)

   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   * tool_generator course backend code.
  19   *
  20   * @package tool_generator
  21   * @copyright 2013 The Open University
  22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  /**
  28   * Backend code for the 'make large course' tool.
  29   *
  30   * @package tool_generator
  31   * @copyright 2013 The Open University
  32   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   */
  34  class tool_generator_course_backend extends tool_generator_backend {
  35      /**
  36       * @var array Number of sections in course
  37       */
  38      private static $paramsections = array(1, 10, 100, 500, 1000, 2000);
  39      /**
  40       * @var array Number of assignments in course
  41       */
  42      private static $paramassignments = array(1, 10, 100, 500, 1000, 2000);
  43      /**
  44       * @var array Number of Page activities in course
  45       */
  46      private static $parampages = array(1, 50, 200, 1000, 5000, 10000);
  47      /**
  48       * @var array Number of students enrolled in course
  49       */
  50      private static $paramusers = array(1, 100, 1000, 10000, 50000, 100000);
  51      /**
  52       * Total size of small files: 1KB, 1MB, 10MB, 100MB, 1GB, 2GB.
  53       *
  54       * @var array Number of small files created in a single file activity
  55       */
  56      private static $paramsmallfilecount = array(1, 64, 128, 1024, 16384, 32768);
  57      /**
  58       * @var array Size of small files (to make the totals into nice numbers)
  59       */
  60      private static $paramsmallfilesize = array(1024, 16384, 81920, 102400, 65536, 65536);
  61      /**
  62       * Total size of big files: 8KB, 8MB, 80MB, 800MB, 8GB, 16GB.
  63       *
  64       * @var array Number of big files created as individual file activities
  65       */
  66      private static $parambigfilecount = array(1, 2, 5, 10, 10, 10);
  67      /**
  68       * @var array Size of each large file
  69       */
  70      private static $parambigfilesize = array(8192, 4194304, 16777216, 83886080,
  71              858993459, 1717986918);
  72      /**
  73       * @var array Number of forum discussions
  74       */
  75      private static $paramforumdiscussions = array(1, 10, 100, 500, 1000, 2000);
  76      /**
  77       * @var array Number of forum posts per discussion
  78       */
  79      private static $paramforumposts = array(2, 2, 5, 10, 10, 10);
  80  
  81      /**
  82       * @var string Course shortname
  83       */
  84      private $shortname;
  85  
  86      /**
  87       * @var testing_data_generator Data generator
  88       */
  89      protected $generator;
  90  
  91      /**
  92       * @var stdClass Course object
  93       */
  94      private $course;
  95  
  96      /**
  97       * @var array Array from test user number (1...N) to userid in database
  98       */
  99      private $userids;
 100  
 101      /**
 102       * Constructs object ready to create course.
 103       *
 104       * @param string $shortname Course shortname
 105       * @param int $size Size as numeric index
 106       * @param bool $fixeddataset To use fixed or random data
 107       * @param int|bool $filesizelimit The max number of bytes for a generated file
 108       * @param bool $progress True if progress information should be displayed
 109       */
 110      public function __construct($shortname, $size, $fixeddataset = false, $filesizelimit = false, $progress = true) {
 111  
 112          // Set parameters.
 113          $this->shortname = $shortname;
 114  
 115          parent::__construct($size, $fixeddataset, $filesizelimit, $progress);
 116      }
 117  
 118      /**
 119       * Returns the relation between users and course sizes.
 120       *
 121       * @return array
 122       */
 123      public static function get_users_per_size() {
 124          return self::$paramusers;
 125      }
 126  
 127      /**
 128       * Gets a list of size choices supported by this backend.
 129       *
 130       * @return array List of size (int) => text description for display
 131       */
 132      public static function get_size_choices() {
 133          $options = array();
 134          for ($size = self::MIN_SIZE; $size <= self::MAX_SIZE; $size++) {
 135              $options[$size] = get_string('coursesize_' . $size, 'tool_generator');
 136          }
 137          return $options;
 138      }
 139  
 140      /**
 141       * Checks that a shortname is available (unused).
 142       *
 143       * @param string $shortname Proposed course shortname
 144       * @return string An error message if the name is unavailable or '' if OK
 145       */
 146      public static function check_shortname_available($shortname) {
 147          global $DB;
 148          $fullname = $DB->get_field('course', 'fullname',
 149                  array('shortname' => $shortname), IGNORE_MISSING);
 150          if ($fullname !== false) {
 151              // I wanted to throw an exception here but it is not possible to
 152              // use strings from moodle.php in exceptions, and I didn't want
 153              // to duplicate the string in tool_generator, so I changed this to
 154              // not use exceptions.
 155              return get_string('shortnametaken', 'moodle', $fullname);
 156          }
 157          return '';
 158      }
 159  
 160      /**
 161       * Runs the entire 'make' process.
 162       *
 163       * @return int Course id
 164       */
 165      public function make() {
 166          global $DB, $CFG;
 167          require_once($CFG->dirroot . '/lib/phpunit/classes/util.php');
 168  
 169          raise_memory_limit(MEMORY_EXTRA);
 170  
 171          if ($this->progress && !CLI_SCRIPT) {
 172              echo html_writer::start_tag('ul');
 173          }
 174  
 175          $entirestart = microtime(true);
 176  
 177          // Start transaction.
 178          $transaction = $DB->start_delegated_transaction();
 179  
 180          // Get generator.
 181          $this->generator = phpunit_util::get_data_generator();
 182  
 183          // Make course.
 184          $this->course = $this->create_course();
 185          $this->create_users();
 186          $this->create_assignments();
 187          $this->create_pages();
 188          $this->create_small_files();
 189          $this->create_big_files();
 190          $this->create_forum();
 191  
 192          // Log total time.
 193          $this->log('coursecompleted', round(microtime(true) - $entirestart, 1));
 194  
 195          if ($this->progress && !CLI_SCRIPT) {
 196              echo html_writer::end_tag('ul');
 197          }
 198  
 199          // Commit transaction and finish.
 200          $transaction->allow_commit();
 201          return $this->course->id;
 202      }
 203  
 204      /**
 205       * Creates the actual course.
 206       *
 207       * @return stdClass Course record
 208       */
 209      private function create_course() {
 210          $this->log('createcourse', $this->shortname);
 211          $courserecord = array('shortname' => $this->shortname,
 212                  'fullname' => get_string('fullname', 'tool_generator',
 213                      array('size' => get_string('shortsize_' . $this->size, 'tool_generator'))),
 214                  'numsections' => self::$paramsections[$this->size]);
 215          return $this->generator->create_course($courserecord, array('createsections' => true));
 216      }
 217  
 218      /**
 219       * Creates a number of user accounts and enrols them on the course.
 220       * Note: Existing user accounts that were created by this system are
 221       * reused if available.
 222       */
 223      private function create_users() {
 224          global $DB;
 225  
 226          // Work out total number of users.
 227          $count = self::$paramusers[$this->size];
 228  
 229          // Get existing users in order. We will 'fill up holes' in this up to
 230          // the required number.
 231          $this->log('checkaccounts', $count);
 232          $nextnumber = 1;
 233          $rs = $DB->get_recordset_select('user', $DB->sql_like('username', '?'),
 234                  array('tool_generator_%'), 'username', 'id, username');
 235          foreach ($rs as $rec) {
 236              // Extract number from username.
 237              $matches = array();
 238              if (!preg_match('~^tool_generator_([0-9]{6})$~', $rec->username, $matches)) {
 239                  continue;
 240              }
 241              $number = (int)$matches[1];
 242  
 243              // Create missing users in range up to this.
 244              if ($number != $nextnumber) {
 245                  $this->create_user_accounts($nextnumber, min($number - 1, $count));
 246              } else {
 247                  $this->userids[$number] = (int)$rec->id;
 248              }
 249  
 250              // Stop if we've got enough users.
 251              $nextnumber = $number + 1;
 252              if ($number >= $count) {
 253                  break;
 254              }
 255          }
 256          $rs->close();
 257  
 258          // Create users from end of existing range.
 259          if ($nextnumber <= $count) {
 260              $this->create_user_accounts($nextnumber, $count);
 261          }
 262  
 263          // Assign all users to course.
 264          $this->log('enrol', $count, true);
 265  
 266          $enrolplugin = enrol_get_plugin('manual');
 267          $instances = enrol_get_instances($this->course->id, true);
 268          foreach ($instances as $instance) {
 269              if ($instance->enrol === 'manual') {
 270                  break;
 271              }
 272          }
 273          if ($instance->enrol !== 'manual') {
 274              throw new coding_exception('No manual enrol plugin in course');
 275          }
 276          $role = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
 277  
 278          for ($number = 1; $number <= $count; $number++) {
 279              // Enrol user.
 280              $enrolplugin->enrol_user($instance, $this->userids[$number], $role->id);
 281              $this->dot($number, $count);
 282          }
 283  
 284          // Sets the pointer at the beginning to be aware of the users we use.
 285          reset($this->userids);
 286  
 287          $this->end_log();
 288      }
 289  
 290      /**
 291       * Creates user accounts with a numeric range.
 292       *
 293       * @param int $first Number of first user
 294       * @param int $last Number of last user
 295       */
 296      private function create_user_accounts($first, $last) {
 297          global $CFG;
 298  
 299          $this->log('createaccounts', (object)array('from' => $first, 'to' => $last), true);
 300          $count = $last - $first + 1;
 301          $done = 0;
 302          for ($number = $first; $number <= $last; $number++, $done++) {
 303              // Work out username with 6-digit number.
 304              $textnumber = (string)$number;
 305              while (strlen($textnumber) < 6) {
 306                  $textnumber = '0' . $textnumber;
 307              }
 308              $username = 'tool_generator_' . $textnumber;
 309  
 310              // Create user account.
 311              $record = array('firstname' => get_string('firstname', 'tool_generator'),
 312                      'lastname' => $number, 'username' => $username);
 313  
 314              // We add a user password if it has been specified.
 315              if (!empty($CFG->tool_generator_users_password)) {
 316                  $record['password'] = $CFG->tool_generator_users_password;
 317              }
 318  
 319              $user = $this->generator->create_user($record);
 320              $this->userids[$number] = (int)$user->id;
 321              $this->dot($done, $count);
 322          }
 323          $this->end_log();
 324      }
 325  
 326      /**
 327       * Creates a number of Assignment activities.
 328       */
 329      private function create_assignments() {
 330          // Set up generator.
 331          $assigngenerator = $this->generator->get_plugin_generator('mod_assign');
 332  
 333          // Create assignments.
 334          $number = self::$paramassignments[$this->size];
 335          $this->log('createassignments', $number, true);
 336          for ($i = 0; $i < $number; $i++) {
 337              $record = array('course' => $this->course);
 338              $options = array('section' => $this->get_target_section());
 339              $assigngenerator->create_instance($record, $options);
 340              $this->dot($i, $number);
 341          }
 342  
 343          $this->end_log();
 344      }
 345  
 346      /**
 347       * Creates a number of Page activities.
 348       */
 349      private function create_pages() {
 350          // Set up generator.
 351          $pagegenerator = $this->generator->get_plugin_generator('mod_page');
 352  
 353          // Create pages.
 354          $number = self::$parampages[$this->size];
 355          $this->log('createpages', $number, true);
 356          for ($i = 0; $i < $number; $i++) {
 357              $record = array('course' => $this->course);
 358              $options = array('section' => $this->get_target_section());
 359              $pagegenerator->create_instance($record, $options);
 360              $this->dot($i, $number);
 361          }
 362  
 363          $this->end_log();
 364      }
 365  
 366      /**
 367       * Creates one resource activity with a lot of small files.
 368       */
 369      private function create_small_files() {
 370          $count = self::$paramsmallfilecount[$this->size];
 371          $this->log('createsmallfiles', $count, true);
 372  
 373          // Create resource with default textfile only.
 374          $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
 375          $record = array('course' => $this->course,
 376                  'name' => get_string('smallfiles', 'tool_generator'));
 377          $options = array('section' => 0);
 378          $resource = $resourcegenerator->create_instance($record, $options);
 379  
 380          // Add files.
 381          $fs = get_file_storage();
 382          $context = context_module::instance($resource->cmid);
 383          $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
 384                  'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/');
 385          for ($i = 0; $i < $count; $i++) {
 386              $filerecord['filename'] = 'smallfile' . $i . '.dat';
 387  
 388              // Generate random binary data (different for each file so it
 389              // doesn't compress unrealistically).
 390              $data = self::get_random_binary($this->limit_filesize(self::$paramsmallfilesize[$this->size]));
 391  
 392              $fs->create_file_from_string($filerecord, $data);
 393              $this->dot($i, $count);
 394          }
 395  
 396          $this->end_log();
 397      }
 398  
 399      /**
 400       * Creates a string of random binary data. The start of the string includes
 401       * the current time, in an attempt to avoid large-scale repetition.
 402       *
 403       * @param int $length Number of bytes
 404       * @return Random data
 405       */
 406      private static function get_random_binary($length) {
 407  
 408          $data = microtime(true);
 409          if (strlen($data) > $length) {
 410              // Use last digits of data.
 411              return substr($data, -$length);
 412          }
 413          $length -= strlen($data);
 414          for ($j = 0; $j < $length; $j++) {
 415              $data .= chr(rand(1, 255));
 416          }
 417          return $data;
 418      }
 419  
 420      /**
 421       * Creates a number of resource activities with one big file each.
 422       */
 423      private function create_big_files() {
 424          global $CFG;
 425  
 426          // Work out how many files and how many blocks to use (up to 64KB).
 427          $count = self::$parambigfilecount[$this->size];
 428          $filesize = $this->limit_filesize(self::$parambigfilesize[$this->size]);
 429          $blocks = ceil($filesize / 65536);
 430          $blocksize = floor($filesize / $blocks);
 431  
 432          $this->log('createbigfiles', $count, true);
 433  
 434          // Prepare temp area.
 435          $tempfolder = make_temp_directory('tool_generator');
 436          $tempfile = $tempfolder . '/' . rand();
 437  
 438          // Create resources and files.
 439          $fs = get_file_storage();
 440          $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
 441          for ($i = 0; $i < $count; $i++) {
 442              // Create resource.
 443              $record = array('course' => $this->course,
 444                      'name' => get_string('bigfile', 'tool_generator', $i));
 445              $options = array('section' => $this->get_target_section());
 446              $resource = $resourcegenerator->create_instance($record, $options);
 447  
 448              // Write file.
 449              $handle = fopen($tempfile, 'w');
 450              if (!$handle) {
 451                  throw new coding_exception('Failed to open temporary file');
 452              }
 453              for ($j = 0; $j < $blocks; $j++) {
 454                  $data = self::get_random_binary($blocksize);
 455                  fwrite($handle, $data);
 456                  $this->dot($i * $blocks + $j, $count * $blocks);
 457              }
 458              fclose($handle);
 459  
 460              // Add file.
 461              $context = context_module::instance($resource->cmid);
 462              $filerecord = array('component' => 'mod_resource', 'filearea' => 'content',
 463                      'contextid' => $context->id, 'itemid' => 0, 'filepath' => '/',
 464                      'filename' => 'bigfile' . $i . '.dat');
 465              $fs->create_file_from_pathname($filerecord, $tempfile);
 466          }
 467  
 468          unlink($tempfile);
 469          $this->end_log();
 470      }
 471  
 472      /**
 473       * Creates one forum activity with a bunch of posts.
 474       */
 475      private function create_forum() {
 476          global $DB;
 477  
 478          $discussions = self::$paramforumdiscussions[$this->size];
 479          $posts = self::$paramforumposts[$this->size];
 480          $totalposts = $discussions * $posts;
 481  
 482          $this->log('createforum', $totalposts, true);
 483  
 484          // Create empty forum.
 485          $forumgenerator = $this->generator->get_plugin_generator('mod_forum');
 486          $record = array('course' => $this->course,
 487                  'name' => get_string('pluginname', 'forum'));
 488          $options = array('section' => 0);
 489          $forum = $forumgenerator->create_instance($record, $options);
 490  
 491          // Add discussions and posts.
 492          $sofar = 0;
 493          for ($i = 0; $i < $discussions; $i++) {
 494              $record = array('forum' => $forum->id, 'course' => $this->course->id,
 495                      'userid' => $this->get_target_user());
 496              $discussion = $forumgenerator->create_discussion($record);
 497              $parentid = $DB->get_field('forum_posts', 'id', array('discussion' => $discussion->id), MUST_EXIST);
 498              $sofar++;
 499              for ($j = 0; $j < $posts - 1; $j++, $sofar++) {
 500                  $record = array('discussion' => $discussion->id,
 501                          'userid' => $this->get_target_user(), 'parent' => $parentid);
 502                  $forumgenerator->create_post($record);
 503                  $this->dot($sofar, $totalposts);
 504              }
 505          }
 506  
 507          $this->end_log();
 508      }
 509  
 510      /**
 511       * Gets a section number.
 512       *
 513       * Depends on $this->fixeddataset.
 514       *
 515       * @return int A section number from 1 to the number of sections
 516       */
 517      private function get_target_section() {
 518  
 519          if (!$this->fixeddataset) {
 520              $key = rand(1, self::$paramsections[$this->size]);
 521          } else {
 522              // Using section 1.
 523              $key = 1;
 524          }
 525  
 526          return $key;
 527      }
 528  
 529      /**
 530       * Gets a user id.
 531       *
 532       * Depends on $this->fixeddataset.
 533       *
 534       * @return int A user id for a random created user
 535       */
 536      private function get_target_user() {
 537  
 538          if (!$this->fixeddataset) {
 539              $userid = $this->userids[rand(1, self::$paramusers[$this->size])];
 540          } else if ($userid = current($this->userids)) {
 541              // Moving pointer to the next user.
 542              next($this->userids);
 543          } else {
 544              // Returning to the beginning if we reached the end.
 545              $userid = reset($this->userids);
 546          }
 547  
 548          return $userid;
 549      }
 550  
 551      /**
 552       * Restricts the binary file size if necessary
 553       *
 554       * @param int $length The total length
 555       * @return int The limited length if a limit was specified.
 556       */
 557      private function limit_filesize($length) {
 558  
 559          // Limit to $this->filesizelimit.
 560          if (is_numeric($this->filesizelimit) && $length > $this->filesizelimit) {
 561              $length = floor($this->filesizelimit);
 562          }
 563  
 564          return $length;
 565      }
 566  
 567  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1