/** * Compute the quiz statistics. * * @param int $quizid the quiz id. * @param int $whichattempts which attempts to use, represented internally as one of the constants as used in * $quiz->grademethod ie. * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST * we calculate stats based on which attempts would affect the grade for each student. * @param \core\dml\sql_join $groupstudentsjoins Contains joins, wheres, params for students in this group. * @param int $p number of positions (slots). * @param float $sumofmarkvariance sum of mark variance, calculated as part of question statistics * @return calculated $quizstats The statistics for overall attempt scores. */ public function calculate($quizid, $whichattempts, \core\dml\sql_join $groupstudentsjoins, $p, $sumofmarkvariance) { $this->progress->start_progress('', 3); $quizstats = new calculated($whichattempts); $countsandaverages = $this->attempt_counts_and_averages($quizid, $groupstudentsjoins); $this->progress->progress(1); foreach ($countsandaverages as $propertyname => $value) { $quizstats->{$propertyname} = $value; } $s = $quizstats->s(); if ($s != 0) { // Recalculate sql again this time possibly including test for first attempt. list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $groupstudentsjoins, $whichattempts); $quizstats->median = $this->median($s, $fromqa, $whereqa, $qaparams); $this->progress->progress(2); if ($s > 1) { $powers = $this->sum_of_powers_of_difference_to_mean($quizstats->avg(), $fromqa, $whereqa, $qaparams); $this->progress->progress(3); $quizstats->standarddeviation = sqrt($powers->power2 / ($s - 1)); // Skewness. if ($s > 2) { // See http://docs.moodle.org/dev/Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis. $m2 = $powers->power2 / $s; $m3 = $powers->power3 / $s; $m4 = $powers->power4 / $s; $k2 = $s * $m2 / ($s - 1); $k3 = $s * $s * $m3 / (($s - 1) * ($s - 2)); if ($k2 != 0) { $quizstats->skewness = $k3 / pow($k2, 3 / 2); // Kurtosis. if ($s > 3) { $k4 = $s * $s * (($s + 1) * $m4 - 3 * ($s - 1) * $m2 * $m2) / (($s - 1) * ($s - 2) * ($s - 3)); $quizstats->kurtosis = $k4 / ($k2 * $k2); } if ($p > 1) { $quizstats->cic = 100 * $p / ($p - 1) * (1 - $sumofmarkvariance / $k2); $quizstats->errorratio = 100 * sqrt(1 - $quizstats->cic / 100); $quizstats->standarderror = $quizstats->errorratio * $quizstats->standarddeviation / 100; } } } } $quizstats->cache(quiz_statistics_qubaids_condition($quizid, $groupstudentsjoins, $whichattempts)); } $this->progress->end_progress(); return $quizstats; }
/** * Given one fullpath to directory, delete its contents recursively * Copied originally from somewhere in the net. * TODO: Modernise this * * If supplied, progress object should be ready to receive indeterminate * progress reports. * * @param string $dir Directory to delete * @param string $excludedir Exclude this directory * @param \core\progress\base $progress Optional progress reporting object */ public static function delete_dir_contents($dir, $excludeddir = '', \core\progress\base $progress = null) { global $CFG; if ($progress) { $progress->progress(); } if (!is_dir($dir)) { // if we've been given a directory that doesn't exist yet, return true. // this happens when we're trying to clear out a course that has only just // been created. return true; } $slash = "/"; // Create arrays to store files and directories $dir_files = array(); $dir_subdirs = array(); // Make sure we can delete it chmod($dir, $CFG->directorypermissions); if (($handle = opendir($dir)) == false) { // The directory could not be opened return false; } // Loop through all directory entries, and construct two temporary arrays containing files and sub directories while (false !== ($entry = readdir($handle))) { if (is_dir($dir . $slash . $entry) && $entry != ".." && $entry != "." && $entry != $excludeddir) { $dir_subdirs[] = $dir . $slash . $entry; } else { if ($entry != ".." && $entry != "." && $entry != $excludeddir) { $dir_files[] = $dir . $slash . $entry; } } } // Delete all files in the curent directory return false and halt if a file cannot be removed for ($i = 0; $i < count($dir_files); $i++) { chmod($dir_files[$i], $CFG->directorypermissions); if (unlink($dir_files[$i]) == false) { return false; } } // Empty sub directories and then remove the directory for ($i = 0; $i < count($dir_subdirs); $i++) { chmod($dir_subdirs[$i], $CFG->directorypermissions); if (self::delete_dir_contents($dir_subdirs[$i], '', $progress) == false) { return false; } else { if (remove_dir($dir_subdirs[$i]) == false) { return false; } } } // Close directory closedir($handle); // Success, every thing is gone return true return true; }
/** * Appropriate instance depending if we want html output for the user or not. * * @return \core\progress\base child of \core\progress\base to handle the display (or not) of task progress. */ protected function get_progress_trace_instance() { if ($this->progress === null) { if (!$this->table->is_downloading()) { $this->progress = new \core\progress\display_if_slow(get_string('calculatingallstats', 'quiz_statistics')); $this->progress->set_display_names(); } else { $this->progress = new \core\progress\none(); } } return $this->progress; }
/** * Get details information for main moodle_backup.xml file, extracting it from * the specified controller. * * If you specify the progress monitor, this will start a new progress section * to track progress in processing (in case this task takes a long time). * * @param string $backupid Backup ID * @param \core\progress\base $progress Optional progress monitor */ public static function get_moodle_backup_information($backupid, \core\progress\base $progress = null) { // Start tracking progress if required (for load_controller). if ($progress) { $progress->start_progress('get_moodle_backup_information', 2); } $detailsinfo = array(); // Information details $contentsinfo = array(); // Information about backup contents $settingsinfo = array(); // Information about backup settings $bc = self::load_controller($backupid); // Load controller // Note that we have loaded controller. if ($progress) { $progress->progress(1); } // Details info $detailsinfo['id'] = $bc->get_id(); $detailsinfo['backup_id'] = $bc->get_backupid(); $detailsinfo['type'] = $bc->get_type(); $detailsinfo['format'] = $bc->get_format(); $detailsinfo['interactive'] = $bc->get_interactive(); $detailsinfo['mode'] = $bc->get_mode(); $detailsinfo['execution'] = $bc->get_execution(); $detailsinfo['executiontime'] = $bc->get_executiontime(); $detailsinfo['userid'] = $bc->get_userid(); $detailsinfo['courseid'] = $bc->get_courseid(); // Init content placeholders $contentsinfo['activities'] = array(); $contentsinfo['sections'] = array(); $contentsinfo['course'] = array(); // Get tasks and start nested progress. $tasks = $bc->get_plan()->get_tasks(); if ($progress) { $progress->start_progress('get_moodle_backup_information', count($tasks)); $done = 1; } // Contents info (extract information from tasks) foreach ($tasks as $task) { if ($task instanceof backup_activity_task) { // Activity task if ($task->get_setting_value('included')) { // Only return info about included activities list($contentinfo, $settings) = self::get_activity_backup_information($task); $contentsinfo['activities'][] = $contentinfo; $settingsinfo = array_merge($settingsinfo, $settings); } } else { if ($task instanceof backup_section_task) { // Section task if ($task->get_setting_value('included')) { // Only return info about included sections list($contentinfo, $settings) = self::get_section_backup_information($task); $contentsinfo['sections'][] = $contentinfo; $settingsinfo = array_merge($settingsinfo, $settings); } } else { if ($task instanceof backup_course_task) { // Course task list($contentinfo, $settings) = self::get_course_backup_information($task); $contentsinfo['course'][] = $contentinfo; $settingsinfo = array_merge($settingsinfo, $settings); } else { if ($task instanceof backup_root_task) { // Root task list($contentinfo, $settings) = self::get_root_backup_information($task); $settingsinfo = array_merge($settingsinfo, $settings); } } } } // Report task handled. if ($progress) { $progress->progress($done++); } } $bc->destroy(); // Always need to destroy controller to handle circular references // Finish progress reporting. if ($progress) { $progress->end_progress(); $progress->end_progress(); } return array(array((object) $detailsinfo), $contentsinfo, $settingsinfo); }
/** * Moves all the existing 'item' annotations to their final 'itemfinal' ones * for a given backup. * * @param string $backupid Backup ID * @param string $itemname Item name * @param \core\progress\base $progress Progress tracker */ public static function move_annotations_to_final($backupid, $itemname, \core\progress\base $progress) { global $DB; $progress->start_progress('move_annotations_to_final'); $rs = $DB->get_recordset('backup_ids_temp', array('backupid' => $backupid, 'itemname' => $itemname)); $progress->progress(); foreach ($rs as $annotation) { // If corresponding 'itemfinal' annotation does not exist, update 'item' to 'itemfinal' if (!$DB->record_exists('backup_ids_temp', array('backupid' => $backupid, 'itemname' => $itemname . 'final', 'itemid' => $annotation->itemid))) { $DB->set_field('backup_ids_temp', 'itemname', $itemname . 'final', array('id' => $annotation->id)); } $progress->progress(); } $rs->close(); // All the remaining $itemname annotations can be safely deleted $DB->delete_records('backup_ids_temp', array('backupid' => $backupid, 'itemname' => $itemname)); $progress->end_progress(); }
/** * Check all the included users, deciding the action to perform * for each one (mapping / creation) and returning one array * of problems in case something is wrong (lack of permissions, * conficts) * * @param string $restoreid Restore id * @param int $courseid Course id * @param int $userid User id * @param bool $samesite True if restore is to same site * @param \core\progress\base $progress Progress reporter */ public static function precheck_included_users($restoreid, $courseid, $userid, $samesite, \core\progress\base $progress) { global $CFG, $DB; // To return any problem found $problems = array(); // We are going to map mnethostid, so load all the available ones $mnethosts = $DB->get_records('mnet_host', array(), 'wwwroot', 'wwwroot, id'); // Calculate the context we are going to use for capability checking $context = context_course::instance($courseid); // Calculate if we have perms to create users, by checking: // to 'moodle/restore:createuser' and 'moodle/restore:userinfo' // and also observe $CFG->disableusercreationonrestore $cancreateuser = false; if (has_capability('moodle/restore:createuser', $context, $userid) and has_capability('moodle/restore:userinfo', $context, $userid) and empty($CFG->disableusercreationonrestore)) { // Can create users $cancreateuser = true; } // Prepare for reporting progress. $conditions = array('backupid' => $restoreid, 'itemname' => 'user'); $max = $DB->count_records('backup_ids_temp', $conditions); $done = 0; $progress->start_progress('Checking users', $max); // Iterate over all the included users $rs = $DB->get_recordset('backup_ids_temp', $conditions, '', 'itemid, info'); foreach ($rs as $recuser) { $user = (object) backup_controller_dbops::decode_backup_temp_info($recuser->info); // Find the correct mnethostid for user before performing any further check if (empty($user->mnethosturl) || $user->mnethosturl === $CFG->wwwroot) { $user->mnethostid = $CFG->mnet_localhost_id; } else { // fast url-to-id lookups if (isset($mnethosts[$user->mnethosturl])) { $user->mnethostid = $mnethosts[$user->mnethosturl]->id; } else { $user->mnethostid = $CFG->mnet_localhost_id; } } // Now, precheck that user and, based on returned results, annotate action/problem $usercheck = self::precheck_user($user, $samesite); if (is_object($usercheck)) { // No problem, we have found one user in DB to be mapped to // Annotate it, for later process. Set newitemid to mapping user->id self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, $usercheck->id); } else { if ($usercheck === false) { // Found conflict, report it as problem $problems[] = get_string('restoreuserconflict', '', $user->username); } else { if ($usercheck === true) { // User needs to be created, check if we are able if ($cancreateuser) { // Can create user, set newitemid to 0 so will be created later self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, 0, null, (array) $user); } else { // Cannot create user, report it as problem $problems[] = get_string('restorecannotcreateuser', '', $user->username); } } else { // Shouldn't arrive here ever, something is for sure wrong. Exception throw new restore_dbops_exception('restore_error_processing_user', $user->username); } } } $done++; $progress->progress($done); } $rs->close(); $progress->end_progress(); return $problems; }
/** * Update all documents for ousearch. * * If specified, the progress object should be ready to receive indeterminate * progress calls. * * @param bool $feedback If true, prints feedback as HTML list items * @param int $courseid If specified, restricts to particular courseid * @param int $cmid If specified, restricts to particular cmid * @param \core\progress\base $progress Set to a progress object or null */ public static function search_update_all($feedback = false, $courseid = 0, $cmid = 0, \core\progress\base $progress = null) { global $DB; raise_memory_limit(MEMORY_EXTRA); // If cmid is specified, only retrieve that one if ($cmid) { $cmrestrict = "cm.id = ? AND"; $cmrestrictparams = array($cmid); } else { $cmrestrict = ''; $cmrestrictparams = array(); } // Get module-instances that need updating $cms = $DB->get_records_sql("\nSELECT\n cm.id, cm.course, cm.instance, f.name\nFROM\n {forumng} f\n INNER JOIN {course_modules} cm ON cm.instance = f.id\nWHERE\n {$cmrestrict}\n cm.module = (SELECT id FROM {modules} m WHERE name = 'forumng')" . ($courseid ? " AND f.course = ?" : ''), array_merge($cmrestrictparams, $courseid ? array($courseid) : array())); // Print count if ($feedback && !$cmid) { print '<li>' . get_string('search_update_count', 'forumng', '<strong>' . count($cms) . '</strong>') . '</li>'; } // This can take a while, so let's be sure to have a long time limit. $timelimitbefore = 300; // Loop around updating foreach ($cms as $cm) { $transaction = $DB->start_delegated_transaction(); // Wipe existing search data, if any local_ousearch_document::delete_module_instance_data($cm); // Get all discussions for this forum $discussions = $DB->get_records('forumng_discussions', array('forumngid' => $cm->instance), '', 'id, postid'); if ($feedback) { print '<li><strong>' . $cm->name . '</strong> (' . count($discussions) . '):'; } // Process each discussion foreach ($discussions as $discussionrec) { // Ignore discussion with no postid // (This should not happen, where ther is a $discussionrec->id // it also shopuld have a $discussionrec->postid. This if-statement // fixes bug 10497 and would not have any side-effect.) if (!$discussionrec->postid) { continue; } set_time_limit($timelimitbefore); $discussion = mod_forumng_discussion::get_from_id($discussionrec->id, self::CLONE_DIRECT, -1); $root = $discussion->get_root_post(); $root->search_update(); $root->search_update_children(); $root = null; if ($feedback) { echo '. '; flush(); } if ($progress) { $progress->progress(\core\progress\base::INDETERMINATE); } } $transaction->allow_commit(); if ($feedback) { print '</li>'; } } }