From 8da6fc9d57d1046dc196959051b949c9c3ed8c79 Mon Sep 17 00:00:00 2001 From: James Pratt Date: Tue, 19 Nov 2013 20:07:05 +0700 Subject: [PATCH] MDL-41754 (2) quiz statistics : using Sam's new progress classes --- lib/classes/progress/display_if_slow.php | 4 +- .../report/statistics/classes/calculated.php | 16 ++- .../report/statistics/classes/calculator.php | 122 ++++++++++-------- .../statistics/lang/en/quiz_statistics.php | 1 + mod/quiz/report/statistics/report.php | 93 ++++++++++--- .../report/statistics/statistics_form.php | 2 +- .../stats_from_steps_walkthrough_test.php | 4 +- .../statistics/questions/calculator.php | 51 +++++++- 8 files changed, 211 insertions(+), 82 deletions(-) diff --git a/lib/classes/progress/display_if_slow.php b/lib/classes/progress/display_if_slow.php index c0833003b86..7323f965522 100644 --- a/lib/classes/progress/display_if_slow.php +++ b/lib/classes/progress/display_if_slow.php @@ -61,10 +61,10 @@ class display_if_slow extends display { * 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; diff --git a/mod/quiz/report/statistics/classes/calculated.php b/mod/quiz/report/statistics/classes/calculated.php index 3f5b6e64fb6..223589bb814 100644 --- a/mod/quiz/report/statistics/classes/calculated.php +++ b/mod/quiz/report/statistics/classes/calculated.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +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 me@jamiep.org * @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) { diff --git a/mod/quiz/report/statistics/classes/calculator.php b/mod/quiz/report/statistics/classes/calculator.php index 3ad6dac3033..0e7d127032d 100644 --- a/mod/quiz/report/statistics/classes/calculator.php +++ b/mod/quiz/report/statistics/classes/calculator.php @@ -14,6 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace quiz_statistics; +defined('MOODLE_INTERNAL') || die(); + /** * Class to calculate and also manage caching of quiz statistics. * @@ -26,7 +29,19 @@ * @author James Pratt me@jamiep.org * @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) { - return $quizstats; - } - - // 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); - - if ($s > 1) { - - $powers = $this->sum_of_powers_of_difference_to_mean($quizstats->avg(), $fromqa, $whereqa, $qaparams); - - $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 ($s != 0) { + + // 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); + + 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; + } } - 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); diff --git a/mod/quiz/report/statistics/lang/en/quiz_statistics.php b/mod/quiz/report/statistics/lang/en/quiz_statistics.php index 11ea4ee9b59..0a18886da03 100644 --- a/mod/quiz/report/statistics/lang/en/quiz_statistics.php +++ b/mod/quiz/report/statistics/lang/en/quiz_statistics.php @@ -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'; diff --git a/mod/quiz/report/statistics/report.php b/mod/quiz/report/statistics/report.php index 86093350991..4982223a2dd 100644 --- a/mod/quiz/report/statistics/report.php +++ b/mod/quiz/report/statistics/report.php @@ -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(); } /** diff --git a/mod/quiz/report/statistics/statistics_form.php b/mod/quiz/report/statistics/statistics_form.php index 18071eb09a5..114ae32a7c4 100644 --- a/mod/quiz/report/statistics/statistics_form.php +++ b/mod/quiz/report/statistics/statistics_form.php @@ -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); diff --git a/mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php b/mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php index eb1be288bd0..e712aab30ec 100644 --- a/mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php +++ b/mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php @@ -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)); diff --git a/question/classes/statistics/questions/calculator.php b/question/classes/statistics/questions/calculator.php index a3e9d98ff99..8fa439276d6 100644 --- a/question/classes/statistics/questions/calculator.php +++ b/question/classes/statistics/questions/calculator.php @@ -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); } -- 2.43.0