Merge branch 'MDL-41754-master' of git://github.com/jamiepratt/moodle

This commit is contained in:
Marina Glancy
2014-02-04 15:44:51 +08:00
35 changed files with 356 additions and 218 deletions
+2 -2
View File
@@ -97,7 +97,7 @@ echo $OUTPUT->header();
// Prepare a progress bar which can display optionally during long-running
// operations while setting up the UI.
$slowprogress = new core_backup_display_progress_if_slow(get_string('preparingui', 'backup'));
$slowprogress = new \core\progress\display_if_slow(get_string('preparingui', 'backup'));
$previous = optional_param('previous', false, PARAM_BOOL);
if ($backup->get_stage() == backup_ui::STAGE_SCHEMA && !$previous) {
@@ -121,7 +121,7 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
// Display an extra backup step bar so that we can show the 'processing' step first.
echo html_writer::start_div('', array('id' => 'executionprogress'));
echo $renderer->progress_bar($backup->get_progress_bar());
$backup->get_controller()->set_progress(new core_backup_display_progress());
$backup->get_controller()->set_progress(new \core\progress\display());
// Prepare logger and add to end of chain.
$logger = new core_backup_html_logger($CFG->debugdeveloper ? backup::LOG_DEBUG : backup::LOG_INFO);
@@ -110,7 +110,7 @@ class backup_controller extends base_controller {
// By default there is no progress reporter. Interfaces that wish to
// display progress must set it.
$this->progress = new core_backup_null_progress();
$this->progress = new \core\progress\null();
// Instantiate the output_controller singleton and active it if interactive and inmediate
$oc = output_controller::get_instance();
+5 -5
View File
@@ -24,7 +24,7 @@
*/
abstract class base_controller extends backup implements loggable {
/**
* @var core_backup_progress Progress reporting object.
* @var \core\progress\base Progress reporting object.
*/
protected $progress;
@@ -37,7 +37,7 @@ abstract class base_controller extends backup implements loggable {
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
* @return \core\progress\base Progress reporting object
*/
public function get_progress() {
return $this->progress;
@@ -46,9 +46,9 @@ abstract class base_controller extends backup implements loggable {
/**
* Sets the progress reporter.
*
* @param core_backup_progress $progress Progress reporting object
* @param \core\progress\base $progress Progress reporting object
*/
public function set_progress(core_backup_progress $progress) {
public function set_progress(\core\progress\base $progress) {
$this->progress = $progress;
}
@@ -82,4 +82,4 @@ abstract class base_controller extends backup implements loggable {
public function log($message, $level, $a = null, $depth = null, $display = false) {
backup_helper::log($message, $level, $a, $depth, $display, $this->logger);
}
}
}
@@ -70,10 +70,10 @@ class restore_controller extends base_controller {
* @param int $mode backup::MODE_[ GENERAL | HUB | IMPORT | SAMESITE ]
* @param int $userid
* @param int $target backup::TARGET_[ NEW_COURSE | CURRENT_ADDING | CURRENT_DELETING | EXISTING_ADDING | EXISTING_DELETING ]
* @param core_backup_progress $progress Optional progress monitor
* @param \core\progress\base $progress Optional progress monitor
*/
public function __construct($tempdir, $courseid, $interactive, $mode, $userid, $target,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
$this->tempdir = $tempdir;
$this->courseid = $courseid;
$this->interactive = $interactive;
@@ -111,7 +111,7 @@ class restore_controller extends base_controller {
if ($progress) {
$this->progress = $progress;
} else {
$this->progress = new core_backup_null_progress();
$this->progress = new \core\progress\null();
}
$this->progress->start_progress('Constructing restore_controller');
+1 -1
View File
@@ -95,7 +95,7 @@ if ($backup->get_stage() == backup_ui::STAGE_FINAL) {
echo $renderer->progress_bar($backup->get_progress_bar());
// Start the progress display - we split into 2 chunks for backup and restore.
$progress = new core_backup_display_progress();
$progress = new \core\progress\display();
$progress->start_progress('', 2);
$backup->get_controller()->set_progress($progress);
+2 -2
View File
@@ -1748,13 +1748,13 @@ class backup_zip_contents extends backup_execution_step implements file_progress
// Start tracking progress if necessary.
if (!$this->startedprogress) {
$reporter->start_progress('extract_file_to_dir', ($max == file_progress::INDETERMINATE)
? core_backup_progress::INDETERMINATE : $max);
? \core\progress\base::INDETERMINATE : $max);
$this->startedprogress = true;
}
// Pass progress through to whatever handles it.
$reporter->progress(($progress == file_progress::INDETERMINATE)
? core_backup_progress::INDETERMINATE : $progress);
? \core\progress\base::INDETERMINATE : $progress);
}
}
+1 -1
View File
@@ -3564,7 +3564,7 @@ class restore_create_question_files extends restore_execution_step {
// Track progress, as this task can take a long time.
$progress = $this->task->get_progress();
$progress->start_progress($this->get_name(), core_backup_progress::INDETERMINATE);
$progress->start_progress($this->get_name(), \core\progress\base::INDETERMINATE);
// Let's process only created questions
$questionsrs = $DB->get_recordset_sql("SELECT bi.itemid, bi.newitemid, bi.parentitemid, q.qtype
+3 -3
View File
@@ -34,7 +34,7 @@ echo $OUTPUT->header();
// Prepare a progress bar which can display optionally during long-running
// operations while setting up the UI.
$slowprogress = new core_backup_display_progress_if_slow(get_string('preparingui', 'backup'));
$slowprogress = new \core\progress\display_if_slow(get_string('preparingui', 'backup'));
// Overall, allow 10 units of progress.
$slowprogress->start_progress('', 10);
@@ -90,7 +90,7 @@ $slowprogress->end_progress();
if (!$restore->is_independent()) {
// Use a temporary (disappearing) progress bar to show the precheck progress if any.
$precheckprogress = new core_backup_display_progress_if_slow(get_string('preparingdata', 'backup'));
$precheckprogress = new \core\progress\display_if_slow(get_string('preparingdata', 'backup'));
$restore->get_controller()->set_progress($precheckprogress);
if ($restore->get_stage() == restore_ui::STAGE_PROCESS && !$restore->requires_substage()) {
try {
@@ -99,7 +99,7 @@ if (!$restore->is_independent()) {
// Show the current restore state (header with bolded item).
echo $renderer->progress_bar($restore->get_progress_bar());
// Start displaying the actual progress bar percentage.
$restore->get_controller()->set_progress(new core_backup_display_progress());
$restore->get_controller()->set_progress(new \core\progress\display());
// Prepare logger.
$logger = new core_backup_html_logger($CFG->debugdeveloper ? backup::LOG_DEBUG : backup::LOG_INFO);
$restore->get_controller()->add_logger($logger);
@@ -350,10 +350,10 @@ abstract class backup_controller_dbops extends backup_dbops {
* to track progress in processing (in case this task takes a long time).
*
* @param string $backupid Backup ID
* @param core_backup_progress $progress Optional progress monitor
* @param \core\progress\base $progress Optional progress monitor
*/
public static function get_moodle_backup_information($backupid,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
// Start tracking progress if required (for load_controller).
if ($progress) {
@@ -133,9 +133,9 @@ abstract class backup_structure_dbops extends backup_dbops {
*
* @param string $backupid Backup ID
* @param string $itemname Item name
* @param core_backup_progress $progress Progress tracker
* @param \core\progress\base $progress Progress tracker
*/
public static function move_annotations_to_final($backupid, $itemname, core_backup_progress $progress) {
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));
+14 -14
View File
@@ -112,10 +112,10 @@ abstract class restore_dbops {
*
* @param string $restoreid Restore id
* @param string $inforeffile File path
* @param core_backup_progress $progress Progress tracker
* @param \core\progress\base $progress Progress tracker
*/
public static function load_inforef_to_tempids($restoreid, $inforeffile,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
if (!file_exists($inforeffile)) { // Shouldn't happen ever, but...
throw new backup_helper_exception('missing_inforef_xml_file', $inforeffile);
@@ -123,7 +123,7 @@ abstract class restore_dbops {
// Set up progress tracking (indeterminate).
if (!$progress) {
$progress = new core_backup_null_progress();
$progress = new \core\progress\null();
}
$progress->start_progress('Loading inforef.xml file');
@@ -419,10 +419,10 @@ abstract class restore_dbops {
*
* @param string $restoreid Restore id
* @param string $usersfile File path
* @param core_backup_progress $progress Progress tracker
* @param \core\progress\base $progress Progress tracker
*/
public static function load_users_to_tempids($restoreid, $usersfile,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
if (!file_exists($usersfile)) { // Shouldn't happen ever, but...
throw new backup_helper_exception('missing_users_xml_file', $usersfile);
@@ -430,7 +430,7 @@ abstract class restore_dbops {
// Set up progress tracking (indeterminate).
if (!$progress) {
$progress = new core_backup_null_progress();
$progress = new \core\progress\null();
}
$progress->start_progress('Loading users into temporary table');
@@ -861,13 +861,13 @@ abstract class restore_dbops {
* @param int|null $olditemid
* @param int|null $forcenewcontextid explicit value for the new contextid (skip mapping)
* @param bool $skipparentitemidctxmatch
* @param core_backup_progress $progress Optional progress reporter
* @param \core\progress\base $progress Optional progress reporter
* @return array of result object
*/
public static function send_files_to_pool($basepath, $restoreid, $component, $filearea,
$oldcontextid, $dfltuserid, $itemname = null, $olditemid = null,
$forcenewcontextid = null, $skipparentitemidctxmatch = false,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
global $DB, $CFG;
$backupinfo = backup_general_helper::get_backup_information(basename($basepath));
@@ -1084,10 +1084,10 @@ abstract class restore_dbops {
* @param string $basepath Base path of unzipped backup
* @param string $restoreid Restore ID
* @param int $userid Default userid for files
* @param core_backup_progress $progress Object used for progress tracking
* @param \core\progress\base $progress Object used for progress tracking
*/
public static function create_included_users($basepath, $restoreid, $userid,
core_backup_progress $progress) {
\core\progress\base $progress) {
global $CFG, $DB;
$progress->start_progress('Creating included users');
@@ -1481,10 +1481,10 @@ abstract class restore_dbops {
* @param int $courseid Course id
* @param int $userid User id
* @param bool $samesite True if restore is to same site
* @param core_backup_progress $progress Progress reporter
* @param \core\progress\base $progress Progress reporter
*/
public static function precheck_included_users($restoreid, $courseid, $userid, $samesite,
core_backup_progress $progress) {
\core\progress\base $progress) {
global $CFG, $DB;
// To return any problem found
@@ -1570,10 +1570,10 @@ abstract class restore_dbops {
* @param int $courseid Course id
* @param int $userid User id
* @param bool $samesite True if restore is to same site
* @param core_backup_progress $progress Optional progress tracker
* @param \core\progress\base $progress Optional progress tracker
*/
public static function process_included_users($restoreid, $courseid, $userid, $samesite,
core_backup_progress $progress = null) {
\core\progress\base $progress = null) {
global $DB;
// Just let precheck_included_users() to do all the hard work
+10 -10
View File
@@ -46,9 +46,9 @@ abstract class backup_helper {
* progress reports.
*
* @param string $backupid Backup id
* @param core_backup_progress $progress Optional progress reporting object
* @param \core\progress\base $progress Optional progress reporting object
*/
static public function clear_backup_dir($backupid, core_backup_progress $progress = null) {
static public function clear_backup_dir($backupid, \core\progress\base $progress = null) {
global $CFG;
if (!self::delete_dir_contents($CFG->tempdir . '/backup/' . $backupid, '', $progress)) {
throw new backup_helper_exception('cannot_empty_backup_temp_dir');
@@ -63,9 +63,9 @@ abstract class backup_helper {
* progress reports.
*
* @param string $backupid Backup id
* @param core_backup_progress $progress Optional progress reporting object
* @param \core\progress\base $progress Optional progress reporting object
*/
static public function delete_backup_dir($backupid, core_backup_progress $progress = null) {
static public function delete_backup_dir($backupid, \core\progress\base $progress = null) {
global $CFG;
self::clear_backup_dir($backupid, $progress);
return rmdir($CFG->tempdir . '/backup/' . $backupid);
@@ -81,9 +81,9 @@ abstract class backup_helper {
*
* @param string $dir Directory to delete
* @param string $excludedir Exclude this directory
* @param core_backup_progress $progress Optional progress reporting object
* @param \core\progress\base $progress Optional progress reporting object
*/
static public function delete_dir_contents($dir, $excludeddir='', core_backup_progress $progress = null) {
static public function delete_dir_contents($dir, $excludeddir='', \core\progress\base $progress = null) {
global $CFG;
if ($progress) {
@@ -154,9 +154,9 @@ abstract class backup_helper {
* progress reports.
*
* @param int $deletefrom Time to delete from
* @param core_backup_progress $progress Optional progress reporting object
* @param \core\progress\base $progress Optional progress reporting object
*/
static public function delete_old_backup_dirs($deletefrom, core_backup_progress $progress = null) {
static public function delete_old_backup_dirs($deletefrom, \core\progress\base $progress = null) {
global $CFG;
$status = true;
@@ -213,12 +213,12 @@ abstract class backup_helper {
*
* @param int $backupid
* @param string $filepath zip file containing the backup
* @param core_backup_progress $progress Optional progress monitor
* @param \core\progress\base $progress Optional progress monitor
* @return stored_file if created, null otherwise
*
* @throws moodle_exception in case of any problems
*/
static public function store_backup_file($backupid, $filepath, core_backup_progress $progress = null) {
static public function store_backup_file($backupid, $filepath, \core\progress\base $progress = null) {
global $CFG;
// First of all, get some information from the backup_controller to help us decide
-4
View File
@@ -72,10 +72,6 @@ require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/core_backup_html_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress_if_slow.class.php');
require_once($CFG->dirroot . '/backup/util/settings/setting_dependency.class.php');
require_once($CFG->dirroot . '/backup/util/settings/base_setting.class.php');
require_once($CFG->dirroot . '/backup/util/settings/backup_setting.class.php');
@@ -61,10 +61,6 @@ require_once($CFG->dirroot . '/backup/util/loggers/file_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/core_backup_html_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/database_logger.class.php');
require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress.class.php');
require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress_if_slow.class.php');
require_once($CFG->dirroot . '/backup/util/factories/backup_factory.class.php');
require_once($CFG->dirroot . '/backup/util/factories/restore_factory.class.php');
require_once($CFG->dirroot . '/backup/util/helper/backup_helper.class.php');
+1 -1
View File
@@ -91,7 +91,7 @@ class backup_plan extends base_plan implements loggable {
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
* @return \core\progress\base Progress reporting object
*/
public function get_progress() {
return $this->controller->get_progress();
+1 -1
View File
@@ -185,7 +185,7 @@ abstract class base_plan implements checksumable, executable {
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
* @return \core\progress\base Progress reporting object
*/
public abstract function get_progress();
+1 -1
View File
@@ -126,7 +126,7 @@ abstract class base_task implements checksumable, executable, loggable {
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
* @return \core\progress\base Progress reporting object
*/
public function get_progress() {
return $this->plan->get_progress();
+1 -1
View File
@@ -98,7 +98,7 @@ class restore_plan extends base_plan implements loggable {
* Gets the progress reporter, which can be used to report progress within
* the backup or restore process.
*
* @return core_backup_progress Progress reporting object
* @return \core\progress\base Progress reporting object
*/
public function get_progress() {
return $this->controller->get_progress();
@@ -103,7 +103,7 @@ abstract class restore_structure_step extends restore_step {
// Set up progress tracking.
$progress = $this->get_task()->get_progress();
$progress->start_progress($this->get_name(), core_backup_progress::INDETERMINATE);
$progress->start_progress($this->get_name(), \core\progress\base::INDETERMINATE);
$xmlparser->set_progress($progress);
// And process it, dispatch to target methods in step will start automatically
@@ -229,7 +229,7 @@ abstract class restore_structure_step extends restore_step {
// the execute() method here, which does set up progress like this.)
$progress = $this->get_task()->get_progress();
if (!$progress->is_in_progress_section() ||
$progress->get_current_max() !== core_backup_progress::INDETERMINATE) {
$progress->get_current_max() !== \core\progress\base::INDETERMINATE) {
$progress = null;
}
@@ -38,7 +38,7 @@ class backup_structure_processor extends base_processor {
protected $vars; // array of backup::VAR_XXX => helper value pairs to be used by source specifications
/**
* @var core_backup_progress Progress tracker (null if none)
* @var \core\progress\base Progress tracker (null if none)
*/
protected $progress;
@@ -46,9 +46,9 @@ class backup_structure_processor extends base_processor {
* Constructor.
*
* @param xml_writer $writer XML writer to save data
* @param core_backup_progress $progress Progress tracker (optional)
* @param c\core\progress\base$progress Progress tracker (optional)
*/
public function __construct(xml_writer $writer, core_backup_progress $progress = null) {
public function __construct(xml_writer $writer, \core\progress\base $progress = null) {
$this->writer = $writer;
$this->progress = $progress;
$this->vars = array();
+5 -5
View File
@@ -51,7 +51,7 @@ class restore_ui extends base_ui {
protected $stage = null;
/**
* @var core_backup_progress Progress indicator (where there is no controller)
* @var \core\progress\base Progress indicator (where there is no controller)
*/
protected $progressreporter = null;
@@ -146,11 +146,11 @@ class restore_ui extends base_ui {
* there are long-running tasks even though there is no restore controller
* in use.
*
* @return core_backup_null_progress
* @return \core\progress\null
*/
public function get_progress_reporter() {
if (!$this->progressreporter) {
$this->progressreporter = new core_backup_null_progress();
$this->progressreporter = new \core\progress\null();
}
return $this->progressreporter;
}
@@ -158,9 +158,9 @@ class restore_ui extends base_ui {
/**
* Sets the progress reporter that will be returned by get_progress_reporter.
*
* @param core_backup_progress $progressreporter Progress reporter
* @param c\core\progress\base$progressreporter Progress reporter
*/
public function set_progress_reporter(core_backup_progress $progressreporter) {
public function set_progress_reporter(\core\progress\base $progressreporter) {
$this->progressreporter = $progressreporter;
}
+7 -7
View File
@@ -94,7 +94,7 @@ abstract class restore_ui_stage extends base_ui_stage {
*/
abstract class restore_ui_independent_stage {
/**
* @var core_backup_progress Optional progress reporter
* @var \core\progress\base Optional progress reporter
*/
private $progressreporter;
@@ -117,11 +117,11 @@ abstract class restore_ui_independent_stage {
* in use. There is a similar function in restore_ui. but that class is not
* used on some stages.
*
* @return core_backup_null_progress
* @return \core\progress\null
*/
public function get_progress_reporter() {
if (!$this->progressreporter) {
$this->progressreporter = new core_backup_null_progress();
$this->progressreporter = new \core\progress\null();
}
return $this->progressreporter;
}
@@ -129,9 +129,9 @@ abstract class restore_ui_independent_stage {
/**
* Sets the progress reporter that will be returned by get_progress_reporter.
*
* @param core_backup_progress $progressreporter Progress reporter
* @param \core\progress\base $progressreporter Progress reporter
*/
public function set_progress_reporter(core_backup_progress $progressreporter) {
public function set_progress_reporter(\core\progress\base $progressreporter) {
$this->progressreporter = $progressreporter;
}
@@ -272,13 +272,13 @@ class restore_ui_stage_confirm extends restore_ui_independent_stage implements f
// Start tracking progress if necessary.
if (!$this->startedprogress) {
$reporter->start_progress('extract_file_to_dir',
($max == file_progress::INDETERMINATE) ? core_backup_progress::INDETERMINATE : $max);
($max == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $max);
$this->startedprogress = true;
}
// Pass progress through to whatever handles it.
$reporter->progress(
($progress == file_progress::INDETERMINATE) ? core_backup_progress::INDETERMINATE : $progress);
($progress == file_progress::INDETERMINATE) ? \core\progress\base::INDETERMINATE : $progress);
}
/**
@@ -29,7 +29,7 @@
* attributes and case folding and works only with UTF-8 content. It's one
* progressive push parser because, intead of loading big crunchs of information
* in memory, it "publishes" (pushes) small information in a "propietary array format" througt
* the corresponding @progressive_parser_procesor, that will be the responsibe for
* the corresponding @progressive_parser_processor, that will be the responsibe for
* returning information into handy formats to higher levels.
*
* Note that, while this progressive parser is able to process any XML file, it is
@@ -37,7 +37,7 @@
* the expected behaviour) so information belonging to the same path can be returned in
* different chunks if there are inner levels/paths in the middle. Be warned!
*
* The "propietary array format" that the parser publishes to the @progressive_parser_procesor
* The "propietary array format" that the parser publishes to the @progressive_parser_processor
* is this:
* array (
* 'path' => path where the tags belong to,
@@ -56,7 +56,11 @@ class progressive_parser {
protected $xml_parser; // PHP's low level XML SAX parser
protected $file; // full path to file being progressively parsed | => mutually exclusive
protected $contents; // contents being progressively parsed |
protected $procesor; // progressive_parser_procesor to be used to publish processed information
/**
* @var progressive_parser_processor to be used to publish processed information
*/
protected $processor;
protected $level; // level of the current tag
protected $path; // path of the current tag
@@ -68,7 +72,7 @@ class progressive_parser {
protected $currtag; // name/value/attributes of the tag being processed
/**
* @var core_backup_progress Progress tracker called for each action
* @var \core\progress\base Progress tracker called for each action
*/
protected $progress;
@@ -129,9 +133,9 @@ class progressive_parser {
*
* The caller should have already called start_progress on the progress tracker.
*
* @param core_backup_progress $progress Progress tracker
* @param \core\progress\base $progress Progress tracker
*/
public function set_progress(core_backup_progress $progress) {
public function set_progress(\core\progress\base $progress) {
$this->progress = $progress;
}
@@ -14,17 +14,21 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\progress;
defined('MOODLE_INTERNAL') || die();
/**
* Base class for handling progress information during a backup and restore.
* Base class for handling progress information.
*
* Subclasses should generally override the current_progress function which
* summarises all progress information.
*
* @package core_backup
* @package core_progress
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class core_backup_progress {
abstract class base {
/**
* @var int Constant indicating that the number of progress calls is unknown.
*/
@@ -66,7 +70,7 @@ abstract class core_backup_progress {
protected $currents = array();
/**
* @var int Array of counts within parent progress entry (ignored for first)
* @var int[] Array of counts within parent progress entry (ignored for first)
*/
protected $parentcounts = array();
@@ -88,16 +92,16 @@ abstract class core_backup_progress {
* @param string $description Description to display
* @param int $max Maximum value of progress for this section
* @param int $parentcount How many progress points this section counts for
* @throws coding_exception If max is invalid
* @throws \coding_exception If max is invalid
*/
public function start_progress($description, $max = self::INDETERMINATE,
$parentcount = 1) {
if ($max != self::INDETERMINATE && $max < 0) {
throw new coding_exception(
throw new \coding_exception(
'start_progress() max value cannot be negative');
}
if ($parentcount < 1) {
throw new coding_exception(
throw new \coding_exception(
'start_progress() parent progress count must be at least 1');
}
if (!empty($this->descriptions)) {
@@ -105,13 +109,13 @@ abstract class core_backup_progress {
if ($prevmax !== self::INDETERMINATE) {
$prevcurrent = end($this->currents);
if ($prevcurrent + $parentcount > $prevmax) {
throw new coding_exception(
throw new \coding_exception(
'start_progress() parent progress would exceed max');
}
}
} else {
if ($parentcount != 1) {
throw new coding_exception(
throw new \coding_exception(
'start_progress() progress count must be 1 when no parent');
}
}
@@ -130,11 +134,11 @@ abstract class core_backup_progress {
* If there is a parent progress section, its progress will be increased
* automatically to reflect the end of the child section.
*
* @throws coding_exception If progress hasn't been started
* @throws \coding_exception If progress hasn't been started
*/
public function end_progress() {
if (!count($this->descriptions)) {
throw new coding_exception('end_progress() without start_progress()');
throw new \coding_exception('end_progress() without start_progress()');
}
array_pop($this->descriptions);
array_pop($this->maxes);
@@ -164,7 +168,7 @@ abstract class core_backup_progress {
* INDETERMINATE. Otherwise it must not be indeterminate.
*
* @param int $progress Progress so far
* @throws coding_exception If progress value is invalid
* @throws \coding_exception If progress value is invalid
*/
public function progress($progress = self::INDETERMINATE) {
// Ignore too-frequent progress calls (more than once per second).
@@ -176,7 +180,7 @@ abstract class core_backup_progress {
// Check we are inside a progress section.
$max = end($this->maxes);
if ($max === false) {
throw new coding_exception(
throw new \coding_exception(
'progress() without start_progress');
}
@@ -184,20 +188,20 @@ abstract class core_backup_progress {
if ($progress === self::INDETERMINATE) {
// Indeterminate progress.
if ($max !== self::INDETERMINATE) {
throw new coding_exception(
throw new \coding_exception(
'progress() INDETERMINATE, expecting value');
}
} else {
// Determinate progress.
$current = end($this->currents);
if ($max === self::INDETERMINATE) {
throw new coding_exception(
throw new \coding_exception(
'progress() with value, expecting INDETERMINATE');
} else if ($progress < 0 || $progress > $max) {
throw new coding_exception(
throw new \coding_exception(
'progress() value out of range');
} else if ($progress < $current) {
throw new coding_Exception(
throw new \coding_exception(
'progress() value may not go backwards');
}
$this->currents[key($this->currents)] = $progress;
@@ -208,7 +212,7 @@ abstract class core_backup_progress {
$this->lastprogresstime = $now;
// Update time limit before next progress display.
core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
\core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
$this->update_progress();
}
@@ -236,24 +240,25 @@ abstract class core_backup_progress {
/**
* Checks max value of current progress section.
*
* @return int Current max value (may be core_backup_progress::INDETERMINATE)
* @throws coding_exception If not in a progress section
* @return int Current max value (may be \core\progress\base::INDETERMINATE)
* @throws \coding_exception If not in a progress section
*/
public function get_current_max() {
$max = end($this->maxes);
if ($max === false) {
throw new coding_exception('Not inside progress section');
throw new \coding_exception('Not inside progress section');
}
return $max;
}
/**
* @throws \coding_exception
* @return string Current progress section description
*/
public function get_current_description() {
$description = end($this->descriptions);
if ($description === false) {
throw new coding_exception('Not inside progress section');
throw new \coding_exception('Not inside progress section');
}
return $description;
}
@@ -14,23 +14,27 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\progress;
defined('MOODLE_INTERNAL') || die();
/**
* Progress handler that uses a standard Moodle progress bar to display
* progress. The Moodle progress bar cannot show indeterminate progress,
* so we do extra output in addition to the bar.
*
* @package core_backup
* @package core_progress
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_backup_display_progress extends core_backup_progress {
class display extends base {
/**
* @var int Number of wibble states (state0...stateN-1 classes in CSS)
*/
const WIBBLE_STATES = 13;
/**
* @var progress_bar Current progress bar.
* @var \progress_bar Current progress bar.
*/
private $bar;
@@ -54,8 +58,8 @@ class core_backup_display_progress extends core_backup_progress {
}
/**
* By default, the progress section names do not display because (in backup)
* these are usually untranslated and incomprehensible. To make them
* By default, the progress section names do not display because
* these will probably be untranslated and incomprehensible. To make them
* display, call this method.
*
* @param bool $displaynames True to display names
@@ -69,15 +73,15 @@ class core_backup_display_progress extends core_backup_progress {
*
* Called in constructor and in update_progress if required.
*
* @throws coding_exception If already started
* @throws \coding_exception If already started
*/
public function start_html() {
if ($this->bar) {
throw new coding_exception('Already started');
throw new \coding_exception('Already started');
}
$this->bar = new progress_bar();
$this->bar = new \progress_bar();
$this->bar->create();
echo html_writer::start_div('wibbler');
echo \html_writer::start_div('wibbler');
}
/**
@@ -92,13 +96,13 @@ class core_backup_display_progress extends core_backup_progress {
$this->bar = null;
// End wibbler div.
echo html_writer::end_div();
echo \html_writer::end_div();
}
/**
* When progress is updated, updates the bar.
*
* @see core_backup_progress::update_progress()
* @see \core\progress\base::update_progress()
*/
public function update_progress() {
// If finished...
@@ -114,7 +118,7 @@ class core_backup_display_progress extends core_backup_progress {
// (up to once per second).
if (time() != $this->lastwibble) {
$this->lastwibble = time();
echo html_writer::div('', 'wibble state' . $this->currentstate);
echo \html_writer::div('', 'wibble state' . $this->currentstate);
// Go on to next colour.
$this->currentstate += $this->direction;
@@ -14,20 +14,24 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\progress;
defined('MOODLE_INTERNAL') || die();
/**
* Progress handler that uses a standard Moodle progress bar to display
* progress. Same as core_backup_display_progress, but the bar does not
* progress. Same as \core\progress\display, but the bar does not
* appear until a certain time has elapsed, and disappears automatically
* after it finishes.
*
* The bar can be re-used, i.e. if you end all sections it will disappear,
* but if you start all sections, a new bar will be output.
*
* @package core_backup
* @package core_progress
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_backup_display_progress_if_slow extends core_backup_display_progress {
class display_if_slow extends display {
/**
* @var int Waits this many seconds before displaying progress bar
*/
@@ -57,10 +61,10 @@ class core_backup_display_progress_if_slow extends core_backup_display_progress
* Constructs the progress reporter. This will not output HTML just yet,
* until the required delay time expires.
*
* @param string $heading Text to display above bar (if it appears); '' for none
* @param string $heading Text to display above bar (if it appears); '' for none (default)
* @param int $delay Delay time (default 5 seconds)
*/
public function __construct($heading, $delay = self::DEFAULT_DISPLAY_DELAY) {
public function __construct($heading = '', $delay = self::DEFAULT_DISPLAY_DELAY) {
// Set start time based on delay.
$this->starttime = time() + $delay;
$this->heading = $heading;
@@ -71,16 +75,16 @@ class core_backup_display_progress_if_slow extends core_backup_display_progress
* Starts displaying the progress bar, with optional heading and a special
* div so it can be hidden later.
*
* @see core_backup_display_progress::start_html()
* @see \core\progress\display::start_html()
*/
public function start_html() {
global $OUTPUT;
$this->id = 'core_backup_display_progress_if_slow' . self::$nextid;
$this->id = 'core_progress_display_if_slow' . self::$nextid;
self::$nextid++;
// Containing div includes a CSS class so that it can be themed if required,
// and an id so it can be automatically hidden at end.
echo html_writer::start_div('core_backup_display_progress_if_slow',
echo \html_writer::start_div('core_progress_display_if_slow',
array('id' => $this->id));
// Display optional heading.
@@ -96,7 +100,7 @@ class core_backup_display_progress_if_slow extends core_backup_display_progress
* When progress is updated, after a certain time, starts actually displaying
* the progress bar.
*
* @see core_backup_progress::update_progress()
* @see \core\progress\base::update_progress()
*/
public function update_progress() {
// If we haven't started yet, consider starting.
@@ -116,12 +120,12 @@ class core_backup_display_progress_if_slow extends core_backup_display_progress
/**
* Finishes parent display then closes div and hides it.
*
* @see core_backup_display_progress::end_html()
* @see \core\progress\display::end_html()
*/
public function end_html() {
parent::end_html();
echo html_writer::end_div();
echo html_writer::script('document.getElementById("' . $this->id .
echo \html_writer::end_div();
echo \html_writer::script('document.getElementById("' . $this->id .
'").style.display = "none"');
}
}
@@ -14,14 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\progress;
defined('MOODLE_INTERNAL') || die();
/**
* Progress handler that ignores progress entirely.
*
* @package core_backup
* @package core_progress
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_backup_null_progress extends core_backup_progress {
class null extends base {
public function update_progress() {
// Do nothing.
}
@@ -17,7 +17,7 @@
/**
* Unit tests for the progress classes.
*
* @package core_backup
* @package core_progress
* @category phpunit
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -25,20 +25,16 @@
defined('MOODLE_INTERNAL') || die();
// Include all the needed stuff.
global $CFG;
require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
/**
* Progress tests.
*/
class backup_progress_testcase extends basic_testcase {
class core_progress_testcase extends basic_testcase {
/**
* Tests for basic use with simple numeric progress.
*/
public function test_basic() {
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
// Check values of empty progress things.
$this->assertFalse($progress->is_in_progress_section());
@@ -59,7 +55,7 @@ class backup_progress_testcase extends basic_testcase {
core_php_time_limit::get_and_clear_unit_test_data();
$progress->progress(2);
$this->assertTrue($progress->was_update_called());
$this->assertEquals(array(core_backup_progress::TIME_LIMIT_WITHOUT_PROGRESS),
$this->assertEquals(array(\core\progress\base::TIME_LIMIT_WITHOUT_PROGRESS),
core_php_time_limit::get_and_clear_unit_test_data());
// Check the new value.
@@ -86,7 +82,7 @@ class backup_progress_testcase extends basic_testcase {
*/
public function test_nested() {
// Outer progress goes from 0 to 10.
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
$progress->start_progress('hello', 10);
// Get up to 4, check position.
@@ -163,7 +159,7 @@ class backup_progress_testcase extends basic_testcase {
* Tests the feature for 'weighting' nested progress.
*/
public function test_nested_weighted() {
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
$progress->start_progress('', 10);
// First nested child has 2 units of its own and is worth 1 unit.
@@ -183,7 +179,7 @@ class backup_progress_testcase extends basic_testcase {
$this->assert_min_max(0.4, 0.4, $progress);
// Next indeterminate child is worth 6 units.
$progress->start_progress('', core_backup_progress::INDETERMINATE, 6);
$progress->start_progress('', \core\progress\base::INDETERMINATE, 6);
$progress->step_time();
$progress->progress();
$this->assert_min_max(0.4, 1.0, $progress);
@@ -196,7 +192,7 @@ class backup_progress_testcase extends basic_testcase {
* to be similar.
*/
public function test_realistic() {
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
$progress->start_progress('parent', 100);
$progress->start_progress('child', 1);
$progress->progress(1);
@@ -210,7 +206,7 @@ class backup_progress_testcase extends basic_testcase {
* zero entries.
*/
public function test_zero() {
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
$progress->start_progress('parent', 100);
$progress->progress(1);
$this->assert_min_max(0.01, 0.01, $progress);
@@ -229,7 +225,7 @@ class backup_progress_testcase extends basic_testcase {
* Tests for any exceptions due to invalid calls.
*/
public function test_exceptions() {
$progress = new core_backup_mock_progress();
$progress = new core_mock_progress();
// Check errors when empty.
try {
@@ -268,7 +264,7 @@ class backup_progress_testcase extends basic_testcase {
// Indeterminate when value expected.
$progress->start_progress('hello', 10);
try {
$progress->progress(core_backup_progress::INDETERMINATE);
$progress->progress(\core\progress\base::INDETERMINATE);
$this->fail();
} catch (coding_exception $e) {
$this->assertEquals(1, preg_match('~expecting value~', $e->getMessage()));
@@ -326,9 +322,9 @@ class backup_progress_testcase extends basic_testcase {
*
* @param number $min Expected min progress
* @param number $max Expected max progress
* @param core_backup_mock_progress $progress
* @param core_mock_progress $progress
*/
private function assert_min_max($min, $max, core_backup_mock_progress $progress) {
private function assert_min_max($min, $max, core_mock_progress $progress) {
$this->assertEquals(array($min, $max),
$progress->get_progress_proportion_range());
}
@@ -338,7 +334,7 @@ class backup_progress_testcase extends basic_testcase {
* Helper class that records when update_progress is called and allows time
* stepping.
*/
class core_backup_mock_progress extends core_backup_progress {
class core_mock_progress extends \core\progress\base {
private $updatecalled = false;
private $time = 1;
@@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace quiz_statistics;
defined('MOODLE_INTERNAL') || die();
/**
* The statistics calculator returns an instance of this class which contains the calculated statistics.
*
@@ -26,7 +30,7 @@
* @author James Pratt [email protected]
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class quiz_statistics_calculated {
class calculated {
/**
* @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
@@ -110,7 +114,7 @@ class quiz_statistics_calculated {
* @return int|float
*/
protected function get_field($field) {
$fieldname = quiz_statistics_calculator::using_attempts_string_id($this->whichattempts).$field;
$fieldname = calculator::using_attempts_string_id($this->whichattempts).$field;
return $this->{$fieldname};
}
@@ -182,7 +186,7 @@ class quiz_statistics_calculated {
}
$quizinfo[get_string($property, 'quiz_statistics',
quiz_statistics_calculator::using_attempts_lang_string($this->whichattempts))] = $formattedvalue;
calculator::using_attempts_lang_string($this->whichattempts))] = $formattedvalue;
}
return $quizinfo;
@@ -199,12 +203,12 @@ class quiz_statistics_calculated {
/**
* Cache the stats contained in this class.
*
* @param $qubaids qubaid_condition
* @param $qubaids \qubaid_condition
*/
public function cache($qubaids) {
global $DB;
$toinsert = new stdClass();
$toinsert = new \stdClass();
foreach ($this->fieldsindb as $field) {
$toinsert->{$field} = $this->{$field};
@@ -229,7 +233,7 @@ class quiz_statistics_calculated {
/**
* Given a record from 'quiz_statistics' table load the data into the properties of this class.
*
* @param $record from db.
* @param $record \stdClass from db.
*/
public function populate_from_record($record) {
foreach ($this->fieldsindb as $field) {
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace quiz_statistics;
defined('MOODLE_INTERNAL') || die();
/**
* Class to calculate and also manage caching of quiz statistics.
*
@@ -26,7 +29,19 @@
* @author James Pratt [email protected]
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class quiz_statistics_calculator {
class calculator {
/**
* @var \core\progress\base
*/
protected $progress;
public function __construct(\core\progress\base $progress = null) {
if ($progress === null) {
$progress = new \core\progress\null();
}
$this->progress = $progress;
}
/**
* Compute the quiz statistics.
@@ -39,69 +54,70 @@ class quiz_statistics_calculator {
* @param array $groupstudents 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 quiz_statistics_calculated $quizstats The statistics for overall attempt scores.
* @return calculated $quizstats The statistics for overall attempt scores.
*/
public function calculate($quizid, $whichattempts, $groupstudents, $p, $sumofmarkvariance) {
$this->progress->start_progress('', 3);
$quizstats = new quiz_statistics_calculated($whichattempts);
$quizstats = new calculated($whichattempts);
$countsandaverages = $this->attempt_counts_and_averages($quizid, $groupstudents);
$this->progress->progress(1);
foreach ($countsandaverages as $propertyname => $value) {
$quizstats->{$propertyname} = $value;
}
$s = $quizstats->s();
if ($s != 0) {
if ($s == 0) {
return $quizstats;
}
// Recalculate sql again this time possibly including test for first attempt.
list($fromqa, $whereqa, $qaparams) =
quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts);
// Recalculate sql again this time possibly including test for first attempt.
list($fromqa, $whereqa, $qaparams) =
quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts);
$quizstats->median = $this->median($s, $fromqa, $whereqa, $qaparams);
$this->progress->progress(2);
$quizstats->median = $this->median($s, $fromqa, $whereqa, $qaparams);
if ($s > 1) {
if ($s > 1) {
$powers = $this->sum_of_powers_of_difference_to_mean($quizstats->avg(), $fromqa, $whereqa, $qaparams);
$this->progress->progress(3);
$powers = $this->sum_of_powers_of_difference_to_mean($quizstats->avg(), $fromqa, $whereqa, $qaparams);
$quizstats->standarddeviation = sqrt($powers->power2 / ($s - 1));
$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;
// 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));
$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);
}
// 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;
}
}
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, $groupstudents, $whichattempts));
}
$quizstats->cache(quiz_statistics_qubaids_condition($quizid, $groupstudents, $whichattempts));
$this->progress->end_progress();
return $quizstats;
}
@@ -111,8 +127,8 @@ class quiz_statistics_calculator {
/**
* Load cached statistics from the database.
*
* @param $qubaids qubaid_condition
* @return quiz_statistics_calculated The statistics for overall attempt scores or false if not cached.
* @param $qubaids \qubaid_condition
* @return calculated The statistics for overall attempt scores or false if not cached.
*/
public function get_cached($qubaids) {
global $DB;
@@ -120,7 +136,7 @@ class quiz_statistics_calculator {
$timemodified = time() - self::TIME_TO_CACHE;
$fromdb = $DB->get_record_select('quiz_statistics', 'hashcode = ? AND timemodified > ?',
array($qubaids->get_hash_code(), $timemodified));
$stats = new quiz_statistics_calculated();
$stats = new calculated();
$stats->populate_from_record($fromdb);
return $stats;
}
@@ -128,7 +144,7 @@ class quiz_statistics_calculator {
/**
* Find time of non-expired statistics in the database.
*
* @param $qubaids qubaid_condition
* @param $qubaids \qubaid_condition
* @return integer|boolean Time of cached record that matches this qubaid_condition or false is non found.
*/
public function get_last_calculated_time($qubaids) {
@@ -188,12 +204,12 @@ class quiz_statistics_calculator {
* #Calculating_MEAN_of_grades_for_all_attempts_by_students
* @param int $quizid
* @param array $groupstudents
* @return stdClass with properties with count and avg with prefixes firstattempts, highestattempts, etc.
* @return \stdClass with properties with count and avg with prefixes firstattempts, highestattempts, etc.
*/
protected function attempt_counts_and_averages($quizid, $groupstudents) {
global $DB;
$attempttotals = new stdClass();
$attempttotals = new \stdClass();
foreach (array_keys(quiz_get_grading_options()) as $which) {
list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $groupstudents, $which);
@@ -33,6 +33,7 @@ $string['attemptsall'] = 'all attempts';
$string['attemptsfirst'] = 'first attempt';
$string['backtoquizreport'] = 'Back to main statistics report page.';
$string['calculatefrom'] = 'Calculate statistics from';
$string['calculatingallstats'] = 'Calculating statistics for quiz, questions and analysing repsonse data';
$string['cic'] = 'Coefficient of internal consistency (for {$a})';
$string['completestatsfilename'] = 'completestats';
$string['count'] = 'Count';
+78 -15
View File
@@ -46,11 +46,16 @@ class quiz_statistics_report extends quiz_default_report {
/** @var quiz_statistics_table instance of table class used for main questions stats table. */
protected $table;
/** @var \core\progress\base|null $progress Handles progress reporting or not. */
protected $progress = null;
/**
* Display the report.
*/
public function display($quiz, $cm, $course) {
global $CFG, $DB, $OUTPUT, $PAGE;
global $OUTPUT;
raise_memory_limit(MEMORY_HUGE);
$this->context = context_module::instance($cm->id);
@@ -130,13 +135,19 @@ class quiz_statistics_report extends quiz_default_report {
get_string('quizstructureanalysis', 'quiz_statistics'));
$questions = $this->load_and_initialise_questions_for_calculations($quiz);
// Print the page header stuff (if not downloading.
if (!$this->table->is_downloading()) {
$this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
}
if (!$nostudentsingroup) {
// Get the data to be displayed.
$progress = $this->get_progress_trace_instance();
list($quizstats, $questionstats, $subquestionstats) =
$this->get_quiz_and_questions_stats($quiz, $whichattempts, $groupstudents, $questions);
$this->get_all_stats_and_analysis($quiz, $whichattempts, $groupstudents, $questions, $progress);
} else {
// Or create empty stats containers.
$quizstats = new quiz_statistics_calculated($whichattempts);
$quizstats = new \quiz_statistics\calculated($whichattempts);
$questionstats = array();
$subquestionstats = array();
}
@@ -146,9 +157,8 @@ class quiz_statistics_report extends quiz_default_report {
$this->table->statistics_setup($quiz, $cm->id, $reporturl, $quizstats->s());
}
// Print the page header stuff (if not downloading.
// Print the rest of the page header stuff (if not downloading.
if (!$this->table->is_downloading()) {
$this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
if (groups_get_activity_groupmode($cm)) {
groups_print_activity_menu($cm, $reporturl->out());
@@ -488,36 +498,46 @@ class quiz_statistics_report extends quiz_default_report {
* Get the quiz and question statistics, either by loading the cached results,
* or by recomputing them.
*
* @param object $quiz the quiz settings.
* @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
* @param object $quiz the quiz settings.
* @param string $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 array $groupstudents students in this group.
* @param array $questions full question data.
* @param array $groupstudents students in this group.
* @param array $questions full question data.
* @return array with 4 elements:
* - $quizstats The statistics for overall attempt scores.
* - $questionstats array of \core_question\statistics\questions\calculated objects keyed by slot.
* - $subquestionstats array of \core_question\statistics\questions\calculated_for_subquestion objects keyed by question id.
*/
public function get_quiz_and_questions_stats($quiz, $whichattempts, $groupstudents, $questions) {
public function get_all_stats_and_analysis($quiz, $whichattempts, $groupstudents, $questions, $progress = null) {
if ($progress === null) {
$progress = new \core\progress\null();
}
$qubaids = quiz_statistics_qubaids_condition($quiz->id, $groupstudents, $whichattempts);
$qcalc = new \core_question\statistics\questions\calculator($questions);
$qcalc = new \core_question\statistics\questions\calculator($questions, $progress);
$quizcalc = new quiz_statistics_calculator();
$quizcalc = new \quiz_statistics\calculator($progress);
if ($quizcalc->get_last_calculated_time($qubaids) === false) {
$progress->start_progress('', 3);
// Recalculate now.
list($questionstats, $subquestionstats) = $qcalc->calculate($qubaids);
$progress->progress(1);
$quizstats = $quizcalc->calculate($quiz->id, $whichattempts, $groupstudents, count($questions),
$qcalc->get_sum_of_mark_variance());
$progress->progress(2);
if ($quizstats->s()) {
$this->analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats);
$this->analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats, $progress);
}
$progress->progress(3);
$progress->end_progress();
} else {
$quizstats = $quizcalc->get_cached($qubaids);
list($questionstats, $subquestionstats) = $qcalc->get_cached($qubaids);
@@ -526,10 +546,41 @@ class quiz_statistics_report extends quiz_default_report {
return array($quizstats, $questionstats, $subquestionstats);
}
protected function analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats) {
/**
* 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\null();
}
}
return $this->progress;
}
protected function analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats,
$progress = null) {
if ($progress === null) {
$progress = new \core\progress\null();
}
// Starting response analysis tasks.
$progress->start_progress('', count($questions) + count($subquestionstats));
// Starting response analysis of main questions.
$progress->start_progress('', count($questions), count($questions));
$done = array();
$donecount = 1;
foreach ($questions as $question) {
$progress->progress($donecount);
$donecount++;
if (!question_bank::get_qtype($question->qtype, false)->can_analyse_responses()) {
continue;
}
@@ -538,8 +589,15 @@ class quiz_statistics_report extends quiz_default_report {
$responesstats = new \core_question\statistics\responses\analyser($question);
$responesstats->calculate($qubaids);
}
$progress->end_progress();
// Starting response analysis of sub-questions.
$countsubquestions = count($subquestionstats);
$progress->start_progress('', $countsubquestions, $countsubquestions);
$donecount = 1;
foreach ($subquestionstats as $subquestionstat) {
$progress->progress($donecount);
$donecount++;
if (!question_bank::get_qtype($subquestionstat->question->qtype, false)->can_analyse_responses() ||
isset($done[$subquestionstat->question->id])) {
continue;
@@ -549,6 +607,11 @@ class quiz_statistics_report extends quiz_default_report {
$responesstats = new \core_question\statistics\responses\analyser($subquestionstat->question);
$responesstats->calculate($qubaids);
}
// Finished sub-question tasks.
$progress->end_progress();
// Finished all response analysis tasks.
$progress->end_progress();
}
/**
@@ -40,7 +40,7 @@ class quiz_statistics_settings_form extends moodleform {
$options = array();
foreach (array_keys(quiz_get_grading_options()) as $which) {
$options[$which] = \quiz_statistics_calculator::using_attempts_lang_string($which);
$options[$which] = \quiz_statistics\calculator::using_attempts_lang_string($which);
}
$mform->addElement('select', 'whichattempts', get_string('calculatefrom', 'quiz_statistics'), $options);
@@ -81,13 +81,13 @@ class quiz_report_statistics_from_steps_testcase extends mod_quiz_attempt_walkth
$groupstudents = array();
$questions = $this->report->load_and_initialise_questions_for_calculations($this->quiz);
list($quizstats, $questionstats, $subquestionstats) =
$this->report->get_quiz_and_questions_stats($this->quiz, $whichattempts, $groupstudents, $questions);
$this->report->get_all_stats_and_analysis($this->quiz, $whichattempts, $groupstudents, $questions);
$qubaids = quiz_statistics_qubaids_condition($this->quiz->id, $groupstudents, $whichattempts);
// We will create some quiz and question stat calculator instances and some response analyser instances, just in order
// to check the time of the
$quizcalc = new quiz_statistics_calculator();
$quizcalc = new \quiz_statistics\calculator();
// Should not be a delay of more than one second between the calculation of stats above and here.
$this->assertTimeCurrent($quizcalc->get_last_calculated_time($qubaids));
@@ -53,13 +53,25 @@ class calculator {
protected $randomselectors = array();
/**
* @var \progress_trace
*/
protected $progress;
/**
* Constructor.
*
* @param object[] questions to analyze, keyed by slot, also analyses sub questions for random questions.
* we expect some extra fields - slot, maxmark and number on the full question data objects.
* @param \core\progress\base|null $progress the element to send progress messages to, default is {@link \core\progress\null}.
*/
public function __construct($questions) {
public function __construct($questions, $progress = null) {
if ($progress === null) {
$progress = new \core\progress\null();
}
$this->progress = $progress;
foreach ($questions as $slot => $question) {
$this->questionstats[$slot] = new calculated();
$this->questionstats[$slot]->questionid = $question->id;
@@ -76,15 +88,19 @@ class calculator {
* @return array containing two arrays calculated[] and calculated_for_subquestion[].
*/
public function calculate($qubaids) {
\core_php_time_limit::raise();
$this->progress->start_progress('', 6);
list($lateststeps, $summarks) = $this->get_latest_steps($qubaids);
if ($lateststeps) {
$this->progress->start_progress('', count($lateststeps), 1);
// Compute the statistics of position, and for random questions, work
// out which questions appear in which positions.
$countdone = 1;
foreach ($lateststeps as $step) {
$this->progress->progress($countdone);
$countdone++;
$this->initial_steps_walker($step, $this->questionstats[$step->slot], $summarks);
// If this is a random question what is the real item being used?
@@ -110,6 +126,7 @@ class calculator {
$this->randomselectors[$randomselectorstring][$step->questionid] = $step->questionid;
}
}
$this->progress->end_progress();
foreach ($this->randomselectors as $key => $notused) {
ksort($this->randomselectors[$key]);
@@ -117,7 +134,11 @@ class calculator {
// Compute the statistics of question id, if we need any.
$subquestions = question_load_questions(array_keys($this->subquestionstats));
$this->progress->start_progress('', count($subquestions), 1);
$countdone = 1;
foreach ($subquestions as $qid => $subquestion) {
$this->progress->progress($countdone);
$countdone++;
$this->subquestionstats[$qid]->question = $subquestion;
$this->subquestionstats[$qid]->question->maxmark = $this->subquestionstats[$qid]->maxmark;
$this->subquestionstats[$qid]->randomguessscore = $this->get_random_guess_score($subquestion);
@@ -139,6 +160,7 @@ class calculator {
$this->subquestionstats[$qid]->positions = '';
}
}
$this->progress->end_progress();
// Finish computing the averages, and put the subquestion data into the
// corresponding questions.
@@ -147,7 +169,11 @@ class calculator {
// $question and $nextquestion available, but apart from that it is
// foreach ($this->questions as $qid => $question).
reset($this->questionstats);
$this->progress->start_progress('', count($this->questionstats), 1);
$countdone = 1;
while (list($slot, $questionstat) = each($this->questionstats)) {
$this->progress->progress($countdone);
$countdone++;
$nextquestionstats = current($this->questionstats);
$this->initial_question_walker($questionstat);
@@ -166,18 +192,28 @@ class calculator {
}
}
}
$this->progress->end_progress();
// Go through the records one more time.
$this->progress->start_progress('', count($lateststeps), 1);
$countdone = 1;
foreach ($lateststeps as $step) {
$this->progress->progress($countdone);
$countdone++;
$this->secondary_steps_walker($step, $this->questionstats[$step->slot], $summarks);
if ($this->questionstats[$step->slot]->subquestions) {
$this->secondary_steps_walker($step, $this->subquestionstats[$step->questionid], $summarks);
}
}
$this->progress->end_progress();
$this->progress->start_progress('', count($this->questionstats), 1);
$sumofcovariancewithoverallmark = 0;
$countdone = 1;
foreach ($this->questionstats as $questionstat) {
$this->progress->progress($countdone);
$countdone++;
$this->secondary_question_walker($questionstat);
$this->sumofmarkvariance += $questionstat->markvariance;
@@ -186,10 +222,16 @@ class calculator {
$sumofcovariancewithoverallmark += sqrt($questionstat->covariancewithoverallmark);
}
}
$this->progress->end_progress();
$this->progress->start_progress('', count($this->subquestionstats), 1);
$countdone = 1;
foreach ($this->subquestionstats as $subquestionstat) {
$this->progress->progress($countdone);
$countdone++;
$this->secondary_question_walker($subquestionstat);
}
$this->progress->end_progress();
foreach ($this->questionstats as $questionstat) {
if ($sumofcovariancewithoverallmark) {
@@ -204,6 +246,9 @@ class calculator {
}
}
$this->cache_stats($qubaids);
// All finished.
$this->progress->end_progress();
}
return array($this->questionstats, $this->subquestionstats);
}