|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00027 defined('MOODLE_INTERNAL') || die(); 00028 00029 00034 function tool_qeupgradehelper_is_upgraded() { 00035 global $CFG, $DB; 00036 $dbman = $DB->get_manager(); 00037 return is_readable($CFG->dirroot . '/question/engine/upgrade/upgradelib.php') && 00038 $dbman->table_exists('question_usages'); 00039 } 00040 00044 function tool_qeupgradehelper_require_upgraded() { 00045 if (!tool_qeupgradehelper_is_upgraded()) { 00046 throw new moodle_exception('upgradedsiterequired', 'tool_qeupgradehelper', 00047 tool_qeupgradehelper_url('index')); 00048 } 00049 } 00050 00054 function tool_qeupgradehelper_require_not_upgraded() { 00055 if (tool_qeupgradehelper_is_upgraded()) { 00056 throw new moodle_exception('notupgradedsiterequired', 'tool_qeupgradehelper', 00057 tool_qeupgradehelper_url('index')); 00058 } 00059 } 00060 00066 function tool_qeupgradehelper_url($script, $params = array()) { 00067 return new moodle_url('/admin/tool/qeupgradehelper/' . $script . '.php', $params); 00068 } 00069 00070 00077 class tool_qeupgradehelper_action { 00079 public $name; 00081 public $url; 00083 public $description; 00084 00088 protected function __construct($name, moodle_url $url, $description) { 00089 $this->name = $name; 00090 $this->url = $url; 00091 $this->description = $description; 00092 } 00093 00100 public static function make($shortname, $params = array()) { 00101 return new self( 00102 get_string($shortname, 'tool_qeupgradehelper'), 00103 tool_qeupgradehelper_url($shortname, $params), 00104 get_string($shortname . '_desc', 'tool_qeupgradehelper')); 00105 } 00106 } 00107 00108 00116 abstract class tool_qeupgradehelper_quiz_list { 00117 public $title; 00118 public $intro; 00119 public $quizacolheader; 00120 public $sql; 00121 public $quizlist = null; 00122 public $totalquizas = 0; 00123 public $totalqas = 0; 00124 00125 protected function __construct($title, $intro, $quizacolheader) { 00126 global $DB; 00127 $this->title = get_string($title, 'tool_qeupgradehelper'); 00128 $this->intro = get_string($intro, 'tool_qeupgradehelper'); 00129 $this->quizacolheader = get_string($quizacolheader, 'tool_qeupgradehelper'); 00130 $this->build_sql(); 00131 $this->quizlist = $DB->get_records_sql($this->sql); 00132 } 00133 00134 protected function build_sql() { 00135 $this->sql = ' 00136 SELECT 00137 quiz.id, 00138 quiz.name, 00139 c.shortname, 00140 c.id AS courseid, 00141 COUNT(1) AS attemptcount, 00142 SUM(qsesscounts.num) AS questionattempts 00143 00144 FROM {quiz_attempts} quiza 00145 JOIN {quiz} quiz ON quiz.id = quiza.quiz 00146 JOIN {course} c ON c.id = quiz.course 00147 LEFT JOIN ( 00148 SELECT attemptid, COUNT(1) AS num 00149 FROM {question_sessions} 00150 GROUP BY attemptid 00151 ) qsesscounts ON qsesscounts.attemptid = quiza.uniqueid 00152 00153 WHERE quiza.preview = 0 00154 ' . $this->extra_where_clause() . ' 00155 00156 GROUP BY quiz.id, quiz.name, c.shortname, c.id 00157 00158 ORDER BY c.shortname, quiz.name, quiz.id'; 00159 } 00160 00161 abstract protected function extra_where_clause(); 00162 00163 public function get_col_headings() { 00164 return array( 00165 get_string('quizid', 'tool_qeupgradehelper'), 00166 get_string('course'), 00167 get_string('pluginname', 'quiz'), 00168 $this->quizacolheader, 00169 get_string('questionsessions', 'tool_qeupgradehelper'), 00170 ); 00171 } 00172 00173 public function get_row($quizinfo) { 00174 $this->totalquizas += $quizinfo->attemptcount; 00175 $this->totalqas += $quizinfo->questionattempts; 00176 return array( 00177 $quizinfo->id, 00178 html_writer::link(new moodle_url('/course/view.php', 00179 array('id' => $quizinfo->courseid)), format_string($quizinfo->shortname)), 00180 html_writer::link(new moodle_url('/mod/quiz/view.php', 00181 array('q' => $quizinfo->id)), format_string($quizinfo->name)), 00182 $quizinfo->attemptcount, 00183 $quizinfo->questionattempts ? $quizinfo->questionattempts : 0, 00184 ); 00185 } 00186 00187 public function get_row_class($quizinfo) { 00188 return null; 00189 } 00190 00191 public function get_total_row() { 00192 return array( 00193 '', 00194 html_writer::tag('b', get_string('total')), 00195 '', 00196 html_writer::tag('b', $this->totalquizas), 00197 html_writer::tag('b', $this->totalqas), 00198 ); 00199 } 00200 00201 public function is_empty() { 00202 return empty($this->quizlist); 00203 } 00204 } 00205 00206 00213 class tool_qeupgradehelper_upgradable_quiz_list extends tool_qeupgradehelper_quiz_list { 00214 public function __construct() { 00215 parent::__construct('quizzeswithunconverted', 'listtodointro', 'attemptstoconvert'); 00216 } 00217 00218 protected function extra_where_clause() { 00219 return 'AND quiza.needsupgradetonewqe = 1'; 00220 } 00221 00222 public function get_col_headings() { 00223 $headings = parent::get_col_headings(); 00224 $headings[] = get_string('action', 'tool_qeupgradehelper'); 00225 return $headings; 00226 } 00227 00228 public function get_row($quizinfo) { 00229 $row = parent::get_row($quizinfo); 00230 $row[] = html_writer::link(tool_qeupgradehelper_url('convertquiz', array('quizid' => $quizinfo->id)), 00231 get_string('convertquiz', 'tool_qeupgradehelper')); 00232 return $row; 00233 } 00234 } 00235 00236 00243 class tool_qeupgradehelper_resettable_quiz_list extends tool_qeupgradehelper_quiz_list { 00244 public function __construct() { 00245 parent::__construct('quizzesthatcanbereset', 'listupgradedintro', 'convertedattempts'); 00246 } 00247 00248 protected function extra_where_clause() { 00249 return 'AND quiza.needsupgradetonewqe = 0 00250 AND EXISTS(SELECT 1 FROM {question_states} 00251 WHERE attempt = quiza.uniqueid)'; 00252 } 00253 00254 public function get_col_headings() { 00255 $headings = parent::get_col_headings(); 00256 $headings[] = get_string('action', 'tool_qeupgradehelper'); 00257 return $headings; 00258 } 00259 00260 public function get_row($quizinfo) { 00261 $row = parent::get_row($quizinfo); 00262 $row[] = html_writer::link(tool_qeupgradehelper_url('resetquiz', array('quizid' => $quizinfo->id)), 00263 get_string('resetquiz', 'tool_qeupgradehelper')); 00264 return $row; 00265 } 00266 } 00267 00268 00275 class tool_qeupgradehelper_pre_upgrade_quiz_list extends tool_qeupgradehelper_quiz_list { 00276 public function __construct() { 00277 parent::__construct('quizzestobeupgraded', 'listpreupgradeintro', 'numberofattempts'); 00278 } 00279 00280 protected function extra_where_clause() { 00281 return ''; 00282 } 00283 } 00284 00285 00293 class tool_qeupgradehelper_pre_upgrade_quiz_list_restricted extends tool_qeupgradehelper_pre_upgrade_quiz_list { 00294 protected $quizids; 00295 protected $restrictedtotalquizas = 0; 00296 protected $restrictedtotalqas = 0; 00297 00298 public function __construct($quizids) { 00299 parent::__construct(); 00300 $this->quizids = $quizids; 00301 } 00302 00303 public function get_row_class($quizinfo) { 00304 if (!in_array($quizinfo->id, $this->quizids)) { 00305 return 'dimmed'; 00306 } else { 00307 return parent::get_row_class($quizinfo); 00308 } 00309 } 00310 00311 public function get_col_headings() { 00312 $headings = parent::get_col_headings(); 00313 $headings[] = get_string('includedintheupgrade', 'tool_qeupgradehelper'); 00314 return $headings; 00315 } 00316 00317 public function get_row($quizinfo) { 00318 $row = parent::get_row($quizinfo); 00319 if (in_array($quizinfo->id, $this->quizids)) { 00320 $this->restrictedtotalquizas += $quizinfo->attemptcount; 00321 $this->restrictedtotalqas += $quizinfo->questionattempts; 00322 $row[] = get_string('yes'); 00323 } else { 00324 $row[] = get_string('no'); 00325 } 00326 return $row; 00327 } 00328 00329 protected function out_of($restrictedtotal, $fulltotal) { 00330 $a = new stdClass(); 00331 $a->some = $a->some = html_writer::tag('b', $restrictedtotal); 00332 $a->total = $fulltotal; 00333 return get_string('outof', 'tool_qeupgradehelper', $a); 00334 } 00335 00336 public function get_total_row() { 00337 return array( 00338 '', 00339 html_writer::tag('b', get_string('total')), 00340 '', 00341 $this->out_of($this->restrictedtotalquizas, $this->totalquizas), 00342 $this->out_of($this->restrictedtotalqas, $this->totalqas), 00343 ); 00344 } 00345 } 00346 00347 00352 function tool_qeupgradehelper_get_num_very_old_attempts() { 00353 global $DB; 00354 return $DB->count_records_sql(' 00355 SELECT COUNT(1) 00356 FROM {quiz_attempts} quiza 00357 WHERE uniqueid IN ( 00358 SELECT DISTINCT qst.attempt 00359 FROM {question_states} qst 00360 LEFT JOIN {question_sessions} qsess ON 00361 qst.question = qsess.questionid AND qst.attempt = qsess.attemptid 00362 WHERE qsess.id IS NULL)'); 00363 } 00364 00371 function tool_qeupgradehelper_get_quiz($quizid) { 00372 global $DB; 00373 return $DB->get_record_sql(" 00374 SELECT 00375 quiz.id, 00376 quiz.name, 00377 c.shortname, 00378 c.id AS courseid, 00379 COUNT(1) AS numtoconvert 00380 00381 FROM {quiz_attempts} quiza 00382 JOIN {quiz} quiz ON quiz.id = quiza.quiz 00383 JOIN {course} c ON c.id = quiz.course 00384 00385 WHERE quiza.preview = 0 00386 AND quiza.needsupgradetonewqe = 1 00387 AND quiz.id = ? 00388 00389 GROUP BY quiz.id, quiz.name, c.shortname, c.id 00390 00391 ORDER BY c.shortname, quiz.name, quiz.id", array($quizid)); 00392 } 00393 00401 function tool_qeupgradehelper_get_resettable_quiz($quizid) { 00402 global $DB; 00403 return $DB->get_record_sql(" 00404 SELECT 00405 quiz.id, 00406 quiz.name, 00407 c.shortname, 00408 c.id AS courseid, 00409 COUNT(1) AS totalattempts, 00410 SUM(CASE WHEN quiza.needsupgradetonewqe = 0 AND 00411 oldtimemodified.time IS NOT NULL THEN 1 ELSE 0 END) AS convertedattempts, 00412 SUM(CASE WHEN quiza.needsupgradetonewqe = 0 AND 00413 newtimemodified.time IS NULL OR oldtimemodified.time >= newtimemodified.time 00414 THEN 1 ELSE 0 END) AS resettableattempts 00415 00416 FROM {quiz_attempts} quiza 00417 JOIN {quiz} quiz ON quiz.id = quiza.quiz 00418 JOIN {course} c ON c.id = quiz.course 00419 LEFT JOIN ( 00420 SELECT attempt, MAX(timestamp) AS time 00421 FROM {question_states} 00422 GROUP BY attempt 00423 ) AS oldtimemodified ON oldtimemodified.attempt = quiza.uniqueid 00424 LEFT JOIN ( 00425 SELECT qa.questionusageid, MAX(qas.timecreated) AS time 00426 FROM {question_attempts} qa 00427 JOIN {question_attempt_steps} qas ON qas.questionattemptid = qa.id 00428 GROUP BY qa.questionusageid 00429 ) AS newtimemodified ON newtimemodified.questionusageid = quiza.uniqueid 00430 00431 WHERE quiza.preview = 0 00432 AND quiz.id = ? 00433 00434 GROUP BY quiz.id, quiz.name, c.shortname, c.id", array($quizid)); 00435 } 00436 00443 function tool_qeupgradehelper_get_session_id($attemptid, $questionid) { 00444 global $DB; 00445 $attempt = $DB->get_record('quiz_attempts', array('id' => $attemptid)); 00446 if (!$attempt) { 00447 return null; 00448 } 00449 return $DB->get_field('question_sessions', 'id', 00450 array('attemptid' => $attempt->uniqueid, 'questionid' => $questionid)); 00451 } 00452 00461 function tool_qeupgradehelper_find_test_case($behaviour, $statehistory, $qtype, $extratests) { 00462 global $DB; 00463 00464 $params = array( 00465 'qtype' => $qtype, 00466 'statehistory' => $statehistory 00467 ); 00468 00469 if ($behaviour == 'deferredfeedback') { 00470 $extrawhere = ''; 00471 $params['optionflags'] = 0; 00472 00473 } else if ($behaviour == 'adaptive') { 00474 $extrawhere = 'AND penaltyscheme = :penaltyscheme'; 00475 $params['optionflags'] = 0; 00476 $params['penaltyscheme'] = 0; 00477 00478 } else { 00479 $extrawhere = 'AND penaltyscheme = :penaltyscheme'; 00480 $params['optionflags'] = 0; 00481 $params['penaltyscheme'] = 1; 00482 } 00483 00484 $possibleids = $DB->get_records_sql_menu(' 00485 SELECT 00486 qsess.id, 00487 1 00488 00489 FROM {question_sessions} qsess 00490 JOIN {question_states} qst ON qst.attempt = qsess.attemptid 00491 AND qst.question = qsess.questionid 00492 JOIN {quiz_attempts} quiza ON quiza.uniqueid = qsess.attemptid 00493 JOIN {quiz} quiz ON quiz.id = quiza.quiz 00494 JOIN {question} q ON q.id = qsess.questionid 00495 00496 WHERE q.qtype = :qtype 00497 AND quiz.optionflags = :optionflags 00498 ' . $extrawhere . ' 00499 00500 GROUP BY 00501 qsess.id 00502 00503 HAVING SUM( 00504 (CASE WHEN qst.event = 10 THEN 1 ELSE qst.event END) * 00505 POWER(10, CAST(qst.seq_number AS NUMERIC(110,0))) 00506 ) = :statehistory' . $extratests, $params, 0, 100); 00507 00508 if (!$possibleids) { 00509 return null; 00510 } 00511 00512 return array_rand($possibleids); 00513 } 00514 00519 function tool_qeupgradehelper_generate_unit_test($questionsessionid, $namesuffix) { 00520 global $DB; 00521 00522 $qsession = $DB->get_record('question_sessions', array('id' => $questionsessionid)); 00523 $attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $qsession->attemptid)); 00524 $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz)); 00525 $qstates = $DB->get_records('question_states', 00526 array('attempt' => $qsession->attemptid, 'question' => $qsession->questionid), 00527 'seq_number, id'); 00528 00529 $question = tool_qeupgradehelper_load_question($qsession->questionid, $quiz->id); 00530 00531 if (!tool_qeupgradehelper_is_upgraded()) { 00532 if (!$quiz->optionflags) { 00533 $quiz->preferredbehaviour = 'deferredfeedback'; 00534 } else if ($quiz->penaltyscheme) { 00535 $quiz->preferredbehaviour = 'adaptive'; 00536 } else { 00537 $quiz->preferredbehaviour = 'adaptivenopenalty'; 00538 } 00539 unset($quiz->optionflags); 00540 unset($quiz->penaltyscheme); 00541 00542 $question->defaultmark = $question->defaultgrade; 00543 unset($question->defaultgrade); 00544 } 00545 00546 $attempt->needsupgradetonewqe = 1; 00547 00548 echo '<textarea readonly="readonly" rows="80" cols="120" >' . " 00549 public function test_{$question->qtype}_{$quiz->preferredbehaviour}_{$namesuffix}() { 00550 "; 00551 tool_qeupgradehelper_display_convert_attempt_input($quiz, $attempt, 00552 $question, $qsession, $qstates); 00553 00554 if ($question->qtype == 'random') { 00555 list($randombit, $realanswer) = explode('-', reset($qstates)->answer, 2); 00556 $newquestionid = substr($randombit, 6); 00557 $newquestion = tool_qeupgradehelper_load_question($newquestionid); 00558 $newquestion->maxmark = $question->maxmark; 00559 00560 echo tool_qeupgradehelper_format_var('$realquestion', $newquestion); 00561 echo ' $this->loader->put_question_in_cache($realquestion); 00562 '; 00563 } 00564 00565 echo ' 00566 $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates); 00567 00568 $expectedqa = (object) array('; 00569 echo " 00570 'behaviour' => '{$quiz->preferredbehaviour}', 00571 'questionid' => {$question->id}, 00572 'variant' => 1, 00573 'maxmark' => {$question->maxmark}, 00574 'minfraction' => 0, 00575 'flagged' => 0, 00576 'questionsummary' => '', 00577 'rightanswer' => '', 00578 'responsesummary' => '', 00579 'timemodified' => 0, 00580 'steps' => array("; 00581 foreach ($qstates as $state) { 00582 echo " 00583 {$state->seq_number} => (object) array( 00584 'sequencenumber' => {$state->seq_number}, 00585 'state' => '', 00586 'fraction' => null, 00587 'timecreated' => {$state->timestamp}, 00588 'userid' => {$attempt->userid}, 00589 'data' => array(), 00590 ),"; 00591 } 00592 echo ' 00593 ), 00594 ); 00595 00596 $this->compare_qas($expectedqa, $qa); 00597 } 00598 </textarea>'; 00599 } 00600 00601 function tool_qeupgradehelper_format_var($name, $var) { 00602 $out = var_export($var, true); 00603 $out = str_replace('<', '<', $out); 00604 $out = str_replace('ADOFetchObj::__set_state(array(', '(object) array(', $out); 00605 $out = str_replace('stdClass::__set_state(array(', '(object) array(', $out); 00606 $out = str_replace('array (', 'array(', $out); 00607 $out = preg_replace('/=> \n\s*/', '=> ', $out); 00608 $out = str_replace(')),', '),', $out); 00609 $out = str_replace('))', ')', $out); 00610 $out = preg_replace('/\n (?! )/', "\n ", $out); 00611 $out = preg_replace('/\n (?! )/', "\n ", $out); 00612 $out = preg_replace('/\n (?! )/', "\n ", $out); 00613 $out = preg_replace('/\n (?! )/', "\n ", $out); 00614 $out = preg_replace('/\n (?! )/', "\n ", $out); 00615 $out = preg_replace('/\n (?! )/', "\n ", $out); 00616 $out = preg_replace('/\n (?! )/', "\n ", $out); 00617 $out = preg_replace('/\n (?! )/', "\n ", $out); 00618 $out = preg_replace('/\n (?! )/', "\n ", $out); 00619 $out = preg_replace('/\n (?! )/', "\n ", $out); 00620 $out = preg_replace('/\n (?! )/', "\n ", $out); 00621 $out = preg_replace('/\n (?! )/', "\n ", $out); 00622 $out = preg_replace('/\n(?! )/', "\n ", $out); 00623 $out = preg_replace('/\bNULL\b/', 'null', $out); 00624 return " $name = $out;\n"; 00625 } 00626 00627 function tool_qeupgradehelper_display_convert_attempt_input($quiz, $attempt, 00628 $question, $qsession, $qstates) { 00629 echo tool_qeupgradehelper_format_var('$quiz', $quiz); 00630 echo tool_qeupgradehelper_format_var('$attempt', $attempt); 00631 echo tool_qeupgradehelper_format_var('$question', $question); 00632 echo tool_qeupgradehelper_format_var('$qsession', $qsession); 00633 echo tool_qeupgradehelper_format_var('$qstates', $qstates); 00634 } 00635 00636 function tool_qeupgradehelper_load_question($questionid, $quizid) { 00637 global $CFG, $DB; 00638 00639 $question = $DB->get_record_sql(' 00640 SELECT q.*, qqi.grade AS maxmark 00641 FROM {question} q 00642 JOIN {quiz_question_instances} qqi ON qqi.question = q.id 00643 WHERE q.id = :questionid AND qqi.quiz = :quizid', 00644 array('questionid' => $questionid, 'quizid' => $quizid)); 00645 00646 if (tool_qeupgradehelper_is_upgraded()) { 00647 require_once($CFG->dirroot . '/question/engine/bank.php'); 00648 $qtype = question_bank::get_qtype($question->qtype, false); 00649 } else { 00650 global $QTYPES; 00651 if (!array_key_exists($question->qtype, $QTYPES)) { 00652 $question->qtype = 'missingtype'; 00653 $question->questiontext = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext; 00654 } 00655 $qtype = $QTYPES[$question->qtype]; 00656 } 00657 00658 $qtype->get_question_options($question); 00659 00660 return $question; 00661 } 00662 00663 function tool_qeupgradehelper_get_quiz_for_upgrade() { 00664 global $DB; 00665 00666 return $DB->get_record_sql("SELECT quiz.id 00667 FROM {quiz_attempts} quiza 00668 JOIN {quiz} quiz ON quiz.id = quiza.quiz 00669 JOIN {course} c ON c.id = quiz.course 00670 WHERE quiza.preview = 0 AND quiza.needsupgradetonewqe = 1 00671 GROUP BY quiz.id, quiz.name, c.shortname, c.id 00672 ORDER BY quiza.timemodified DESC", array(), IGNORE_MULTIPLE); 00673 }