Merging in from the MOODLE_15_QUIZREFACTOR branch.
The quiz module now can handle adaptive questions and can render remote questions via RQP web services.
This commit is contained in:
+4
-4
@@ -144,7 +144,7 @@
|
||||
if (empty($_GET['confirmupgrade'])) {
|
||||
print_header($strdatabasechecking, $stradministration, $strdatabasechecking,
|
||||
"", "", false, " ", " ");
|
||||
notice_yesno(get_string('upgradesure', 'admin', $a->newversion), 'index.php?confirmupgrade=yes', 'index.php');
|
||||
notice_yesno(get_string('upgradesure', 'admin', $a->newversion).'<br />'.get_string('upgradesure', 'quiz'), 'index.php?confirmupgrade=yes', 'index.php');
|
||||
exit;
|
||||
|
||||
} else {
|
||||
@@ -260,11 +260,11 @@
|
||||
if (empty($CFG->siteidentifier)) { // Unique site identification code
|
||||
set_config('siteidentifier', random_string(32));
|
||||
}
|
||||
|
||||
|
||||
/// Check if the guest user exists. If not, create one.
|
||||
if (! record_exists("user", "username", "guest")) {
|
||||
$guest->auth = "manual";
|
||||
$guest->username = "guest";
|
||||
$guest->auth = "manual";
|
||||
$guest->username = "guest";
|
||||
$guest->password = md5("guest");
|
||||
$guest->firstname = addslashes(get_string("guestuser"));
|
||||
$guest->lastname = " ";
|
||||
|
||||
@@ -314,6 +314,7 @@ $string['uniform'] = 'decimals, from a uniform distribution';
|
||||
$string['unit'] = 'Unit';
|
||||
$string['unknowntype'] = 'Question type not supported at line $a. The question will be ignored';
|
||||
$string['unsupportedformulafunction'] = 'The function $a is not supported';
|
||||
$string['upgradesure'] = '<div style=\"color: red;\">In particular the quiz module will perform an extensive change of the quiz tables and this upgrade has not yet been sufficiently tested. You are very strongly urged to backup your database tables before proceeding.</div>';
|
||||
$string['viewallanswers'] = 'View $a completed quizzes';
|
||||
$string['viewallreports'] = 'View reports for $a attempts';
|
||||
$string['warningsdetected'] = '$a warning(s) detected';
|
||||
|
||||
+374
-224
@@ -1,12 +1,31 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This page prints a particular instance of quiz
|
||||
/**
|
||||
* This page prints a particular instance of quiz
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been completely
|
||||
* rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
|
||||
* the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
|
||||
optional_variable($id); // Course Module ID, or
|
||||
optional_variable($q); // quiz ID
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID
|
||||
$q = optional_param('q', 0, PARAM_INT); // or quiz ID
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
$questionids = optional_param('questionids', '');
|
||||
$finishattempt = optional_param('finishattempt', 0, PARAM_BOOL);
|
||||
$timeup = optional_param('timeup', 0, PARAM_BOOL); // True if form was submitted by timer.
|
||||
$forcenew = optional_param('forcenew', false, PARAM_BOOL); // Teacher has requested new preview
|
||||
|
||||
// We treat automatically closed attempts just like normally closed attempts
|
||||
if ($timeup) {
|
||||
$finishattempt = 1;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_record("course_modules", "id", $id)) {
|
||||
@@ -34,66 +53,87 @@
|
||||
}
|
||||
|
||||
require_login($course->id, false, $cm);
|
||||
$isteacher = isteacher($course->id);
|
||||
|
||||
|
||||
/// Set number for next attempt:
|
||||
|
||||
if ($attempts = quiz_get_user_attempts($quiz->id, $USER->id)) {
|
||||
$attemptnumber = 2;
|
||||
foreach ($attempts as $attempt) {
|
||||
if ($attempt->attempt >= $attemptnumber) {
|
||||
$attemptnumber = $attempt->attempt + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Get number for the next or unfinished attempt
|
||||
if(!$attemptnumber = (int)get_field_sql('SELECT MAX(attempt)+1 FROM ' .
|
||||
"{$CFG->prefix}quiz_attempts WHERE quiz = '{$quiz->id}' AND " .
|
||||
"userid = '{$USER->id}' AND timefinish > 0")) {
|
||||
$attemptnumber = 1;
|
||||
}
|
||||
|
||||
$strattemptnum = get_string("attempt", "quiz", $attemptnumber);
|
||||
$strattemptnum = get_string('attempt', 'quiz', $attemptnumber);
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$popup = $isteacher ? 0 : $quiz->popup; // Controls whether this is shown in a javascript-protected window.
|
||||
|
||||
|
||||
// Print the page header
|
||||
|
||||
if (!empty($quiz->popup)) {
|
||||
/// Print the page header
|
||||
if (!empty($popup)) {
|
||||
define('MESSAGE_WINDOW', true); // This prevents the message window coming up
|
||||
print_header("$course->shortname: ".format_string($quiz->name), '', '', '', '', false, '', '', false, '');
|
||||
|
||||
print_header($course->shortname.': '.format_string($quiz->name), '', '', '', '', false, '', '', false, '');
|
||||
include('protect_js.php');
|
||||
} else {
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strquiz = get_string("modulename", "quiz");
|
||||
$strupdatemodule = isteacheredit($course->id)
|
||||
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
|
||||
: "";
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a> ->
|
||||
<a href=\"view.php?id=$cm->id\">".format_string($quiz->name,true)."</a> -> $strattemptnum",
|
||||
"", "", true);
|
||||
<a href=\"view.php?id=$cm->id\">".format_string($quiz->name)."</a> -> $strattemptnum",
|
||||
"", "", true, $strupdatemodule);
|
||||
}
|
||||
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
/// Check availability
|
||||
/// Print the quiz name heading and tabs for teacher
|
||||
if ($isteacher) {
|
||||
$currenttab = 'preview';
|
||||
include('tabs.php');
|
||||
} else {
|
||||
print_heading(format_string($quiz->name));
|
||||
}
|
||||
|
||||
if ($quiz->attempts and $attempts and count($attempts) >= $quiz->attempts) {
|
||||
error("Sorry, you've had $quiz->attempts attempts already.", "view.php?id=$cm->id");
|
||||
/// Check availability
|
||||
if (isguest()) {
|
||||
print_heading(get_string('guestsno', 'quiz'));
|
||||
if (empty($popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($quiz->attempts and $attemptnumber > $quiz->attempts) {
|
||||
error(get_string('nomoreattempts', 'quiz'), "view.php?id={$cm->id}");
|
||||
}
|
||||
|
||||
$timenow = time();
|
||||
if (($timenow < $quiz->timeopen || $timenow > $quiz->timeclose)) {
|
||||
if ($isteacher) {
|
||||
notify(get_string('notavailabletostudents', 'quiz'));
|
||||
} else {
|
||||
error(get_string('notavailable', 'quiz'), "view.php?id={$cm->id}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Check subnet access
|
||||
if ($quiz->subnet and !address_in_subnet(getremoteaddr(), $quiz->subnet)) {
|
||||
error(get_string("subneterror", "quiz"), "view.php?id=$cm->id");
|
||||
if ($isteacher) {
|
||||
notify(get_string('subnetnotice', 'quiz'));
|
||||
} else {
|
||||
error(get_string("subneterror", "quiz"), "view.php?id=$cm->id");
|
||||
}
|
||||
}
|
||||
|
||||
/// Check password access
|
||||
if ($quiz->password and empty($_POST['q'])) {
|
||||
if (empty($_POST['quizpassword'])) {
|
||||
|
||||
print_heading(format_string($quiz->name));
|
||||
print_heading(get_string("attempt", "quiz", $attemptnumber));
|
||||
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
print_simple_box(format_text($quiz->intro), "center");
|
||||
}
|
||||
echo "<br />\n";
|
||||
|
||||
|
||||
echo "<form name=\"passwordform\" method=\"post\" action=\"attempt.php?id=$cm->id\">\n";
|
||||
print_simple_box_start("center");
|
||||
|
||||
|
||||
echo "<div align=\"center\">\n";
|
||||
print_string("requirepasswordmessage", "quiz");
|
||||
echo "<br /><br />\n";
|
||||
@@ -103,9 +143,9 @@
|
||||
|
||||
print_simple_box_end();
|
||||
echo "</form>\n";
|
||||
|
||||
if (empty($quiz->popup)) {
|
||||
print_footer($course);
|
||||
|
||||
if (empty($popup)) {
|
||||
print_footer();
|
||||
}
|
||||
exit;
|
||||
|
||||
@@ -113,226 +153,336 @@
|
||||
if (strcmp($quiz->password, $_POST['quizpassword']) !== 0) {
|
||||
error(get_string("passworderror", "quiz"), "view.php?id=$cm->id");
|
||||
}
|
||||
unset($_POST['quizpassword']); /// needed so as not to confuse later code dealing with submitted answers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Load attempt or create a new attempt if there is no unfinished one
|
||||
|
||||
if ($isteacher and $forcenew) { // teacher wants a new preview
|
||||
// so we set a finish time on the current attempt (if any).
|
||||
// It will then automatically be deleted below
|
||||
set_field('quiz_attempts', 'timefinish', time(), 'quiz', $quiz->id, 'userid', $USER->id);
|
||||
}
|
||||
|
||||
$attempt = get_record('quiz_attempts', 'quiz', $quiz->id,
|
||||
'userid', $USER->id, 'timefinish', 0);
|
||||
|
||||
if (!$attempt) {
|
||||
// Check if this is a preview request from a teacher
|
||||
// in which case the previous previews should be deleted
|
||||
if ($isteacher) {
|
||||
if ($oldattempts = get_records_select('quiz_attempts', "quiz = '$quiz->id'
|
||||
AND userid = '$USER->id'")) {
|
||||
delete_records('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id);
|
||||
delete_records('quiz_grades', 'quiz', $quiz->id, 'userid', $USER->id);
|
||||
foreach ($oldattempts as $oldattempt) {
|
||||
// there should only be one but we loop just in case
|
||||
delete_records('quiz_states', 'attempt', $oldattempt->id);
|
||||
delete_records('quiz_newest_states', 'attemptid', $oldattempt->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Start a new attempt and initialize the question sessions
|
||||
$attempt = quiz_create_attempt($quiz, $attemptnumber);
|
||||
// If this is an attempt by a teacher mark it as a preview
|
||||
if ($isteacher) {
|
||||
$attempt->preview = 1;
|
||||
}
|
||||
// Save the attempt
|
||||
if (!$attempt->id = insert_record('quiz_attempts', $attempt)) {
|
||||
error('Could not create new attempt');
|
||||
}
|
||||
// make log entries
|
||||
if ($isteacher) {
|
||||
add_to_log($course->id, 'quiz', 'preview',
|
||||
"attempt.php?id=$cm->id",
|
||||
"$quiz->id", $cm->id);
|
||||
} else {
|
||||
add_to_log($course->id, 'quiz', 'attempt',
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
} else {
|
||||
// log continuation of attempt only if some time has lapsed
|
||||
if ((time() - $attempt->timemodified) > 600) { // 10 minutes have elapsed
|
||||
add_to_log($course->id, 'quiz', 'continue attempt',
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Load all the questions and states needed by this script
|
||||
|
||||
// list of questions needed by page
|
||||
$pagelist = quiz_questions_on_page($attempt->layout, $page);
|
||||
|
||||
// add all questions that are on the submitted form
|
||||
if ($questionids) {
|
||||
$questionlist = $pagelist.','.$questionids;
|
||||
} else {
|
||||
$questionlist = $pagelist;
|
||||
}
|
||||
|
||||
if (!$questionlist) {
|
||||
error(get_string('noquestionsfound', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
}
|
||||
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}quiz_questions q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($questionlist)";
|
||||
|
||||
// Load the questions
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
error(get_string('noquestionsfound', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
}
|
||||
|
||||
// Load the question type specific information
|
||||
if (!quiz_get_question_options($questions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// Restore the question sessions to their most recent states
|
||||
// creating new sessions where required
|
||||
if (!$states = quiz_restore_question_sessions($questions, $quiz, $attempt)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
|
||||
/// Process form data /////////////////////////////////////////////////
|
||||
|
||||
if ($responses = data_submitted() and empty($_POST['quizpassword'])) {
|
||||
|
||||
// set the default event. This can be overruled by individual buttons.
|
||||
$event = (array_key_exists('markall', $responses)) ? QUIZ_EVENTGRADE :
|
||||
($finishattempt ? QUIZ_EVENTCLOSE : QUIZ_EVENTSAVE);
|
||||
|
||||
// Unset any variables we know are not responses
|
||||
unset($responses->id);
|
||||
unset($responses->q);
|
||||
unset($responses->oldpage);
|
||||
unset($responses->newpage);
|
||||
unset($responses->review);
|
||||
unset($responses->questionids);
|
||||
unset($responses->saveattempt); // responses get saved anway
|
||||
unset($responses->finishattempt); // same as $finishattempt
|
||||
unset($responses->markall);
|
||||
unset($responses->forcenewattempt);
|
||||
|
||||
// extract responses
|
||||
// $actions is an array indexed by the questions ids
|
||||
$actions = quiz_extract_responses($questions, $responses, $event);
|
||||
|
||||
// Process each question in turn
|
||||
|
||||
$questionidarray = explode(',', $questionids);
|
||||
foreach($questionidarray as $i) {
|
||||
if (!isset($actions[$i])) {
|
||||
$actions[$i]->responses = array('' => '');
|
||||
}
|
||||
quiz_process_responses($questions[$i], $states[$i], $actions[$i], $quiz, $attempt);
|
||||
quiz_save_question_session($questions[$i], $states[$i]);
|
||||
}
|
||||
|
||||
$attempt->timemodified = time();
|
||||
|
||||
// We have now finished processing form data
|
||||
}
|
||||
|
||||
|
||||
/// Finish attempt if requested
|
||||
if ($finishattempt) {
|
||||
|
||||
// Set the attempt to be finished
|
||||
$attempt->timefinish = time();
|
||||
|
||||
// Find all the questions for this attempt for which the newest
|
||||
// state is not also the newest graded state
|
||||
if ($closequestions = get_records_select('quiz_newest_states',
|
||||
"attemptid = $attempt->id AND new != newgraded", '', 'questionid, questionid')) {
|
||||
|
||||
// load all the questions
|
||||
$closequestionlist = implode(',', array_keys($closequestions));
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}quiz_questions q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($closequestionlist)";
|
||||
if (!$closequestions = get_records_sql($sql)) {
|
||||
error('Questions missing');
|
||||
}
|
||||
|
||||
// Load the question type specific information
|
||||
if (!quiz_get_question_options($closequestions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// Restore the question sessions
|
||||
if (!$closestates = quiz_restore_question_sessions($closequestions, $quiz, $attempt)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
foreach($closequestions as $key => $question) {
|
||||
$action->event = QUIZ_EVENTCLOSE;
|
||||
$action->responses = $closestates[$key]->responses;
|
||||
quiz_process_responses($question, $closestates[$key], $action, $quiz, $attempt);
|
||||
quiz_save_question_session($question, $closestates[$key]);
|
||||
}
|
||||
}
|
||||
add_to_log($course->id, 'quiz', 'close attempt',
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
|
||||
|
||||
/// Update the quiz attempt and the overall grade for the quiz
|
||||
if ($responses || $finishattempt) {
|
||||
if (!update_record('quiz_attempts', $attempt)) {
|
||||
error('Failed to save the current quiz attempt!');
|
||||
}
|
||||
if (($attempt->attempt > 1 || $attempt->timefinish > 0) and !$attempt->preview) {
|
||||
quiz_save_best_grade($quiz);
|
||||
}
|
||||
}
|
||||
|
||||
if ($finishattempt) {
|
||||
redirect('review.php?attempt='.$attempt->id);
|
||||
}
|
||||
|
||||
/// Get time limit if any.
|
||||
|
||||
$timelimit = $quiz->timelimit * 60;
|
||||
|
||||
$unattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id);
|
||||
if($timelimit > 0) {
|
||||
$timestart = $unattempt->timestart;
|
||||
if($timestart) {
|
||||
$timesincestart = time() - $timestart;
|
||||
if ($timelimit > 0) {
|
||||
$timestart = $attempt->timestart;
|
||||
if ($timestart) {
|
||||
$timesincestart = $timenow - $timestart;
|
||||
$timerstartvalue = $timelimit - $timesincestart;
|
||||
} else {
|
||||
$timerstartvalue = $timelimit;
|
||||
}
|
||||
if ($timerstartvalue <= 0) {
|
||||
$timerstartvalue = 1;
|
||||
}
|
||||
if(($timelimit + 60) <= $timesincestart) {
|
||||
// To pass it on to quiz_grade_responses
|
||||
$quiz->timesincestart = $timesincestart;
|
||||
}
|
||||
}
|
||||
|
||||
if($timelimit and $timerstartvalue <= 0) {
|
||||
$timerstartvalue = 1;
|
||||
}
|
||||
|
||||
$timenow = time();
|
||||
$available = ($quiz->timeopen < $timenow and $timenow < $quiz->timeclose) || isteacher($course->id);
|
||||
/// Print the quiz page ////////////////////////////////////////////////////////
|
||||
|
||||
/// Check to see if they are submitting answers
|
||||
if ($rawanswers = data_submitted()) {
|
||||
|
||||
$rawanswers = (array)$rawanswers;
|
||||
|
||||
$shuffleorder = NULL;
|
||||
|
||||
unset($rawanswers["q"]); // quiz id
|
||||
if (! count($rawanswers) and ! $timelimit) {
|
||||
print_heading(get_string("noanswers", "quiz"));
|
||||
print_continue("attempt.php?q=$quiz->id");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$questions = get_records_list("quiz_questions", "id", $quiz->questions)) {
|
||||
error("No questions found!");
|
||||
}
|
||||
|
||||
foreach ($rawanswers as $key => $value) { // Parse input for question->response
|
||||
|
||||
if ($postedquestionid = quiz_extract_posted_id($key)) {
|
||||
$questions[$postedquestionid]->response[$key] = trim($value);
|
||||
|
||||
} else if ('shuffleorder' == $key) {
|
||||
$shuffleorder = explode(",", $value); // Actual order questions were given in
|
||||
|
||||
} else if ('navigation' == $key) {
|
||||
$navigation = $value; // Determines next page to show in quiz
|
||||
|
||||
} else { // Useful for debugging new question types. Must be last.
|
||||
error("Unrecognizable input has been posted ($key -> $value)");
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve ->maxgrade for all questions
|
||||
If (!($grades = quiz_get_question_grades($quiz->id, $quiz->questions))) {
|
||||
$grades = array();
|
||||
}
|
||||
foreach ($grades as $qid => $grade) {
|
||||
$questions[$qid]->maxgrade = $grade->grade;
|
||||
}
|
||||
|
||||
if (isset($timesincestart)) {
|
||||
$quiz->timesincestart = $timesincestart; // To pass it on to quiz_grade_responses
|
||||
}
|
||||
|
||||
if (!$result = quiz_grade_responses($quiz, $questions, $unattempt->id)) {
|
||||
error("Could not grade your quiz attempt!");
|
||||
}
|
||||
|
||||
if ($attempt = quiz_save_attempt($quiz, $questions, $result,
|
||||
$attemptnumber, 0 == $navigation)) {
|
||||
if (empty($navigation)) {
|
||||
add_to_log($course->id, "quiz", "submit",
|
||||
"review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", $cm->id);
|
||||
}
|
||||
} else {
|
||||
notice(get_string("alreadysubmitted", "quiz"), "view.php?id=$cm->id");
|
||||
if (empty($quiz->popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($navigation)) {
|
||||
/// Attempt has finished
|
||||
|
||||
if (! quiz_save_best_grade($quiz, $USER->id)) {
|
||||
error("Sorry! Could not calculate your best grade!");
|
||||
}
|
||||
|
||||
if (empty($quiz->popup) and !$quiz->feedback) {
|
||||
// No need to stop on this page, go directly to view.php
|
||||
redirect('view.php?q='.$quiz->id);
|
||||
}
|
||||
|
||||
$strgrade = get_string("grade");
|
||||
$strscore = get_string("score", "quiz");
|
||||
|
||||
if ($quiz->grade) {
|
||||
print_heading("$strscore: $result->sumgrades/$quiz->sumgrades ($result->percentage %)");
|
||||
print_heading("$strgrade: $result->grade/$quiz->grade");
|
||||
}
|
||||
|
||||
/// continue button - use javascript to close down child window if in popup
|
||||
include('attempt_close_js.php');
|
||||
|
||||
if ($quiz->feedback) {
|
||||
$quiz->shuffleanswers = false; // Never shuffle answers in feedback
|
||||
|
||||
/// Make sure to get all questions in case not all are shown
|
||||
/// in quiz all the time.
|
||||
$questions = quiz_get_attempt_questions($quiz, $attempt);
|
||||
quiz_print_quiz_questions($quiz, $questions,
|
||||
quiz_grade_responses($quiz, $questions, $unattempt->id),
|
||||
$shuffleorder);
|
||||
/// continue button - use javascript to close down child window if in popup
|
||||
include('attempt_close_js.php');
|
||||
}
|
||||
|
||||
if (empty($quiz->popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
/// Print the attempt number or preview heading
|
||||
if ($isteacher) {
|
||||
print_heading(get_string('previewquiz', 'quiz'));
|
||||
unset($buttonoptions);
|
||||
$buttonoptions['q'] = $quiz->id;
|
||||
$buttonoptions['forcenew'] = true;
|
||||
echo '<center>';
|
||||
print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz'));
|
||||
echo '</center>';
|
||||
} else {
|
||||
$navigation= 1;
|
||||
$shuffleorder= NULL;
|
||||
print_heading($strattemptnum);
|
||||
}
|
||||
|
||||
|
||||
/// Print the quiz page
|
||||
|
||||
if (isguest()) {
|
||||
print_heading(get_string("guestsno", "quiz"));
|
||||
if (empty($quiz->popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/// Actually seeing the questions marks the start of an attempt
|
||||
|
||||
if (isset($unattempt) && $unattempt) {
|
||||
$attempt = $unattempt;
|
||||
|
||||
} else if ($attempt = quiz_start_attempt($quiz->id, $USER->id, $attemptnumber)) {
|
||||
add_to_log($course->id, "quiz", "attempt",
|
||||
"review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", $cm->id);
|
||||
} else {
|
||||
error("Sorry! Could not start the quiz (could not save starting time)");
|
||||
}
|
||||
|
||||
/// First print the headings and so on
|
||||
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
if (!$available) {
|
||||
error("Sorry, this quiz is not available", "view.php?id=$cm->id");
|
||||
}
|
||||
|
||||
print_heading(get_string("attempt", "quiz", $attemptnumber));
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
print_simple_box(format_text($quiz->intro), "center");
|
||||
}
|
||||
|
||||
|
||||
/// Add the javascript timer in the title bar if the closing time appears close
|
||||
|
||||
$secondsleft = $quiz->timeclose - time();
|
||||
if ($secondsleft > 0 and $secondsleft < 24*3600) { // less than a day remaining
|
||||
include("jsclock.php");
|
||||
include('jsclock.php');
|
||||
}
|
||||
|
||||
/// Include Javascript protection for this page if required
|
||||
/// Start the form
|
||||
if($quiz->timelimit > 0) {
|
||||
// Make sure javascript is enabled for time limited quizzes
|
||||
?>
|
||||
<script language="javascript" type="text/javascript">
|
||||
<!--
|
||||
document.write("<form name=\"responseform\" method=\"post\" action=\"attempt.php\">\n");
|
||||
// -->
|
||||
</script>
|
||||
<noscript>
|
||||
<center><p><strong><?php print_string('noscript', 'quiz'); ?></strong></p></center>
|
||||
</noscript>
|
||||
<?php
|
||||
} else {
|
||||
echo "<form name=\"responseform\" method=\"post\" action=\"attempt.php\">\n";
|
||||
}
|
||||
|
||||
if (!empty($quiz->popup)) {
|
||||
include("protect_js.php");
|
||||
// Add a hidden field with the quiz id
|
||||
echo '<input type="hidden" name="q" value="' . s($quiz->id) . "\" />\n";
|
||||
|
||||
/// Print the navigation panel if required
|
||||
$numpages = quiz_number_of_pages($attempt->layout);
|
||||
if ($numpages > 1) {
|
||||
?>
|
||||
<script language="javascript" type="text/javascript">
|
||||
function navigate(page) {
|
||||
document.responseform.page.value=page;
|
||||
document.responseform.submit();
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
echo '<input type="hidden" id="page" name="page" value="'.$page."\" />\n";
|
||||
quiz_print_navigation_panel($page, $numpages);
|
||||
echo "<br />\n";
|
||||
}
|
||||
|
||||
/// Print all the questions
|
||||
|
||||
echo "<br />";
|
||||
// Add a hidden field with questionids
|
||||
echo '<input type="hidden" name="questionids" value="'.$pagelist."\" />\n";
|
||||
|
||||
$questions = quiz_get_attempt_questions($quiz, $attempt, true);
|
||||
if ($quiz->attemptonlast && $attemptnumber >= 2 and
|
||||
$quiz->attempts == 0 || !$unattempt) {
|
||||
// There are unlimited attempts or it is a new attempt.
|
||||
// As the attempt also builds on the last, we can here
|
||||
// have the student see the scores of the pre-entered
|
||||
// responses that we here will have graded:
|
||||
$result = quiz_grade_responses($quiz, $questions);
|
||||
$result->attemptbuildsonthelast = true;
|
||||
} else {
|
||||
$result = NULL;
|
||||
}
|
||||
|
||||
// We do not show feedback or correct answers during an attempt:
|
||||
$quiz->feedback = $quiz->correctanswers = false;
|
||||
|
||||
if (!quiz_print_quiz_questions($quiz, $questions,
|
||||
$result, $shuffleorder, $navigation)) {
|
||||
print_continue("view.php?id=$cm->id");
|
||||
$pagequestions = explode(',', $pagelist);
|
||||
$number = quiz_first_questionnumber($attempt->layout, $pagelist);
|
||||
foreach ($pagequestions as $i) {
|
||||
$options = quiz_get_renderoptions($quiz, $states[$i]);
|
||||
// Print the question
|
||||
if ($i > 0) {
|
||||
echo "<br />\n";
|
||||
}
|
||||
quiz_print_quiz_question($questions[$i], $states[$i], $number, $quiz, $options);
|
||||
quiz_save_question_session($questions[$i], $states[$i]);
|
||||
$number += $questions[$i]->length;
|
||||
}
|
||||
|
||||
/// If quiz is available and time limit is set include floating timer.
|
||||
/// Print the submit buttons
|
||||
|
||||
if ($available and $timelimit > 0) {
|
||||
$strconfirmattempt = addslashes(get_string("confirmclose", "quiz"));
|
||||
$onclick = "return confirm('$strconfirmattempt')";
|
||||
echo "<center>\n";
|
||||
|
||||
echo "<input type=\"submit\" name=\"saveattempt\" value=\"".get_string("savenosubmit", "quiz")."\" />\n";
|
||||
if ($quiz->optionflags & QUIZ_ADAPTIVE) {
|
||||
echo "<input type=\"submit\" name=\"markall\" value=\"".get_string("markall", "quiz")."\" />\n";
|
||||
}
|
||||
echo "<input type=\"submit\" name=\"finishattempt\" value=\"".get_string("finishattempt", "quiz")."\" onclick=\"$onclick\" />\n";
|
||||
echo '<input type="hidden" name="timeup" value="0" />';
|
||||
|
||||
echo "</center>";
|
||||
|
||||
// Print the navigation panel if required
|
||||
if ($numpages > 1) {
|
||||
echo "<br />\n";
|
||||
quiz_print_navigation_panel($page, $numpages);
|
||||
echo '<br />';
|
||||
}
|
||||
|
||||
// Finish the form
|
||||
echo "</form>\n";
|
||||
|
||||
// If time limit is set include floating timer.
|
||||
if ($timelimit > 0) {
|
||||
require('jstimer.php');
|
||||
}
|
||||
|
||||
if (empty($quiz->popup)) {
|
||||
if (!$isteacher) {
|
||||
include('attempt_close_js.php');
|
||||
}
|
||||
|
||||
// Finish the page
|
||||
if (empty($popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<?php defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');?>
|
||||
|
||||
<div align="center">
|
||||
<?php
|
||||
if (!empty($quiz->popup)) {
|
||||
<?php
|
||||
if (!empty($popup)) {
|
||||
?>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
<!--
|
||||
|
||||
document.write('<input type="button" value="<?php print_string('continue') ?>" '+
|
||||
'onclick="javascript: window.opener.location.href=\'../../course/view.php?id=<?php echo $course->id ?>\'; '+
|
||||
document.write('<input type="button" value="<?php print_string('closewindow') ?>" '+
|
||||
'onclick="javascript: window.opener.location.href=\'view.php?id=<?php echo $cm->id ?>\'; '+
|
||||
'window.close();" />');
|
||||
// -->
|
||||
</script>
|
||||
@@ -19,7 +19,7 @@ document.write('<input type="button" value="<?php print_string('continue') ?>" '
|
||||
|
||||
<?php
|
||||
} else {
|
||||
print_single_button("../../course/view.php", array( 'id' => $course->id ), get_string('continue'));
|
||||
print_single_button("view.php", array( 'id' => $cm->id ), get_string('continue'));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This script lists student attempts
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
optional_variable($id); // Course Module ID, or
|
||||
optional_variable($q); // quiz ID
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_record("course_modules", "id", $id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
|
||||
if (! $course = get_record("course", "id", $cm->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
|
||||
if (! $quiz = get_record("quiz", "id", $cm->instance)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (! $quiz = get_record("quiz", "id", $q)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
if (! $course = get_record("course", "id", $quiz->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
require_login($course->id, false);
|
||||
|
||||
if (!isteacher($course->id)) {
|
||||
error("You are not allowed to use this script");
|
||||
}
|
||||
|
||||
// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quiz->questions and isteacheredit($course->id)) {
|
||||
redirect('edit.php?quizid='.$quiz->id);
|
||||
}
|
||||
|
||||
add_to_log($course->id, "quiz", "attempts", "attempts.php?id=$cm->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// Define some strings
|
||||
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strquiz = get_string("modulename", "quiz");
|
||||
$strreallydel = addslashes(get_string('deleteattemptcheck','quiz'));
|
||||
$strnoattempts = get_string('noattempts','quiz');
|
||||
$strtimeformat = get_string('strftimedatetime');
|
||||
$strreviewquestion = get_string('reviewresponse', 'quiz');
|
||||
|
||||
/// Print the page header
|
||||
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>
|
||||
-> ".format_string($quiz->name),
|
||||
"", "", true, update_module_button($cm->id, $course->id, $strquiz), navmenu($course, $cm));
|
||||
|
||||
/// Print the tabs
|
||||
|
||||
$currenttab = 'attempts';
|
||||
include('tabs.php');
|
||||
|
||||
/// Deal with actions
|
||||
|
||||
$action = optional_param('action', '');
|
||||
|
||||
switch($action) {
|
||||
case 'delete': /// Some attempts need to be deleted
|
||||
// the following needs to be improved to delete all associated data as well
|
||||
|
||||
$attemptids = isset($_POST['attemptid']) ? $_POST['attemptid'] : array();
|
||||
if(!is_array($attemptids) || empty($attemptids)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($attemptids as $num => $attemptid) {
|
||||
if(empty($attemptid)) {
|
||||
unset($attemptids[$num]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($attemptids as $attemptid) {
|
||||
if ($todelete = get_record('quiz_attempts', 'id', $attemptid)) {
|
||||
|
||||
delete_records('quiz_attempts', 'id', $attemptid);
|
||||
delete_records('quiz_states', 'attempt', $attemptid);
|
||||
|
||||
// Search quiz_attempts for other instances by this user.
|
||||
// If none, then delete record for this quiz, this user from quiz_grades
|
||||
// else recalculate best grade
|
||||
|
||||
$userid = $todelete->userid;
|
||||
if (!record_exists('quiz_attempts', 'userid', $userid, 'quiz', $quiz->id)) {
|
||||
delete_records('quiz_grades', 'userid', $userid,'quiz', $quiz->id);
|
||||
} else {
|
||||
quiz_save_best_grade($quiz, $userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/// Check to see if groups are being used in this quiz
|
||||
if ($groupmode = groupmode($course, $cm)) { // Groups are being used
|
||||
$currentgroup = setup_and_print_groups($course, $groupmode, "attempts.php?id=$cm->id&mode=overview");
|
||||
} else {
|
||||
$currentgroup = false;
|
||||
}
|
||||
|
||||
/// Get all students
|
||||
if ($currentgroup) {
|
||||
$users = get_group_students($currentgroup);
|
||||
}
|
||||
else {
|
||||
$users = get_course_students($course->id);
|
||||
}
|
||||
|
||||
if(empty($users)) {
|
||||
print_heading($strnoattempts);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Set table options
|
||||
if(!isset($SESSION->quiz_overview_table)) {
|
||||
$SESSION->quiz_overview_table = array('noattempts' => false, 'detailedmarks' => false);
|
||||
}
|
||||
|
||||
foreach($SESSION->quiz_overview_table as $option => $value) {
|
||||
$urlparam = optional_param($option, NULL);
|
||||
if($urlparam === NULL) {
|
||||
$$option = $value;
|
||||
}
|
||||
else {
|
||||
$$option = $SESSION->quiz_overview_table[$option] = $urlparam;
|
||||
}
|
||||
}
|
||||
|
||||
/// Print display options
|
||||
|
||||
echo '<div class="controls">';
|
||||
echo '<form method="post" action="attempts.php">';
|
||||
echo '<p>'.get_string('displayoptions', 'quiz').': ';
|
||||
echo '<input type="hidden" name="id" value="'.$cm->id.'" />';
|
||||
echo '<input type="hidden" name="noattempts" value="0" />';
|
||||
echo '<input type="hidden" name="detailedmarks" value="0" />';
|
||||
echo '<input type="checkbox" id="checknoattempts" name="noattempts" '.($noattempts?'checked="checked" ':'').'value="1" /> <label for="checknoattempts">'.get_string('shownoattempts', 'quiz').'</label> ';
|
||||
echo '<input type="checkbox" id="checkdetailedmarks" name="detailedmarks" '.($detailedmarks?'checked="checked" ':'').'value="1" /> <label for="checkdetailedmarks">'.get_string('showdetailedmarks', 'quiz').'</label> ';
|
||||
echo '<input type="submit" value="'.get_string('go').'" />';
|
||||
echo '</p>';
|
||||
echo '</form>';
|
||||
echo '</div>';
|
||||
|
||||
/// Define table columns
|
||||
$tablecolumns = array('checkbox', 'picture', 'fullname', 'timestart', 'duration');
|
||||
$tableheaders = array(NULL, '', get_string('fullname'), get_string('startedon', 'quiz'), get_string('attemptduration', 'quiz'));
|
||||
|
||||
if ($quiz->grade) {
|
||||
$tablecolumns[] = 'sumgrades';
|
||||
$tableheaders[] = get_string('grade', 'quiz').'/'.$quiz->grade;
|
||||
}
|
||||
|
||||
if($detailedmarks) {
|
||||
// we want to display marks for all questions
|
||||
// Start by getting all questions
|
||||
$questionlist = quiz_questions_in_quiz($quiz->questions);
|
||||
$questionids = explode(',', $questionlist);
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}quiz_questions q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($questionlist)";
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
error('No questions found');
|
||||
}
|
||||
$number = 1;
|
||||
foreach($questionids as $key => $id) {
|
||||
if ($questions[$id]->length) {
|
||||
// Only print questions of non-zero length
|
||||
$tablecolumns[] = '$'.$id;
|
||||
$tableheaders[] = '#'.$number;
|
||||
$questions[$id]->number = $number;
|
||||
$number += $questions[$id]->length;
|
||||
} else {
|
||||
// get rid of zero length questions
|
||||
unset($questions[$id]);
|
||||
unset($questionids[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up the table
|
||||
|
||||
$table = new flexible_table('mod-quiz-report-overview-report');
|
||||
|
||||
$table->define_columns($tablecolumns);
|
||||
$table->define_headers($tableheaders);
|
||||
$table->define_baseurl($CFG->wwwroot.'/mod/quiz/attempts.php?id='.$cm->id);
|
||||
|
||||
$table->sortable(true);
|
||||
$table->collapsible(true);
|
||||
$table->initialbars(count($users)>20);
|
||||
|
||||
$table->column_suppress('picture');
|
||||
$table->column_suppress('fullname');
|
||||
|
||||
$table->column_class('picture', 'picture');
|
||||
|
||||
$table->set_attribute('cellspacing', '0');
|
||||
$table->set_attribute('id', 'attempts');
|
||||
$table->set_attribute('class', 'generaltable generalbox');
|
||||
|
||||
// Start working -- this is necessary as soon as the niceties are over
|
||||
$table->setup();
|
||||
|
||||
|
||||
/// Construct the SQL
|
||||
|
||||
if($where = $table->get_sql_where()) {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
|
||||
if($sort = $table->get_sql_sort()) {
|
||||
$sortparts = explode(',', $sort);
|
||||
$newsort = array();
|
||||
$firsttime = true;
|
||||
foreach($sortparts as $sortpart) {
|
||||
$sortpart = trim($sortpart);
|
||||
if(substr($sortpart, 0, 1) == '$') {
|
||||
if($firsttime) {
|
||||
$qnum = intval(substr($sortpart, 1));
|
||||
$where .= '('.sql_isnull('qr.question').' OR qr.question = '.$qnum.') AND ';
|
||||
$newsort[] = 'grade '.(strpos($sortpart, 'ASC')? 'ASC' : 'DESC');
|
||||
$firsttime = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$newsort[] = $sortpart;
|
||||
}
|
||||
}
|
||||
$sort = ' ORDER BY '.implode(', ', $newsort);
|
||||
}
|
||||
|
||||
$select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uvsa, u.id AS userid, u.firstname, u.lastname, u.picture, qa.id AS attempt, qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration ';
|
||||
$group = 'GROUP BY uvsa';
|
||||
$sql = 'FROM '.$CFG->prefix.'user u '.
|
||||
'LEFT JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '.
|
||||
'LEFT JOIN '.$CFG->prefix.'quiz_states qr ON qr.attempt = qa.id '.
|
||||
'WHERE '.$where.'u.id IN ('.implode(',', array_keys($users)).') AND ('.($noattempts ? sql_isnull('qa.quiz').' OR ' : '') . 'qa.quiz = '.$quiz->id.') ';
|
||||
|
||||
|
||||
$total = count_records_sql('SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$sql);
|
||||
$table->pagesize(10, $total);
|
||||
|
||||
if($table->get_page_start() !== '' && $table->get_page_size() !== '') {
|
||||
$limit = ' '.sql_paging_limit($table->get_page_start(), $table->get_page_size());
|
||||
}
|
||||
else {
|
||||
$limit = '';
|
||||
}
|
||||
|
||||
/// Fetch the attempts
|
||||
|
||||
$attempts = get_records_sql($select.$sql.$group.$sort.$limit);
|
||||
|
||||
/// Build table rows
|
||||
|
||||
if(!empty($attempts)) {
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
|
||||
$picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true);
|
||||
|
||||
$row = array(
|
||||
'<input type="checkbox" name="attemptid[]" value="'.$attempt->attempt.'" />',
|
||||
$picture,
|
||||
'<a href="'.$CFG->wwwroot.'/user/view.php?id='.$attempt->userid.'&course='.$course->id.'">'.fullname($attempt).'</a>',
|
||||
empty($attempt->attempt) ? '-' : '<a href="review.php?q='.$quiz->id.'&attempt='.$attempt->attempt.'">'.userdate($attempt->timestart, $strtimeformat).'</a>',
|
||||
empty($attempt->attempt) ? '-' :
|
||||
(empty($attempt->timefinish) ? get_string('unfinished', 'quiz') :
|
||||
format_time($attempt->duration))
|
||||
);
|
||||
|
||||
if ($quiz->grade) {
|
||||
$row[] = $attempt->sumgrades === NULL ? '-' : '<a href="review.php?q='.$quiz->id.'&attempt='.$attempt->attempt.'">'.format_float($attempt->sumgrades / $quiz->sumgrades * $quiz->grade,$quiz->decimalpoints).'</a>';
|
||||
}
|
||||
|
||||
if($detailedmarks) {
|
||||
if(empty($attempt->attempt)) {
|
||||
foreach($questionids as $questionid) {
|
||||
$row[] = '-';
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach($questionids as $questionid) {
|
||||
if ($gradedstateid = get_field('quiz_newest_states', 'newgraded', 'attemptid', $attempt->attempt, 'questionid', $questionid)) {
|
||||
$grade = format_float(get_field('quiz_states', 'grade', 'id', $gradedstateid), $quiz->decimalpoints);
|
||||
} else {
|
||||
// This is an old-style attempt
|
||||
$grade = format_float(get_field('quiz_states', 'grade', 'attempt', $attempt->attempt, 'question', $questionid), $quiz->decimalpoints);
|
||||
}
|
||||
$row[] = link_to_popup_window ('/mod/quiz/reviewquestion.php?state='.$gradedstateid.'&number='.$questions[$questionid]->number, 'reviewquestion', $grade, 450, 650, $strreviewquestion, 'none', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$table->add_data($row);
|
||||
}
|
||||
|
||||
/// Start form
|
||||
|
||||
echo '<div id="tablecontainer">';
|
||||
echo '<form id="attemptsform" method="post" action="attempts.php" onsubmit="var menu = document.getElementById(\'actionmenu\'); return (menu.options[menu.selectedIndex].value == \'delete\' ? \''.$strreallydel.'\' : true);">';
|
||||
echo '<input type="hidden" name="id" value="'.$cm->id.'" />';
|
||||
|
||||
/// Print table
|
||||
|
||||
$table->print_html();
|
||||
|
||||
/// Print "Select all" etc.
|
||||
|
||||
echo '<table id="commands">';
|
||||
echo '<tr><td>';
|
||||
echo '<a href="javascript:select_all_in(\'DIV\', null, \'tablecontainer\');">'.get_string('selectall', 'quiz').'</a> / ';
|
||||
echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'tablecontainer\');">'.get_string('selectnone', 'quiz').'</a> ';
|
||||
echo ' ';
|
||||
$options = array('delete' => get_string('delete'));
|
||||
$menu = str_replace('<select', '<select id="actionmenu"', choose_from_menu($options, 'action', '', get_string('withselected', 'quiz'), 'if(this.selectedIndex > 0) submitFormById(\'attemptsform\');', '', true));
|
||||
echo $menu;
|
||||
echo '<noscript id="noscriptactionmenu" style="display: inline;">';
|
||||
echo '<input type="submit" value="'.get_string('go').'" /></noscript>';
|
||||
echo '<script type="text/javascript">'."\n<!--\n".'document.getElementById("noscriptactionmenu").style.display = "none";'."\n-->\n".'</script>';
|
||||
echo '</td></tr></table>';
|
||||
|
||||
/// Close form
|
||||
echo '</form></div>';
|
||||
}
|
||||
else {
|
||||
print_heading(get_string('noattemptstoshow', 'quiz'));
|
||||
}
|
||||
|
||||
|
||||
/// Print footer
|
||||
|
||||
print_footer($course);
|
||||
|
||||
?>
|
||||
+57
-26
@@ -90,7 +90,7 @@
|
||||
// - quiz_question_grades
|
||||
// - quiz_attempts
|
||||
// - quiz_grades
|
||||
// - quiz_responses
|
||||
// - quiz_states
|
||||
// This step is the standard mod backup. (course dependent).
|
||||
|
||||
//STEP 1. Backup categories/questions and associated structures
|
||||
@@ -618,31 +618,62 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//Backup quiz_question_grades contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_question_grades ($bf,$preferences,$quiz) {
|
||||
//Backup quiz_question_instances contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_question_instances ($bf,$preferences,$quiz) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_question_grades = get_records("quiz_question_grades","quiz",$quiz,"id");
|
||||
//If there are question_grades
|
||||
if ($quiz_question_grades) {
|
||||
$quiz_question_instances = get_records("quiz_question_instances","quiz",$quiz,"id");
|
||||
//If there are question_instances
|
||||
if ($quiz_question_instances) {
|
||||
//Write start tag
|
||||
$status =fwrite ($bf,start_tag("QUESTION_GRADES",4,true));
|
||||
//Iterate over each question_grade
|
||||
foreach ($quiz_question_grades as $que_gra) {
|
||||
//Start question grade
|
||||
$status =fwrite ($bf,start_tag("QUESTION_GRADE",5,true));
|
||||
//Print question_grade contents
|
||||
fwrite ($bf,full_tag("ID",6,false,$que_gra->id));
|
||||
fwrite ($bf,full_tag("QUESTION",6,false,$que_gra->question));
|
||||
fwrite ($bf,full_tag("GRADE",6,false,$que_gra->grade));
|
||||
//End question grade
|
||||
$status =fwrite ($bf,end_tag("QUESTION_GRADE",5,true));
|
||||
$status =fwrite ($bf,start_tag("QUESTION_INSTANCES",4,true));
|
||||
//Iterate over each question_instance
|
||||
foreach ($quiz_question_instances as $que_ins) {
|
||||
//Start question instance
|
||||
$status =fwrite ($bf,start_tag("QUESTION_INSTANCE",5,true));
|
||||
//Print question_instance contents
|
||||
fwrite ($bf,full_tag("ID",6,false,$que_ins->id));
|
||||
fwrite ($bf,full_tag("QUESTION",6,false,$que_ins->question));
|
||||
fwrite ($bf,full_tag("GRADE",6,false,$que_ins->grade));
|
||||
//End question instance
|
||||
$status =fwrite ($bf,end_tag("QUESTION_INSTANCE",5,true));
|
||||
}
|
||||
//Write end tag
|
||||
$status =fwrite ($bf,end_tag("QUESTION_GRADES",4,true));
|
||||
$status =fwrite ($bf,end_tag("QUESTION_INSTANCES",4,true));
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
//Backup quiz_question_versions contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_question_versions ($bf,$preferences,$quiz) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_question_versions = get_records("quiz_question_versions","quiz",$quiz,"id");
|
||||
//If there are question_versions
|
||||
if ($quiz_question_versions) {
|
||||
//Write start tag
|
||||
$status =fwrite ($bf,start_tag("QUESTION_VERSIONS",4,true));
|
||||
//Iterate over each question_version
|
||||
foreach ($quiz_question_versions as $que_ver) {
|
||||
//Start question version
|
||||
$status =fwrite ($bf,start_tag("QUESTION_VERSION",5,true));
|
||||
//Print question_version contents
|
||||
fwrite ($bf,full_tag("ID",6,false,$que_ver->id));
|
||||
fwrite ($bf,full_tag("OLDQUESTION",6,false,$que_ver->oldquestion));
|
||||
fwrite ($bf,full_tag("NEWQUESTION",6,false,$que_ver->newquestion));
|
||||
fwrite ($bf,full_tag("USERID",6,false,$que_ver->userid));
|
||||
fwrite ($bf,full_tag("TIMESTAMP",6,false,$que_ver->timestamp));
|
||||
//End question version
|
||||
$status =fwrite ($bf,end_tag("QUESTION_VERSION",5,true));
|
||||
}
|
||||
//Write end tag
|
||||
$status =fwrite ($bf,end_tag("QUESTION_VERSIONS",4,true));
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
@@ -732,8 +763,8 @@
|
||||
fwrite ($bf,full_tag("TIMESTART",6,false,$attempt->timestart));
|
||||
fwrite ($bf,full_tag("TIMEFINISH",6,false,$attempt->timefinish));
|
||||
fwrite ($bf,full_tag("TIMEMODIFIED",6,false,$attempt->timemodified));
|
||||
//Now write to xml the responses (in this attempt)
|
||||
$status = backup_quiz_responses ($bf,$preferences,$attempt->id);
|
||||
//Now write to xml the states (in this attempt)
|
||||
$status = backup_quiz_states ($bf,$preferences,$attempt->id);
|
||||
//End attempt
|
||||
$status =fwrite ($bf,end_tag("ATTEMPT",5,true));
|
||||
}
|
||||
@@ -743,16 +774,16 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//Backup quiz_responses contents (executed from backup_quiz_attempts)
|
||||
function backup_quiz_responses ($bf,$preferences,$attempt) {
|
||||
//Backup quiz_states contents (executed from backup_quiz_attempts)
|
||||
function backup_quiz_states ($bf,$preferences,$attempt) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_responses = get_records("quiz_responses","attempt",$attempt,"id");
|
||||
//If there are responses
|
||||
if ($quiz_responses) {
|
||||
$quiz_states = get_records("quiz_states","attempt",$attempt,"id");
|
||||
//If there are states
|
||||
if ($quiz_states) {
|
||||
//Write start tag
|
||||
$status =fwrite ($bf,start_tag("RESPONSES",6,true));
|
||||
//Iterate over each response
|
||||
@@ -769,7 +800,7 @@
|
||||
$status =fwrite ($bf,end_tag("RESPONSE",7,true));
|
||||
}
|
||||
//Write end tag
|
||||
$status =fwrite ($bf,end_tag("RESPONSES",6,true));
|
||||
$status =fwrite ($bf,end_tag("STATES",6,true));
|
||||
}
|
||||
return $status;
|
||||
}
|
||||
|
||||
+17
-31
@@ -25,6 +25,10 @@
|
||||
"<a href=\"index.php?id=$course->id\">".get_string('modulenameplural', 'quiz').'</a>'.
|
||||
' -> '.get_string('editcategories', 'quiz'));
|
||||
|
||||
if (isset($SESSION->modform->instance) and $quiz = get_record('quiz', 'id', $SESSION->modform->instance)) {
|
||||
include('tabs.php');
|
||||
}
|
||||
|
||||
/// CHECK FOR AND ACT UPON VARIABLES SUBMITTED VIA GET OR POST
|
||||
$qcobject = new quiz_category_object();
|
||||
$qcobject->set_course($course);
|
||||
@@ -239,19 +243,14 @@ class quiz_category_object {
|
||||
* @param object course
|
||||
*/
|
||||
function output_edit_table() {
|
||||
$this->edittable->head = array ($this->str->course, $this->str->category, $this->str->categoryinfo, $this->str->questions, $this->str->publish,
|
||||
$this->edittable->head = array ($this->str->category, $this->str->categoryinfo, $this->str->questions, $this->str->publish,
|
||||
$this->str->delete, $this->str->order, $this->str->movecategoryto);
|
||||
$this->edittable->width = 200;
|
||||
$this->edittable->tablealign = 'center';
|
||||
|
||||
// get list of short names to add to the course display. Only necessary if user has admin view.
|
||||
if (isadmin()) {
|
||||
$keys = implode(',', $this->get_course_ids($this->categories));
|
||||
$courses = get_records_select_menu('course', "id in ($keys)", '', 'id, shortname');
|
||||
} else {
|
||||
$courses = $this->course->shortname;
|
||||
}
|
||||
$this->build_edit_table_body($this->categories, $courses);
|
||||
$courses = $this->course->shortname;
|
||||
|
||||
$this->build_edit_table_body($this->categories);
|
||||
print_table($this->edittable);
|
||||
}
|
||||
/**
|
||||
@@ -261,7 +260,7 @@ class quiz_category_object {
|
||||
* @param mixed courses String with shortname of course | array containing courseid=>shortname
|
||||
* @param int depth controls the indenting
|
||||
*/
|
||||
function build_edit_table_body($categories, $courses, $depth = 0) {
|
||||
function build_edit_table_body($categories, $depth = 0) {
|
||||
$countcats = count($categories);
|
||||
$count = 0;
|
||||
$first = true;
|
||||
@@ -275,10 +274,9 @@ class quiz_category_object {
|
||||
$up = $first ? false : true;
|
||||
$down = $last ? false : true;
|
||||
$first = false;
|
||||
$courseshortname = is_string($courses) ? $courses : $courses[$category->course];
|
||||
$this->quiz_edit_category_row($category, $courseshortname, $depth, $up, $down);
|
||||
$this->quiz_edit_category_row($category, $depth, $up, $down);
|
||||
if (isset($category->children)) {
|
||||
$this->build_edit_table_body($category->children, $courses, $depth + 1);
|
||||
$this->build_edit_table_body($category->children, $depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,16 +307,13 @@ class quiz_category_object {
|
||||
* @param boolean up can it be moved up?
|
||||
* @param boolean down can it be moved down?
|
||||
*/
|
||||
function quiz_edit_category_row($category, $shortname, $depth, $up = false, $down = false) {
|
||||
function quiz_edit_category_row($category, $depth, $up = false, $down = false) {
|
||||
global $USER;
|
||||
$fill = str_repeat($this->tab, $depth);
|
||||
|
||||
$linkcss = $category->publish ? ' class="published"' : ' class="unpublished"';
|
||||
|
||||
/// Each section below adds a data cell to this table row
|
||||
$this->edittable->align["$category->id.course"] = "left";
|
||||
$this->edittable->wrap["$category->id.course"] = "nowrap";
|
||||
$row["$category->id.course"] = $shortname;
|
||||
|
||||
$this->edittable->align["$category->id.name"] = "left";
|
||||
$this->edittable->wrap["$category->id.name"] = "nowrap";
|
||||
@@ -526,7 +521,7 @@ class quiz_category_object {
|
||||
for ($index = count($levels) - 1; $index >= 0; $index--) {
|
||||
foreach($levels[$index] as $key) {
|
||||
$parentkey = $records[$key]->parent;
|
||||
if (!($records[$key]->questioncount = count_records('quiz_questions', 'category', $records[$key]->id, 'hidden', 0))) {
|
||||
if (!($records[$key]->questioncount = count_records('quiz_questions', 'category', $records[$key]->id, 'hidden', 0, 'parent', '0'))) {
|
||||
$records[$key]->questioncount = 0;
|
||||
}
|
||||
if ($parentkey == 0) {
|
||||
@@ -568,20 +563,11 @@ class quiz_category_object {
|
||||
*/
|
||||
function get_quiz_categories($parent=null, $sort="sortorder ASC") {
|
||||
|
||||
$admin = isadmin();
|
||||
if ($admin) {
|
||||
if (is_null($parent)) {
|
||||
$categories = get_records("quiz_categories", "", "", $sort);
|
||||
} else {
|
||||
$categories = get_records("quiz_categories", "parent", $parent, $sort);
|
||||
}
|
||||
if (is_null($parent)) {
|
||||
$categories = get_records('quiz_categories', 'course', "{$this->course->id}", $sort);
|
||||
} else {
|
||||
if (is_null($parent)) {
|
||||
$categories = get_records('quiz_categories', 'course', "{$this->course->id}", $sort);
|
||||
} else {
|
||||
$select = "parent = '$parent' AND course = '{$this->course->id}'";
|
||||
$categories = get_records_select('quiz_categories', $select, $sort);
|
||||
}
|
||||
$select = "parent = '$parent' AND course = '{$this->course->id}'";
|
||||
$categories = get_records_select('quiz_categories', $select, $sort);
|
||||
}
|
||||
return $categories;
|
||||
}
|
||||
|
||||
+171
-74
@@ -1,5 +1,61 @@
|
||||
<?php
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
if (!isset($form->timeopen)) {
|
||||
$form->timeopen = "";
|
||||
}
|
||||
if (!isset($form->timeclose)) {
|
||||
$form->timeclose = "";
|
||||
}
|
||||
if (!isset($form->attempts)) {
|
||||
$form->attempts = $CFG->quiz_attempts;
|
||||
}
|
||||
if (!isset($form->attemptonlast)) {
|
||||
$form->attemptonlast = $CFG->quiz_attemptonlast;
|
||||
}
|
||||
if (!isset($form->grademethod)) {
|
||||
$form->grademethod = $CFG->quiz_grademethod;
|
||||
}
|
||||
if (!isset($form->decimalpoints)) {
|
||||
$form->decimalpoints = $CFG->quiz_decimalpoints;
|
||||
}
|
||||
if (!isset($form->review)) {
|
||||
$form->review = $CFG->quiz_review;
|
||||
}
|
||||
if (!isset($form->questionsperpage)) {
|
||||
$form->questionsperpage = $CFG->quiz_questionsperpage;
|
||||
}
|
||||
if (!isset($form->shufflequestions)) {
|
||||
$form->shufflequestions = $CFG->quiz_shufflequestions;
|
||||
}
|
||||
if (!isset($form->shuffleanswers)) {
|
||||
$form->shuffleanswers = $CFG->quiz_shuffleanswers;
|
||||
}
|
||||
if (!isset($form->grade)) {
|
||||
$form->grade = $CFG->quiz_maximumgrade;
|
||||
}
|
||||
if (!isset($form->questions)) {
|
||||
$form->questions = "";
|
||||
}
|
||||
if (!isset($form->password)) {
|
||||
$form->password = $CFG->quiz_password;
|
||||
}
|
||||
if (!isset($form->subnet)) {
|
||||
$form->subnet = $CFG->quiz_subnet;
|
||||
}
|
||||
if (!isset($form->timelimit)) {
|
||||
$form->timelimit = $CFG->quiz_timelimit;
|
||||
}
|
||||
if (!isset($form->popup)) {
|
||||
$form->popup = $CFG->quiz_popup;
|
||||
}
|
||||
if (!isset($form->optionflags)) {
|
||||
$form->optionflags = $CFG->quiz_optionflags;
|
||||
}
|
||||
if (!isset($form->penaltyscheme)) {
|
||||
$form->penaltyscheme = $CFG->quiz_penaltyscheme;
|
||||
}
|
||||
|
||||
?>
|
||||
<form method="post" action="module.php" name="form">
|
||||
<input type="hidden" name="sesskey" value="<?php echo $USER->sesskey ?>">
|
||||
@@ -75,6 +131,26 @@
|
||||
|
||||
<!-- Table of default values -->
|
||||
|
||||
<?php
|
||||
// the following are used for drop-down menus
|
||||
|
||||
$yesnooptions = array();
|
||||
$yesnooptions[0] = get_string("no");
|
||||
$yesnooptions[1] = get_string("yes");
|
||||
|
||||
$attemptoptions = array();
|
||||
$attemptoptions[0] = get_string("attemptsunlimited", "quiz");
|
||||
$attemptoptions[1] = "1 ".strtolower(get_string("attempt", "quiz"));
|
||||
for ($i=2;$i<=6;$i++) {
|
||||
$attemptoptions[$i] = "$i ".strtolower(get_string("attempts", "quiz"));
|
||||
}
|
||||
|
||||
$penaltyoptions = array();
|
||||
$penaltyoptions[QUIZ_PENALTYNONE] = get_string('penaltynone', 'quiz');
|
||||
$penaltyoptions[QUIZ_PENALTYMULTIPLY] = get_string('penaltymultiply', 'quiz');
|
||||
$penaltyoptions[QUIZ_PENALTYSUBTRACT] = get_string('penaltysubtract', 'quiz');
|
||||
?>
|
||||
|
||||
<table cellpadding="9" cellspacing="0" align="center">
|
||||
|
||||
<tr valign="top">
|
||||
@@ -85,19 +161,19 @@
|
||||
<th align="center">
|
||||
<?php print_string('advancedsettings'); helpbutton("advancedsettings"); ?>
|
||||
</th>
|
||||
</tr>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("timelimit", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
print_timer_selector($CFG->quiz_timelimit, get_string("minutes","quiz"), 'quiz_timelimit');
|
||||
<?php
|
||||
print_timer_selector($form->timelimit, get_string("minutes","quiz"), 'timelimit');
|
||||
helpbutton("timelimit", get_string("quiztimer","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_timelimit" value="0">
|
||||
<input type="checkbox" name="quiz_fix_timelimit" value="1" <?php p($CFG->quiz_fix_timelimit ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_timelimit" value="0">
|
||||
<input type="checkbox" name="fix_timelimit" value="1" <?php p($form->fix_timelimit ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -111,13 +187,13 @@
|
||||
}
|
||||
$perpage[0] = get_string('allinone', 'quiz');
|
||||
|
||||
choose_from_menu($perpage, 'questionsperpage', $CFG->quiz_questionsperpage, '');
|
||||
choose_from_menu($perpage, 'questionsperpage', $form->questionsperpage, '');
|
||||
helpbutton('questionsperpage', get_string('questionsperpage'), 'quiz');
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_questionsperpage" value="0">
|
||||
<input type="checkbox" name="quiz_fix_questionsperpage" value="1" <?php p($CFG->quiz_fix_questionsperpage ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_questionsperpage" value="0">
|
||||
<input type="checkbox" name="fix_questionsperpage" value="1" <?php p($form->fix_questionsperpage ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -128,13 +204,13 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_shufflequestions", $CFG->quiz_shufflequestions, "", "", "");
|
||||
choose_from_menu ($options, "shufflequestions", $form->shufflequestions, "", "", "");
|
||||
helpbutton("shufflequestions", get_string("shufflequestions","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_shufflequestions" value="0">
|
||||
<input type="checkbox" name="quiz_fix_shufflequestions" value="1" <?php p($CFG->quiz_fix_shufflequestions ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_shufflequestions" value="0">
|
||||
<input type="checkbox" name="fix_shufflequestions" value="1" <?php p($form->fix_shufflequestions ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -145,13 +221,13 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_shuffleanswers", $CFG->quiz_shuffleanswers, "", "", "");
|
||||
choose_from_menu ($options, "shuffleanswers", $form->shuffleanswers, "", "", "");
|
||||
helpbutton("attempts", get_string("attemptsallowed","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_shuffleanswers" value="0">
|
||||
<input type="checkbox" name="quiz_fix_shuffleanswers" value="1" <?php p($CFG->quiz_fix_shuffleanswers ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_shuffleanswers" value="0">
|
||||
<input type="checkbox" name="fix_shuffleanswers" value="1" <?php p($form->fix_shuffleanswers ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -165,13 +241,13 @@
|
||||
for ($i=2;$i<=6;$i++) {
|
||||
$options[$i] = "$i ".strtolower(get_string("attempts", "quiz"));
|
||||
}
|
||||
choose_from_menu ($options, "quiz_attempts", $CFG->quiz_attempts, "", "", "");
|
||||
choose_from_menu ($options, "attempts", $form->attempts, "", "", "");
|
||||
helpbutton("attempts", get_string("attemptsallowed","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_attempts" value="0">
|
||||
<input type="checkbox" name="quiz_fix_attempts" value="1" <?php p($CFG->quiz_fix_attempts ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_attempts" value="0">
|
||||
<input type="checkbox" name="fix_attempts" value="1" <?php p($form->fix_attempts ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -182,13 +258,13 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_attemptonlast", $CFG->quiz_attemptonlast, "", "", "");
|
||||
choose_from_menu ($options, "attemptonlast", $form->attemptonlast, "", "", "");
|
||||
helpbutton("repeatattempts", get_string("eachattemptbuildsonthelast", "quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_attemptonlast" value="0">
|
||||
<input type="checkbox" name="quiz_fix_attemptonlast" value="1" <?php p($CFG->quiz_fix_attemptonlast ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_attemptonlast" value="0">
|
||||
<input type="checkbox" name="fix_attemptonlast" value="1" <?php p($form->fix_attemptonlast ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -196,13 +272,41 @@
|
||||
<td align="right"><b><?php print_string("grademethod", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu ($QUIZ_GRADE_METHOD, "quiz_grademethod", $CFG->quiz_grademethod, "", "", "");
|
||||
choose_from_menu ($QUIZ_GRADE_METHOD, "grademethod", $form->grademethod, "", "", "");
|
||||
helpbutton("grademethod", get_string("grademethod","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_grademethod" value="0">
|
||||
<input type="checkbox" name="quiz_fix_grademethod" value="1" <?php p($CFG->quiz_fix_grademethod ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_grademethod" value="0">
|
||||
<input type="checkbox" name="fix_grademethod" value="1" <?php p($form->fix_grademethod ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("adaptive", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "adaptive", $CFG->quiz_optionflags & QUIZ_ADAPTIVE, "");
|
||||
helpbutton("adaptive", get_string("adaptive","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="fix_adaptive" value="0">
|
||||
<input type="checkbox" name="fix_adaptive" value="1" <?php p($form->fix_adaptive ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("penaltyscheme", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($penaltyoptions, "penaltyscheme", "$form->penaltyscheme", "");
|
||||
helpbutton("penaltyscheme", get_string("penaltyscheme","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="fix_penaltyscheme" value="0">
|
||||
<input type="checkbox" name="fix_penaltyscheme" value="1" <?php p($form->fix_penaltyscheme ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -215,13 +319,13 @@
|
||||
$options[1] = '1';
|
||||
$options[2] = '2';
|
||||
$options[3] = '3';
|
||||
choose_from_menu ($options, "quiz_decimalpoints", $CFG->quiz_decimalpoints, "", "", "");
|
||||
choose_from_menu ($options, "decimalpoints", $form->decimalpoints, "", "", "");
|
||||
helpbutton("decimalpoints", get_string("decimalpoints","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_decimalpoints" value="0">
|
||||
<input type="checkbox" name="quiz_fix_decimalpoints" value="1" <?php p($CFG->quiz_fix_decimalpoints ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_decimalpoints" value="0">
|
||||
<input type="checkbox" name="fix_decimalpoints" value="1" <?php p($form->fix_decimalpoints ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -232,13 +336,13 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_showfeedback", $CFG->quiz_showfeedback, "", "", "");
|
||||
choose_from_menu ($options, "showfeedback", $form->showfeedback, "", "", "");
|
||||
helpbutton("feedback", get_string("showfeedback","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_showfeedback" value="0">
|
||||
<input type="checkbox" name="quiz_fix_showfeedback" value="1" <?php p($CFG->quiz_fix_showfeedback ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_showfeedback" value="0">
|
||||
<input type="checkbox" name="fix_showfeedback" value="1" <?php p($form->fix_showfeedback ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -249,51 +353,21 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_showanswer", $CFG->quiz_showanswer, "", "", "");
|
||||
choose_from_menu ($options, "showanswer", $form->showanswer, "", "", "");
|
||||
helpbutton("correctanswers", get_string("showcorrectanswer","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_showanswer" value="0">
|
||||
<input type="checkbox" name="quiz_fix_showanswer" value="1" <?php p($CFG->quiz_fix_showanswer ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_showanswer" value="0">
|
||||
<input type="checkbox" name="fix_showanswer" value="1" <?php p($form->fix_showanswer ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("allowreview", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
$reviewoptions = array();
|
||||
$reviewoptions[0] = get_string('reviewnever', 'quiz');
|
||||
$reviewoptions[1] = get_string('reviewafter', 'quiz');
|
||||
$reviewoptions[2] = get_string('reviewbefore', 'quiz');
|
||||
$reviewoptions[3] = get_string('reviewalways', 'quiz');
|
||||
choose_from_menu ($reviewoptions, "quiz_allowreview", $CFG->quiz_allowreview, "", "", "");
|
||||
helpbutton("review2", get_string("allowreview","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<?php include('reviewoptions.html'); ?>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_allowreview" value="0">
|
||||
<input type="checkbox" name="quiz_fix_allowreview" value="1" <?php p($CFG->quiz_fix_allowreview ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("maximumgrade") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
unset($grades);
|
||||
for ($i=100; $i>=1; $i--) {
|
||||
$grades[$i] = $i;
|
||||
}
|
||||
$grades[0] = get_string("nograde");
|
||||
choose_from_menu ($grades, "quiz_maximumgrade", $CFG->quiz_maximumgrade, "", "", "");
|
||||
helpbutton("maxgrade", get_string("maximumgrade"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_maximumgrade" value="0">
|
||||
<input type="checkbox" name="quiz_fix_maximumgrade" value="1" <?php p($CFG->quiz_fix_maximumgrade ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_review" value="0">
|
||||
<input type="checkbox" name="fix_review" value="1" <?php p($form->fix_review ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -304,42 +378,65 @@
|
||||
unset($options);
|
||||
$options[0] = get_string("no");
|
||||
$options[1] = get_string("yes");
|
||||
choose_from_menu ($options, "quiz_popup", $CFG->quiz_popup, "", "", "");
|
||||
choose_from_menu ($options, "popup", $form->popup, "", "", "");
|
||||
helpbutton("popup", get_string("popup","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_popup" value="0">
|
||||
<input type="checkbox" name="quiz_fix_popup" value="1" <?php p($CFG->quiz_fix_popup ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_popup" value="0">
|
||||
<input type="checkbox" name="fix_popup" value="1" <?php p($form->fix_popup ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("requirepassword", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<input type="text" name="quiz_password" size="20" value="<?php p($CFG->quiz_password) ?>" />
|
||||
<input type="text" name="password" size="20" value="<?php p($form->password) ?>" />
|
||||
<?php helpbutton("requirepassword", get_string("requirepassword", "quiz"), "quiz"); ?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_password" value="0">
|
||||
<input type="checkbox" name="quiz_fix_password" value="1" <?php p($CFG->quiz_fix_password ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_password" value="0">
|
||||
<input type="checkbox" name="fix_password" value="1" <?php p($form->fix_password ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("requiresubnet", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<input type="text" name="quiz_subnet" size="20" value="<?php p($CFG->quiz_subnet) ?>" />
|
||||
<input type="text" name="subnet" size="20" value="<?php p($form->subnet) ?>" />
|
||||
<?php helpbutton("requiresubnet", get_string("requiresubnet", "quiz"), "quiz"); ?>
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="hidden" name="quiz_fix_subnet" value="0">
|
||||
<input type="checkbox" name="quiz_fix_subnet" value="1" <?php p($CFG->quiz_fix_subnet ? "checked" : "") ?> />
|
||||
<input type="hidden" name="fix_subnet" value="0">
|
||||
<input type="checkbox" name="fix_subnet" value="1" <?php p($form->fix_subnet ? "checked" : "") ?> />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("maximumgrade") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
unset($grades);
|
||||
for ($i=100; $i>=1; $i--) {
|
||||
$grades[$i] = $i;
|
||||
}
|
||||
$grades[0] = get_string("nograde");
|
||||
choose_from_menu ($grades, "maximumgrade", $form->maximumgrade, "", "", "");
|
||||
helpbutton("maxgrade", get_string("maximumgrade"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
<!-- Can't hide this setting, it always shows up on edit.php
|
||||
<td align="center">
|
||||
<input type="hidden" name="fix_maximumgrade" value="0">
|
||||
<input type="checkbox" name="fix_maximumgrade" value="1" <?php p($form->fix_maximumgrade ? "checked" : "") ?> />
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="3" align="center">
|
||||
<input type="hidden" name="module" value="quiz" /> </input>
|
||||
<input type="submit" value="<?php print_string("savechanges") ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+287
-5
@@ -4,8 +4,8 @@ function quiz_upgrade($oldversion) {
|
||||
// This function does anything necessary to upgrade
|
||||
// older versions to match current functionality
|
||||
|
||||
global $CFG;
|
||||
include_once("$CFG->dirroot/mod/quiz/locallib.php");
|
||||
global $CFG, $QUIZ_QTYPES;
|
||||
require_once("$CFG->dirroot/mod/quiz/locallib.php");
|
||||
|
||||
if ($oldversion < 2002101800) {
|
||||
execute_sql(" ALTER TABLE `quiz_attempts` ".
|
||||
@@ -208,7 +208,6 @@ function quiz_upgrade($oldversion) {
|
||||
if ($oldversion < 2004073001) {
|
||||
// Six new tables:
|
||||
|
||||
|
||||
// One table for handling units for numerical questions
|
||||
modify_database ("", " CREATE TABLE `prefix_quiz_numerical_units` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
@@ -218,7 +217,6 @@ function quiz_upgrade($oldversion) {
|
||||
PRIMARY KEY (`id`)
|
||||
) TYPE=MyISAM COMMENT='Optional unit options for numerical questions'; ");
|
||||
|
||||
|
||||
// Four tables for handling distribution and storage of
|
||||
// individual data for dataset dependent question types
|
||||
modify_database ("", " CREATE TABLE `prefix_quiz_attemptonlast_datasets` (
|
||||
@@ -347,13 +345,297 @@ function quiz_upgrade($oldversion) {
|
||||
}
|
||||
|
||||
if ($oldversion < 2005041304) {
|
||||
// make random questions hidden
|
||||
modify_database('', "UPDATE prefix_quiz_questions SET hidden = '1' WHERE qtype ='".RANDOM."';");
|
||||
}
|
||||
|
||||
|
||||
if ($oldversion < 2005042002) {
|
||||
table_column('quiz_answers', 'answer', 'answer', 'text', '', '', '', 'not null', '');
|
||||
}
|
||||
|
||||
if ($oldversion < 2005042400) {
|
||||
|
||||
// Changes to quiz table
|
||||
|
||||
// The bits of the optionflags field will hold various option flags
|
||||
table_column('quiz', '', 'optionflags', 'integer', '10', 'unsigned', '0', 'not null', 'timeclose');
|
||||
|
||||
// The penalty scheme
|
||||
table_column('quiz', '', 'penaltyscheme', 'integer', '4', 'unsigned', '0', 'not null', 'optionflags');
|
||||
|
||||
// The review options are now all stored in the bits of the review field
|
||||
table_column('quiz', 'review', 'review', 'integer', 10, 'unsigned', 0, 'not null', '');
|
||||
// We need to set the bits using the data in the existing fields
|
||||
if ($quizzes = get_records('quiz')) {
|
||||
foreach ($quizzes as $quiz) {
|
||||
$review = (QUIZ_REVIEW_IMMEDIATELY & (QUIZ_REVIEW_RESPONSES + QUIZ_REVIEW_SCORES));
|
||||
if ($quiz->feedback) {
|
||||
$review += (QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_FEEDBACK);
|
||||
}
|
||||
if ($quiz->correctanswers) {
|
||||
$review += (QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_ANSWERS);
|
||||
}
|
||||
if ($quiz->review & 1) {
|
||||
$review += QUIZ_REVIEW_CLOSED;
|
||||
}
|
||||
if ($quiz->review & 2) {
|
||||
$review += QUIZ_REVIEW_OPEN;
|
||||
}
|
||||
set_field('quiz', 'review', $review, 'id', $quiz->id);
|
||||
}
|
||||
}
|
||||
// update the default settings in $CFG
|
||||
$review = (QUIZ_REVIEW_IMMEDIATELY & (QUIZ_REVIEW_RESPONSES + QUIZ_REVIEW_SCORES));
|
||||
if (!empty($CFG->quiz_feedback)) {
|
||||
$review += (QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_FEEDBACK);
|
||||
}
|
||||
if (!empty($CFG->quiz_correctanswers)) {
|
||||
$review += (QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_ANSWERS);
|
||||
}
|
||||
if (isset($CFG->quiz_review) and ($CFG->quiz_review & 1)) {
|
||||
$review += QUIZ_REVIEW_CLOSED;
|
||||
}
|
||||
if (isset($CFG->quiz_review) and ($CFG->quiz_review & 2)) {
|
||||
$review += QUIZ_REVIEW_OPEN;
|
||||
}
|
||||
set_config('quiz_review', $review);
|
||||
|
||||
// We can now drop the fields whose data has been moved to the review field
|
||||
execute_sql(" ALTER TABLE `{$CFG->prefix}quiz` DROP feedback");
|
||||
execute_sql(" ALTER TABLE `{$CFG->prefix}quiz` DROP correctanswers");
|
||||
|
||||
/// Changes to quiz_attempts table
|
||||
|
||||
// The preview flag marks teacher previews
|
||||
table_column('quiz_attempts', '', 'preview', 'tinyint', '2', 'unsigned', '0', 'not null', 'timemodified');
|
||||
|
||||
// The layout is the list of questions with inserted page breaks.
|
||||
table_column('quiz_attempts', '', 'layout', 'text', '', '', '', 'not null', 'timemodified');
|
||||
// For old quiz attempts we will set this to the repaginated question list from $quiz->questions
|
||||
|
||||
if ($quizzes) {
|
||||
$quiz->questions = ($quiz->questionsperpage) ? quiz_repaginate($quiz->questions, $quiz->questionsperpage) : $quiz->questions;
|
||||
foreach ($quizzes as $quiz) {
|
||||
|
||||
// repaginate
|
||||
if ($quiz->questionsperpage) {
|
||||
$quiz->questions = quiz_repaginate($quiz->questions, $quiz->questionsperpage);
|
||||
set_field('quiz', 'questions', $quiz->questions, 'id', $quiz->id);
|
||||
}
|
||||
set_field('quiz_attempts', 'layout', $quiz->questions, 'quiz', $quiz->id);
|
||||
|
||||
// set preview flag
|
||||
if ($teachers = get_course_teachers($quiz->course)) {
|
||||
$teacherids = implode(',', array_keys($teachers));
|
||||
execute_sql("UPDATE {$CFG->prefix}quiz_attempts SET preview = 1 WHERE userid IN ($teacherids)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renaming tables
|
||||
|
||||
// rename the quiz_question_grades table to quiz_question_instances
|
||||
modify_database ('', 'ALTER TABLE prefix_quiz_question_grades RENAME prefix_quiz_question_instances;');
|
||||
|
||||
// rename the quiz_responses table quiz_states
|
||||
modify_database ('', 'ALTER TABLE prefix_quiz_responses RENAME prefix_quiz_states;');
|
||||
|
||||
/// add columns to quiz_states table
|
||||
|
||||
// The sequence number of the state.
|
||||
table_column('quiz_states', '', 'seq_number', 'integer', '6', 'unsigned', '0', 'not null', 'originalquestion');
|
||||
// For existing states we leave this at 0 because in the old quiz code there was only one response allowed
|
||||
|
||||
// The time the state was created.
|
||||
table_column('quiz_states', '', 'timestamp', 'integer', '10', 'unsigned', '0', 'not null', 'answer');
|
||||
// For existing states we will below set this to the timemodified field of the attempt
|
||||
|
||||
// The type of event that led to the creation of the state
|
||||
table_column('quiz_states', '', 'event', 'integer', '4', 'unsigned', '0', 'not null', 'timestamp');
|
||||
|
||||
// The raw grade
|
||||
table_column('quiz_states', '', 'raw_grade', 'varchar', '10', '', '', 'not null', 'grade');
|
||||
// For existing states (no penalties) this is equal to the grade
|
||||
execute_sql("UPDATE {$CFG->prefix}quiz_states SET raw_grade = grade");
|
||||
|
||||
// The penalty that the response attracted
|
||||
table_column('quiz_states', '', 'penalty', 'varchar', '10', '', '0.0', 'not null', 'raw_grade');
|
||||
// For existing states this can stay at 0 because penalties did not exist previously.
|
||||
|
||||
/// New table for pointers to newest and newest graded states
|
||||
|
||||
modify_database('', "CREATE TABLE `prefix_quiz_newest_states` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`attemptid` int(10) unsigned NOT NULL default '0',
|
||||
`questionid` int(10) unsigned NOT NULL default '0',
|
||||
`new` int(10) unsigned NOT NULL default '0',
|
||||
`newgraded` int(10) unsigned NOT NULL default '0',
|
||||
`sumpenalty` varchar(10) NOT NULL default '0.0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `attemptid` (`attemptid`,`questionid`)
|
||||
) TYPE=MyISAM COMMENT='Gives ids of the newest open and newest graded states';");
|
||||
|
||||
/// Now upgrade states and newest_states where necessary
|
||||
// to save time on large sites only do this for attempts that have not yet been finished.
|
||||
if ($attempts = get_records_select('quiz_attempts', 'timefinish = 0')) {
|
||||
foreach ($attempts as $attempt) {
|
||||
quiz_upgrade_states($attempt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Entries for the log_display table
|
||||
|
||||
modify_database('', " INSERT INTO prefix_log_display VALUES ('quiz', 'preview', 'quiz', 'name');");
|
||||
modify_database('', " INSERT INTO prefix_log_display VALUES ('quiz', 'start attempt', 'quiz', 'name');");
|
||||
modify_database('', " INSERT INTO prefix_log_display VALUES ('quiz', 'close attempt', 'quiz', 'name');");
|
||||
|
||||
/// Fix numerical question type
|
||||
table_column('quiz_numerical', '', 'tolerance', 'varchar', '255', '', '0.0', 'not null', 'question');
|
||||
if($rows = get_records('quiz_numerical')) {
|
||||
foreach ($rows as $row) {
|
||||
$row->tolerance = ((float)$row->min + (float)$row->max)/2;
|
||||
set_field('quiz_numerical', 'tolerance', $row->tolerance, 'id', $row->id);
|
||||
}
|
||||
}
|
||||
// modify_database('', 'ALTER TABLE `prefix_quiz_numerical` DROP `answer`'); // There is no need for it at all
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_numerical` DROP `min`'); // Replaced by tolerance
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_numerical` DROP `max`'); // Replaced by tolerance
|
||||
|
||||
/// Tables for Remote Questions
|
||||
modify_database ('', "CREATE TABLE `prefix_quiz_rqp` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`type` int(10) unsigned NOT NULL default '0',
|
||||
`source` longblob NOT NULL default '',
|
||||
`format` varchar(255) NOT NULL default '',
|
||||
`flags` tinyint(3) unsigned NOT NULL default '0',
|
||||
`maxscore` int(10) unsigned NOT NULL default '1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
) TYPE=MyISAM COMMENT='Options for RQP questions';");
|
||||
|
||||
modify_database ('', "CREATE TABLE `prefix_quiz_rqp_type` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`name` varchar(255) NOT NULL default '',
|
||||
`rendering_server` varchar(255) NOT NULL default '',
|
||||
`cloning_server` varchar(255) NOT NULL default '',
|
||||
`flags` tinyint(3) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) TYPE=MyISAM COMMENT='RQP question types and the servers to be used to process them';");
|
||||
|
||||
modify_database ('', "CREATE TABLE `prefix_quiz_rqp_states` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`stateid` int(10) unsigned NOT NULL default '0',
|
||||
`responses` text NOT NULL default '',
|
||||
`persistent_data` text NOT NULL default '',
|
||||
`template_vars` text NOT NULL default '',
|
||||
PRIMARY KEY (`id`)
|
||||
) TYPE=MyISAM COMMENT='RQP question type specific state information';");
|
||||
}
|
||||
|
||||
if ($oldversion < 2005042900) {
|
||||
|
||||
table_column('quiz_multianswers', '', 'sequence', 'varchar', '255', '', '', 'not null', 'question');
|
||||
table_column('quiz_numerical', '', 'answers', 'varchar', '255', '', '', 'not null', 'answer');
|
||||
modify_database('', 'UPDATE prefix_quiz_numerical SET answers = answer');
|
||||
table_column('quiz_questions', '', 'parent', 'integer', '10', 'unsigned', '0', 'not null', 'category');
|
||||
modify_database('', "UPDATE prefix_quiz_questions SET parent = id WHERE qtype ='".RANDOM."';");
|
||||
|
||||
// convert multianswer questions to the new model
|
||||
if ($multianswers = get_records_sql("SELECT m.id, q.category, q.id AS parent,
|
||||
q.name, q.questiontextformat, m.norm AS
|
||||
defaultgrade, m.answertype AS qtype,
|
||||
q.version, q.hidden, m.answers,
|
||||
m.positionkey
|
||||
FROM {$CFG->prefix}quiz_questions q,
|
||||
{$CFG->prefix}quiz_multianswers m
|
||||
WHERE q.qtype = '".MULTIANSWER."'
|
||||
AND q.id = m.question
|
||||
ORDER BY q.id ASC, m.positionkey ASC")) {
|
||||
$multianswers = array_values($multianswers);
|
||||
$n = count($multianswers);
|
||||
$parent = $multianswers[0]->parent;
|
||||
$sequence = array();
|
||||
|
||||
// turn reporting off temporarily to avoid one line output per set_field
|
||||
global $db;
|
||||
$olddebug = $db->debug;
|
||||
// $db->debug = false;
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$answers = $multianswers[$i]->answers; unset($multianswers[$i]->answers);
|
||||
$pos = $multianswers[$i]->positionkey; unset($multianswers[$i]->positionkey);
|
||||
|
||||
// create questions for all the multianswer victims
|
||||
unset($multianswers[$i]->id);
|
||||
$multianswers[$i]->length = 0;
|
||||
$multianswers[$i]->questiontext = '';
|
||||
$multianswers[$i]->stamp = make_unique_id_code();
|
||||
$id = insert_record('quiz_questions', $multianswers[$i]);
|
||||
$sequence[$pos] = $id;
|
||||
|
||||
// update the answers table to point to these new questions
|
||||
modify_database('', "UPDATE prefix_quiz_answers SET question = '$id' WHERE id IN ($answers);");
|
||||
// update the questiontype tables to point to these new questions
|
||||
if (SHORTANSWER == $multianswers[$i]->qtype) {
|
||||
modify_database('', "UPDATE prefix_quiz_shortanswer SET question = '$id' WHERE answers = '$answers';");
|
||||
} else if (NUMERICAL == $multianswers[$i]->qtype) {
|
||||
if (strpos($answers, ',')) {
|
||||
$numerical = get_records_list('quiz_numerical', 'answer', $answers);
|
||||
// Get the biggest tolerance value
|
||||
$tolerance = 0;
|
||||
foreach ($numerical as $num) {
|
||||
$tolerance = ($tolerance < $num->tolerance ? $num->tolerance : $tolerance);
|
||||
}
|
||||
delete_records_select('quiz_numerical', "answer IN ($answers)");
|
||||
$new = new stdClass;
|
||||
$new->question = $id;
|
||||
$new->tolerance = $tolerance;
|
||||
$new->answers = $answers;
|
||||
insert_record('quiz_numerical', $new);
|
||||
unset($numerical, $new, $tolerance);
|
||||
} else {
|
||||
modify_database('', "UPDATE prefix_quiz_numerical SET question = '$id', answers = '$answers' WHERE answer IN ($answers);");
|
||||
}
|
||||
} else if (MULTICHOICE == $multianswers[$i]->qtype) {
|
||||
modify_database('', "UPDATE prefix_quiz_multichoice SET question = '$id' WHERE answers = '$answers';");
|
||||
}
|
||||
|
||||
if (!isset($multianswers[$i+1]) || $parent != $multianswers[$i+1]->parent) {
|
||||
delete_records('quiz_multianswers', 'question', $parent);
|
||||
$multi = new stdClass;
|
||||
$multi->question = $parent;
|
||||
$multi->sequence = implode(',', $sequence);
|
||||
insert_record('quiz_multianswers', $multi);
|
||||
if (isset($multianswers[$i+1])) {
|
||||
$parent = $multianswers[$i+1]->parent;
|
||||
$sequence = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
$db->debug = $olddebug;
|
||||
}
|
||||
|
||||
// Remove redundant fields from quiz_multianswers
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_multianswers` DROP `answers`');
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_multianswers` DROP `positionkey`');
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_multianswers` DROP `answertype`');
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_multianswers` DROP `norm`');
|
||||
|
||||
// Change numerical from answer to answers
|
||||
modify_database('', 'ALTER TABLE `prefix_quiz_numerical` DROP `answer`');
|
||||
}
|
||||
|
||||
if ($oldversion < 2005050300) {
|
||||
// length of question determines question numbering. Currently all questions require one
|
||||
// question number except for DESCRIPTION questions.
|
||||
table_column('quiz_questions', '', 'length', 'integer', '10', 'unsigned', '1', 'not null', 'qtype');
|
||||
execute_sql("UPDATE {$CFG->prefix}quiz_questions SET length = 0 WHERE qtype = ".DESCRIPTION);
|
||||
}
|
||||
|
||||
if ($oldversion < 2005050500) {
|
||||
table_column('quiz_questions', '', 'penalty', 'float', '', '', '0.1', 'not null', 'defaultgrade');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+416
-318
@@ -1,389 +1,484 @@
|
||||
# phpMyAdmin SQL Dump
|
||||
# version 2.5.7
|
||||
# http://www.phpmyadmin.net
|
||||
#
|
||||
# Host: localhost
|
||||
# Generation Time: Jul 30, 2004 at 02:52 PM
|
||||
# Server version: 4.0.18
|
||||
# PHP Version: 4.3.7
|
||||
# Database : `moodle`
|
||||
# --------------------------------------------------------
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 2.6.0-pl1
|
||||
-- http://www.phpmyadmin.net
|
||||
--
|
||||
-- Host: localhost
|
||||
-- Generation Time: May 04, 2005 at 06:27 PM
|
||||
-- Server version: 4.0.15
|
||||
-- PHP Version: 4.3.3
|
||||
--
|
||||
-- Database: `upgrading`
|
||||
--
|
||||
|
||||
#
|
||||
# Table structure for table `quiz`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`course` int(10) unsigned NOT NULL default '0',
|
||||
`name` varchar(255) NOT NULL default '',
|
||||
`intro` text NOT NULL,
|
||||
`timeopen` int(10) unsigned NOT NULL default '0',
|
||||
`timeclose` int(10) unsigned NOT NULL default '0',
|
||||
`attempts` smallint(6) NOT NULL default '0',
|
||||
`attemptonlast` tinyint(4) NOT NULL default '0',
|
||||
`feedback` tinyint(4) NOT NULL default '0',
|
||||
`correctanswers` tinyint(4) NOT NULL default '1',
|
||||
`grademethod` tinyint(4) NOT NULL default '1',
|
||||
`decimalpoints` tinyint(4) NOT NULL default '2',
|
||||
`review` tinyint(4) NOT NULL default '0',
|
||||
`questionsperpage` int(10) NOT NULL default '0',
|
||||
`shufflequestions` tinyint(4) NOT NULL default '0',
|
||||
`shuffleanswers` tinyint(4) NOT NULL default '0',
|
||||
`questions` text NOT NULL,
|
||||
`sumgrades` int(10) NOT NULL default '0',
|
||||
`grade` int(10) NOT NULL default '0',
|
||||
`timecreated` int(10) unsigned NOT NULL default '0',
|
||||
`timemodified` int(10) unsigned NOT NULL default '0',
|
||||
`timelimit` int(2) unsigned NOT NULL default '0',
|
||||
`password` varchar(255) NOT NULL default '',
|
||||
`subnet` varchar(255) NOT NULL default '',
|
||||
`popup` tinyint(4) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `course` (`course`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
course int(10) unsigned NOT NULL default '0',
|
||||
name varchar(255) NOT NULL default '',
|
||||
intro text NOT NULL,
|
||||
timeopen int(10) unsigned NOT NULL default '0',
|
||||
timeclose int(10) unsigned NOT NULL default '0',
|
||||
optionflags int(10) unsigned NOT NULL default '0',
|
||||
penaltyscheme int(4) unsigned NOT NULL default '0',
|
||||
attempts smallint(6) NOT NULL default '0',
|
||||
attemptonlast tinyint(4) NOT NULL default '0',
|
||||
grademethod tinyint(4) NOT NULL default '1',
|
||||
decimalpoints int(4) NOT NULL default '2',
|
||||
review int(10) unsigned NOT NULL default '0',
|
||||
questionsperpage int(10) NOT NULL default '0',
|
||||
shufflequestions tinyint(4) NOT NULL default '0',
|
||||
shuffleanswers tinyint(4) NOT NULL default '0',
|
||||
questions text NOT NULL,
|
||||
sumgrades int(10) NOT NULL default '0',
|
||||
grade int(10) NOT NULL default '0',
|
||||
timecreated int(10) unsigned NOT NULL default '0',
|
||||
timemodified int(10) unsigned NOT NULL default '0',
|
||||
timelimit int(2) unsigned NOT NULL default '0',
|
||||
password varchar(255) NOT NULL default '',
|
||||
subnet varchar(255) NOT NULL default '',
|
||||
popup tinyint(4) NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY course (course)
|
||||
) TYPE=MyISAM COMMENT='Main information about each quiz';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_answers`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_answers` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answer` text NOT NULL,
|
||||
`fraction` varchar(10) NOT NULL default '0.0',
|
||||
`feedback` text NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_answers`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_answers (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
answer text NOT NULL,
|
||||
fraction varchar(10) NOT NULL default '0.0',
|
||||
feedback text NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Answers, with a fractional grade (0-1) and feedback';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_attemptonlast_datasets`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_attemptonlast_datasets` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`category` int(10) unsigned NOT NULL default '0',
|
||||
`userid` int(10) unsigned NOT NULL default '0',
|
||||
`datasetnumber` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `category` (`category`,`userid`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_attemptonlast_datasets`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_attemptonlast_datasets (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
category int(10) unsigned NOT NULL default '0',
|
||||
userid int(10) unsigned NOT NULL default '0',
|
||||
datasetnumber int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY category (category,userid)
|
||||
) TYPE=MyISAM COMMENT='Dataset number for attemptonlast attempts per user';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_attempts`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_attempts` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`quiz` int(10) unsigned NOT NULL default '0',
|
||||
`userid` int(10) unsigned NOT NULL default '0',
|
||||
`attempt` smallint(6) NOT NULL default '0',
|
||||
`sumgrades` varchar(10) NOT NULL default '0.0',
|
||||
`timestart` int(10) unsigned NOT NULL default '0',
|
||||
`timefinish` int(10) unsigned NOT NULL default '0',
|
||||
`timemodified` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `quiz` (`quiz`),
|
||||
KEY `userid` (`userid`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_attempts`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_attempts (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quiz int(10) unsigned NOT NULL default '0',
|
||||
userid int(10) unsigned NOT NULL default '0',
|
||||
attempt smallint(6) NOT NULL default '0',
|
||||
sumgrades varchar(10) NOT NULL default '0.0',
|
||||
timestart int(10) unsigned NOT NULL default '0',
|
||||
timefinish int(10) unsigned NOT NULL default '0',
|
||||
timemodified int(10) unsigned NOT NULL default '0',
|
||||
layout text NOT NULL,
|
||||
preview tinyint(3) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY quiz (quiz),
|
||||
KEY userid (userid)
|
||||
) TYPE=MyISAM COMMENT='Stores various attempts on a quiz';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_calculated`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_calculated` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answer` int(10) unsigned NOT NULL default '0',
|
||||
`tolerance` varchar(20) NOT NULL default '0.0',
|
||||
`tolerancetype` int(10) NOT NULL default '1',
|
||||
`correctanswerlength` int(10) NOT NULL default '2',
|
||||
`correctanswerformat` int(10) NOT NULL default '2',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`),
|
||||
KEY `answer` (`answer`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_calculated`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_calculated (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
answer int(10) unsigned NOT NULL default '0',
|
||||
tolerance varchar(20) NOT NULL default '0.0',
|
||||
tolerancetype int(10) NOT NULL default '1',
|
||||
correctanswerlength int(10) NOT NULL default '2',
|
||||
correctanswerformat int(10) NOT NULL default '2',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question),
|
||||
KEY answer (answer)
|
||||
) TYPE=MyISAM COMMENT='Options for questions of type calculated';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_categories`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_categories` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`course` int(10) unsigned NOT NULL default '0',
|
||||
`name` varchar(255) NOT NULL default '',
|
||||
`info` text NOT NULL,
|
||||
`publish` tinyint(4) NOT NULL default '0',
|
||||
`stamp` varchar(255) NOT NULL default '',
|
||||
`parent` int(10) unsigned NOT NULL default '0',
|
||||
`sortorder` int(10) unsigned NOT NULL default '999',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `course` (`course`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_categories`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_categories (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
course int(10) unsigned NOT NULL default '0',
|
||||
name varchar(255) NOT NULL default '',
|
||||
info text NOT NULL,
|
||||
publish tinyint(4) NOT NULL default '0',
|
||||
stamp varchar(255) NOT NULL default '',
|
||||
parent int(10) unsigned NOT NULL default '0',
|
||||
sortorder int(10) unsigned NOT NULL default '999',
|
||||
PRIMARY KEY (id),
|
||||
KEY course (course)
|
||||
) TYPE=MyISAM COMMENT='Categories are for grouping questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_dataset_definitions`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_dataset_definitions` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`category` int(10) unsigned NOT NULL default '0',
|
||||
`name` varchar(255) NOT NULL default '',
|
||||
`type` int(10) NOT NULL default '0',
|
||||
`options` varchar(255) NOT NULL default '',
|
||||
`itemcount` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `category` (`category`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_dataset_definitions`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_dataset_definitions (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
category int(10) unsigned NOT NULL default '0',
|
||||
name varchar(255) NOT NULL default '',
|
||||
type int(10) NOT NULL default '0',
|
||||
options varchar(255) NOT NULL default '',
|
||||
itemcount int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY category (category)
|
||||
) TYPE=MyISAM COMMENT='Organises and stores properties for dataset items';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_dataset_items`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_dataset_items` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`definition` int(10) unsigned NOT NULL default '0',
|
||||
`number` int(10) unsigned NOT NULL default '0',
|
||||
`value` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `definition` (`definition`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_dataset_items`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_dataset_items (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
definition int(10) unsigned NOT NULL default '0',
|
||||
number int(10) unsigned NOT NULL default '0',
|
||||
value varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY definition (definition)
|
||||
) TYPE=MyISAM COMMENT='Individual dataset items';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_grades`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_grades` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`quiz` int(10) unsigned NOT NULL default '0',
|
||||
`userid` int(10) unsigned NOT NULL default '0',
|
||||
`grade` real NOT NULL default '0',
|
||||
`timemodified` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `quiz` (`quiz`),
|
||||
KEY `userid` (`userid`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_grades`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_grades (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quiz int(10) unsigned NOT NULL default '0',
|
||||
userid int(10) unsigned NOT NULL default '0',
|
||||
grade double NOT NULL default '0',
|
||||
timemodified int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY quiz (quiz),
|
||||
KEY userid (userid)
|
||||
) TYPE=MyISAM COMMENT='Final quiz grade (may be best of several attempts)';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_match`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_match` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`subquestions` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_match`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_match (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
subquestions varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Defines fixed matching questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_match_sub`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_match_sub` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`questiontext` text NOT NULL,
|
||||
`answertext` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_match_sub`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_match_sub (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
questiontext text NOT NULL,
|
||||
answertext varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Defines the subquestions that make up a matching question';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_multianswers`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_multianswers` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answers` varchar(255) NOT NULL default '',
|
||||
`positionkey` varchar(255) NOT NULL default '',
|
||||
`answertype` smallint(6) NOT NULL default '0',
|
||||
`norm` int(10) unsigned NOT NULL default '1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_multianswers`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_multianswers (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
sequence varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for multianswer questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_multichoice`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_multichoice` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`layout` tinyint(4) NOT NULL default '0',
|
||||
`answers` varchar(255) NOT NULL default '',
|
||||
`single` tinyint(4) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_multichoice`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_multichoice (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
layout tinyint(4) NOT NULL default '0',
|
||||
answers varchar(255) NOT NULL default '',
|
||||
single tinyint(4) NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for multiple choice questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_numerical`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_numerical` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answer` int(10) unsigned NOT NULL default '0',
|
||||
`min` varchar(255) NOT NULL default '',
|
||||
`max` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `answer` (`answer`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_newest_states`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_newest_states (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
attemptid int(10) unsigned NOT NULL default '0',
|
||||
questionid int(10) unsigned NOT NULL default '0',
|
||||
new int(10) unsigned NOT NULL default '0',
|
||||
newgraded int(10) unsigned NOT NULL default '0',
|
||||
sumpenalty varchar(10) NOT NULL default '0.0',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY attemptid (attemptid,questionid)
|
||||
) TYPE=MyISAM COMMENT='Gives ids of the newest open and newest graded states';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_numerical`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_numerical (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
tolerance varchar(255) NOT NULL default '0.0',
|
||||
answers varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for numerical questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_numerical_units`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_numerical_units` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`multiplier` decimal(40,20) NOT NULL default '1.00000000000000000000',
|
||||
`unit` varchar(50) NOT NULL default '',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_numerical_units`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_numerical_units (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
multiplier decimal(40,20) NOT NULL default '1.00000000000000000000',
|
||||
unit varchar(50) NOT NULL default '',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Optional unit options for numerical questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_question_datasets`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_question_datasets` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`datasetdefinition` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`,`datasetdefinition`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_question_datasets`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_question_datasets (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
datasetdefinition int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question,datasetdefinition)
|
||||
) TYPE=MyISAM COMMENT='Many-many relation between questions and dataset definitions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_question_grades`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_question_grades` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`quiz` int(10) unsigned NOT NULL default '0',
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`grade` smallint(6) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `quiz` (`quiz`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_question_instances`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_question_instances (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quiz int(10) unsigned NOT NULL default '0',
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
grade smallint(6) NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY quiz (quiz),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='The grade for a question in a quiz';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_question_versions`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_question_versions` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`quiz` int(10) unsigned NOT NULL default '0',
|
||||
`oldquestion` int(10) unsigned NOT NULL default '0',
|
||||
`newquestion` int(10) unsigned NOT NULL default '0',
|
||||
`userid` int(10) unsigned NOT NULL default '0',
|
||||
`timestamp` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_question_versions`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_question_versions (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quiz int(10) unsigned NOT NULL default '0',
|
||||
oldquestion int(10) unsigned NOT NULL default '0',
|
||||
newquestion int(10) unsigned NOT NULL default '0',
|
||||
userid int(10) unsigned NOT NULL default '0',
|
||||
timestamp int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id)
|
||||
) TYPE=MyISAM COMMENT='The mapping between old and new versions of a question';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_questions`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_questions` (
|
||||
`id` int(10) NOT NULL auto_increment,
|
||||
`category` int(10) NOT NULL default '0',
|
||||
`name` varchar(255) NOT NULL default '',
|
||||
`questiontext` text NOT NULL,
|
||||
`questiontextformat` tinyint(2) NOT NULL default '0',
|
||||
`image` varchar(255) NOT NULL default '',
|
||||
`defaultgrade` int(10) unsigned NOT NULL default '1',
|
||||
`qtype` smallint(6) NOT NULL default '0',
|
||||
`stamp` varchar(255) NOT NULL default '',
|
||||
`version` int(10) NOT NULL default '1',
|
||||
`hidden` int(1) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `category` (`category`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_questions`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_questions (
|
||||
id int(10) NOT NULL auto_increment,
|
||||
category int(10) NOT NULL default '0',
|
||||
parent int(10) unsigned NOT NULL default '0',
|
||||
name varchar(255) NOT NULL default '',
|
||||
questiontext text NOT NULL,
|
||||
questiontextformat tinyint(2) NOT NULL default '0',
|
||||
image varchar(255) NOT NULL default '',
|
||||
defaultgrade int(10) unsigned NOT NULL default '1',
|
||||
penalty float NOT NULL default '0.1',
|
||||
qtype smallint(6) NOT NULL default '0',
|
||||
length int(10) unsigned NOT NULL default '1',
|
||||
stamp varchar(255) NOT NULL default '',
|
||||
version int(10) NOT NULL default '1',
|
||||
hidden int(1) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY category (category)
|
||||
) TYPE=MyISAM COMMENT='The quiz questions themselves';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_randomsamatch`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_randomsamatch` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`choose` int(10) unsigned NOT NULL default '4',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_randomsamatch`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_randomsamatch (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
choose int(10) unsigned NOT NULL default '4',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Info about a random short-answer matching question';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_responses`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_responses` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`attempt` int(10) unsigned NOT NULL default '0',
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`originalquestion` int(10) unsigned NOT NULL default '0',
|
||||
`answer` text NOT NULL default '',
|
||||
`grade` varchar(10) NOT NULL default '0.0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `attempt` (`attempt`),
|
||||
KEY `question` (`question`)
|
||||
) TYPE=MyISAM COMMENT='Stores user responses to a quiz, and percentage grades';
|
||||
# --------------------------------------------------------
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_rqp`
|
||||
--
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_shortanswer`
|
||||
#
|
||||
CREATE TABLE prefix_quiz_rqp (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
type int(10) unsigned NOT NULL default '0',
|
||||
source longblob NOT NULL,
|
||||
format varchar(255) NOT NULL default '',
|
||||
flags tinyint(3) unsigned NOT NULL default '0',
|
||||
maxscore int(10) unsigned NOT NULL default '1',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for RQP questions';
|
||||
|
||||
CREATE TABLE `prefix_quiz_shortanswer` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`answers` varchar(255) NOT NULL default '',
|
||||
`usecase` tinyint(2) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_rqp_states`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_rqp_states (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
stateid int(10) unsigned NOT NULL default '0',
|
||||
responses text NOT NULL,
|
||||
persistent_data text NOT NULL,
|
||||
template_vars text NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) TYPE=MyISAM COMMENT='RQP question type specific state information';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_rqp_type`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_rqp_type (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
name varchar(255) NOT NULL default '',
|
||||
rendering_server varchar(255) NOT NULL default '',
|
||||
cloning_server varchar(255) NOT NULL default '',
|
||||
flags tinyint(3) NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY name (name)
|
||||
) TYPE=MyISAM COMMENT='RQP question types and the servers to be used to process the';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_shortanswer`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_shortanswer (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
answers varchar(255) NOT NULL default '',
|
||||
usecase tinyint(2) NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for short answer questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
#
|
||||
# Table structure for table `quiz_truefalse`
|
||||
#
|
||||
-- --------------------------------------------------------
|
||||
|
||||
CREATE TABLE `prefix_quiz_truefalse` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`question` int(10) unsigned NOT NULL default '0',
|
||||
`trueanswer` int(10) unsigned NOT NULL default '0',
|
||||
`falseanswer` int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `question` (`question`)
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_states`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_states (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
attempt int(10) unsigned NOT NULL default '0',
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
originalquestion int(10) unsigned NOT NULL default '0',
|
||||
seq_number int(6) unsigned NOT NULL default '0',
|
||||
answer text NOT NULL,
|
||||
timestamp int(10) unsigned NOT NULL default '0',
|
||||
event int(4) unsigned NOT NULL default '0',
|
||||
grade varchar(10) NOT NULL default '0.0',
|
||||
raw_grade varchar(10) NOT NULL default '',
|
||||
penalty varchar(10) NOT NULL default '0.0',
|
||||
PRIMARY KEY (id),
|
||||
KEY attempt (attempt),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Stores user responses to a quiz, and percentage grades';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `prefix_quiz_truefalse`
|
||||
--
|
||||
|
||||
CREATE TABLE prefix_quiz_truefalse (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
question int(10) unsigned NOT NULL default '0',
|
||||
trueanswer int(10) unsigned NOT NULL default '0',
|
||||
falseanswer int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY question (question)
|
||||
) TYPE=MyISAM COMMENT='Options for True-False questions';
|
||||
# --------------------------------------------------------
|
||||
|
||||
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'add', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'update', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'view', 'quiz', 'name');
|
||||
@@ -392,3 +487,6 @@ INSERT INTO prefix_log_display VALUES ('quiz', 'attempt', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'submit', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'review', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'editquestions', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'preview', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'start attempt', 'quiz', 'name');
|
||||
INSERT INTO prefix_log_display VALUES ('quiz', 'close attempt', 'quiz', 'name');
|
||||
|
||||
+10
-10
@@ -4,37 +4,37 @@
|
||||
// It defines default values for any important configuration variables
|
||||
|
||||
$defaults = array (
|
||||
'quiz_allowreview' => 1,
|
||||
'quiz_review' => hexdec('ffffff'),
|
||||
'quiz_attemptonlast' => 0,
|
||||
'quiz_attempts' => 0,
|
||||
'quiz_grademethod' => '',
|
||||
'quiz_decimalpoints' => 2,
|
||||
'quiz_maximumgrade' => 0,
|
||||
'quiz_maximumgrade' => 10,
|
||||
'quiz_password' => '',
|
||||
'quiz_popup' => 0,
|
||||
'quiz_questionsperpage' => 0,
|
||||
'quiz_showanswer' => '',
|
||||
'quiz_showfeedback' => '',
|
||||
'quiz_shuffleanswers' => 1,
|
||||
'quiz_shufflequestions' => 0,
|
||||
'quiz_subnet' => '',
|
||||
'quiz_timelimit' => 0,
|
||||
|
||||
'quiz_fix_allowreview' => 0,
|
||||
'quiz_optionflags' => 1,
|
||||
'quiz_penaltyscheme' => 0,
|
||||
'quiz_adaptive' => 1,
|
||||
|
||||
'quiz_fix_review' => 0,
|
||||
'quiz_fix_attemptonlast' => 0,
|
||||
'quiz_fix_attempts' => 0,
|
||||
'quiz_fix_grademethod' => 0,
|
||||
'quiz_fix_decimalpoints' => 0,
|
||||
'quiz_fix_maximumgrade' => 0,
|
||||
'quiz_fix_password' => 0,
|
||||
'quiz_fix_popup' => 0,
|
||||
'quiz_fix_questionsperpage' => 0,
|
||||
'quiz_fix_showanswer' => 0,
|
||||
'quiz_fix_showfeedback' => 0,
|
||||
'quiz_fix_shuffleanswers' => 0,
|
||||
'quiz_fix_shufflequestions' => 0,
|
||||
'quiz_fix_subnet' => 0,
|
||||
'quiz_fix_timelimit' => 0
|
||||
'quiz_fix_timelimit' => 0,
|
||||
'quiz_fix_adaptive' => 0,
|
||||
'quiz_fix_penaltyscheme' => 0
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
+217
-165
@@ -1,7 +1,40 @@
|
||||
<?php // $Id$
|
||||
|
||||
/**
|
||||
* Page to edit quizzes
|
||||
*
|
||||
* This page generally has two columns:
|
||||
* The right column lists all available questions in a chosen category and
|
||||
* allows them to be edited or more to be added. This column is only there if
|
||||
* the quiz does not already have student attempts
|
||||
* The left column lists all questions that have been added to the current quiz.
|
||||
* This column is only there if this page has been called in the context of a quiz.
|
||||
* The lecturer can add questions from the right hand list to the quiz or remove them
|
||||
*
|
||||
* The script also processes a number of actions:
|
||||
* Actions affecting a quiz:
|
||||
* up and down Changes the order of questions and page breaks
|
||||
* addquestion Adds a single question to the quiz
|
||||
* add Adds several selected questions to the quiz
|
||||
* addrandom Adds a certain number of random questions to the quiz
|
||||
* delete Removes a question from the quiz
|
||||
* setgrades Changes the maximum grades for questions in the quiz
|
||||
* repaginate Re-paginates the quiz
|
||||
* Actions affecting the question pool:
|
||||
* move Moves a question to a different category
|
||||
* deleteselected Deletes the selected questions from the category
|
||||
* Other actions:
|
||||
* cat Chooses the category
|
||||
* displayoptions Sets display options
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been extensively
|
||||
* rewritten by Gustav Delius and other members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once("editlib.php");
|
||||
|
||||
require_login();
|
||||
|
||||
@@ -20,16 +53,24 @@
|
||||
$SESSION->modform = $modform; // Save the form in the current session
|
||||
|
||||
} else if ($quizid) {
|
||||
if (! $modform = get_record('quiz', 'id', $quizid)) {
|
||||
error("The required quiz doesn't exist");
|
||||
}
|
||||
$modform->instance = $modform->id;
|
||||
$SESSION->modform = $modform; // Save the form in the current session
|
||||
if (isset($SESSION->modform) and $SESSION->modform->id == $quizid) {
|
||||
// modform for this quiz already exists, use it
|
||||
$modform = $SESSION->modform;
|
||||
} else {
|
||||
// create new modform from database
|
||||
if (! $modform = get_record('quiz', 'id', $quizid)) {
|
||||
error("The required quiz doesn't exist");
|
||||
}
|
||||
$modform->instance = $modform->id;
|
||||
$SESSION->modform = $modform; // Save the form in the current session
|
||||
|
||||
$cm = get_coursemodule_from_instance('quiz', $modform->instance);
|
||||
add_to_log($cm->course, 'quiz', 'editquestions',
|
||||
"view.php?id=$cm->id",
|
||||
"$quizid", $cm->id);
|
||||
$cm = get_coursemodule_from_instance('quiz', $modform->instance);
|
||||
$modform->cmid = $cm->id;
|
||||
// We don't log all visits to this page but only those that recreate modform
|
||||
add_to_log($cm->course, 'quiz', 'editquestions',
|
||||
"view.php?id=$cm->id",
|
||||
"$quizid", $cm->id);
|
||||
}
|
||||
|
||||
} else if ($courseid) { // Page retrieve through "Edit Questions" link - no quiz selected
|
||||
$modform->course = $courseid;
|
||||
@@ -39,18 +80,13 @@
|
||||
add_to_log($courseid, 'quiz', 'editquestions', "index.php?id=$courseid");
|
||||
|
||||
} else {
|
||||
// no quiz or course was specified so we need to use the stored modform
|
||||
if (!isset($SESSION->modform)) {
|
||||
// We currently also get here after editing a question by
|
||||
// following the edit link on the review page. Perhaps that should be fixed.
|
||||
error('');
|
||||
error(get_string('wronguse', 'quiz'), '../../course/view.php?id='.$course->id);
|
||||
}
|
||||
|
||||
// The data is obtained from a $SESSION variable. This is mostly for historic reasons.
|
||||
// With the way things work now it would be just as possible to get the data from the database.
|
||||
$modform = $SESSION->modform;
|
||||
}
|
||||
|
||||
|
||||
if (! $course = get_record("course", "id", $modform->course)) {
|
||||
error("This course doesn't exist");
|
||||
}
|
||||
@@ -64,113 +100,122 @@
|
||||
if (isset($modform->instance)
|
||||
&& empty($modform->grades)) // Construct an array to hold all the grades.
|
||||
{
|
||||
$modform->grades = quiz_get_all_question_grades($modform->questions, $modform->instance);
|
||||
$modform->grades = quiz_get_all_question_grades($modform);
|
||||
}
|
||||
|
||||
|
||||
/// Now, check for commands on this page and modify variables as necessary
|
||||
|
||||
if (isset($_REQUEST['up']) and confirm_sesskey()) { /// Move the given question up a slot
|
||||
$questions = explode(",", $modform->questions);
|
||||
if ($questions[0] <> $up) {
|
||||
foreach ($questions as $key => $question) {
|
||||
if ($up == $question) {
|
||||
$swap = $questions[$key-1];
|
||||
$questions[$key-1] = $question;
|
||||
$questions[$key] = $swap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($up > 0 and isset($questions[$up])) {
|
||||
$prevkey = ($questions[$up-1] == 0) ? $up-2 : $up-1;
|
||||
$swap = $questions[$prevkey];
|
||||
$questions[$prevkey] = $questions[$up];
|
||||
$questions[$up] = $swap;
|
||||
$modform->questions = implode(",", $questions);
|
||||
}
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
// Always have a page break at the end
|
||||
$modform->questions = $modform->questions . ',0';
|
||||
// Avoid duplicate page breaks
|
||||
$modform->questions = str_replace(',0,0', ',0', $modform->questions);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['down']) and confirm_sesskey()) { /// Move the given question down a slot
|
||||
$questions = explode(",", $modform->questions);
|
||||
if ($questions[count($questions)-1] <> $down) {
|
||||
foreach ($questions as $key => $question) {
|
||||
if ($down == $question) {
|
||||
$swap = $questions[$key+1];
|
||||
$questions[$key+1] = $question;
|
||||
$questions[$key] = $swap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($down < count($questions)) {
|
||||
$nextkey = ($questions[$down+1] == 0) ? $down+2 : $down+1;
|
||||
$swap = $questions[$nextkey];
|
||||
$questions[$nextkey] = $questions[$down];
|
||||
$questions[$down] = $swap;
|
||||
$modform->questions = implode(",", $questions);
|
||||
}
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
// Avoid duplicate page breaks
|
||||
$modform->questions = str_replace(',0,0', ',0', $modform->questions);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['addquestion']) and confirm_sesskey()) { /// Add a single question to the current quiz
|
||||
|
||||
// add question to quiz->questions
|
||||
$key = $_REQUEST['addquestion'];
|
||||
$questions = array();
|
||||
if (!empty($modform->questions)) {
|
||||
$questions = explode(",", $modform->questions);
|
||||
}
|
||||
if (!in_array($key, $questions)) {
|
||||
$questions[] = $key;
|
||||
}
|
||||
$modform->questions = implode(",", $questions);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
|
||||
// update question grades
|
||||
$questionrecord = get_record("quiz_questions", "id", $key);
|
||||
if (!empty($questionrecord->defaultgrade)) {
|
||||
$modform->grades[$key] = $questionrecord->defaultgrade;
|
||||
} else if ($questionrecord->qtype == DESCRIPTION){
|
||||
$modform->grades[$key] = 0;
|
||||
} else {
|
||||
$modform->grades[$key] = 1;
|
||||
}
|
||||
quiz_questiongrades_update($modform->grades, $modform->instance);
|
||||
quiz_add_quiz_question($_REQUEST['addquestion'], $modform);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['add']) and confirm_sesskey()) { /// Add selected questions to the current quiz
|
||||
$rawquestions = $_POST;
|
||||
if (!empty($modform->questions)) {
|
||||
$questions = explode(",", $modform->questions);
|
||||
}
|
||||
foreach ($rawquestions as $key => $value) { // Parse input for question ids
|
||||
foreach ($_POST as $key => $value) { // Parse input for question ids
|
||||
if (substr($key, 0, 1) == "q") {
|
||||
$key = substr($key,1);
|
||||
if (!empty($questions)) {
|
||||
foreach ($questions as $question) {
|
||||
if ($question == $key) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$questions[] = $key;
|
||||
|
||||
$questionrecord = get_record("quiz_questions", "id", $key);
|
||||
|
||||
if (!empty($questionrecord->defaultgrade)) {
|
||||
$modform->grades[$key] = $questionrecord->defaultgrade;
|
||||
} else if ($questionrecord->qtype == DESCRIPTION){
|
||||
$modform->grades[$key] = 0;
|
||||
} else {
|
||||
$modform->grades[$key] = 1;
|
||||
}
|
||||
quiz_add_quiz_question(substr($key,1), $modform);
|
||||
}
|
||||
}
|
||||
if (!empty($questions)) {
|
||||
$modform->questions = implode(",", $questions);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['addrandom']) and confirm_sesskey()) { /// Add random questions to the quiz
|
||||
$recurse = optional_param('recurse', 0, PARAM_BOOL);
|
||||
$categoryid = required_param('categoryid', PARAM_INT);
|
||||
$randomcount = required_param('randomcount', PARAM_INT);
|
||||
// load category
|
||||
if (! $category = get_record('quiz_categories', 'id', $categoryid)) {
|
||||
error('Category ID is incorrect');
|
||||
}
|
||||
// find existing random questions in this category
|
||||
$random = RANDOM;
|
||||
if ($existingquestions = get_records_select('quiz_questions', "qtype = '$random' AND category = '$category->id'")) {
|
||||
// now remove the ones that are already used in this quiz
|
||||
if ($questionids = explode(',', $modform->questions)) {
|
||||
foreach ($questionids as $questionid) {
|
||||
unset($existingquestions[$questionid]);
|
||||
}
|
||||
}
|
||||
// now take as many of these as needed
|
||||
$i = 0;
|
||||
while (($existingquestion = array_pop($existingquestions)) and ($i < $randomcount)) {
|
||||
if ($existingquestion->questiontext == $recurse) {
|
||||
// this question has the right recurse property, so use it
|
||||
quiz_add_quiz_question($existingquestion->id, $modform);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
$randomcreate = $randomcount - $i; // the number of additional random questions needed.
|
||||
} else {
|
||||
$modform->questions = "";
|
||||
$randomcreate = $randomcount;
|
||||
}
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
|
||||
if ($randomcreate > 0) {
|
||||
|
||||
$form->name = get_string('random', 'quiz') .' ('. $category->name .')';
|
||||
$form->questiontext = $recurse; // we use the questiontext field to store the info
|
||||
// on whether to include questions in subcategories
|
||||
$form->questiontextformat = 0;
|
||||
$form->image = '';
|
||||
$form->defaultgrade = 1;
|
||||
$form->hidden = 1;
|
||||
for ($i=0; $i<$randomcreate; $i++) {
|
||||
$form->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
|
||||
$question = new stdClass;
|
||||
$question->category = $category->id;
|
||||
$question->qtype = RANDOM;
|
||||
$question = $QUIZ_QTYPES[RANDOM]->save_question($question, $form, $course);
|
||||
if(!isset($question->id)) {
|
||||
error('Could not insert new random question!');
|
||||
}
|
||||
quiz_add_quiz_question($question->id, $modform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['repaginate']) and confirm_sesskey()) { /// Re-paginate the quiz
|
||||
if (isset($_REQUEST['questionsperpage'])) {
|
||||
$modform->questionsperpage = required_param('questionsperpage', 1, PARAM_INT);
|
||||
if (!set_field('quiz', 'questionsperpage', $modform->questionsperpage, 'id', $modform->id)) {
|
||||
error('Could not save number of questions per page');
|
||||
}
|
||||
}
|
||||
$modform->questions = quiz_repaginate($modform->questions, $modform->questionsperpage);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->id)) {
|
||||
error('Could not save layout');
|
||||
}
|
||||
quiz_questiongrades_update($modform->grades, $modform->instance);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['move']) and confirm_sesskey()) { /// Move selected questions to new category
|
||||
@@ -180,8 +225,7 @@
|
||||
if (!isteacheredit($tocategory->course)) {
|
||||
error(get_string('categorynoedit', 'quiz', $tocategory->name), 'edit.php');
|
||||
}
|
||||
$rawquestions = $_POST;
|
||||
foreach ($rawquestions as $key => $value) { // Parse input for question ids
|
||||
foreach ($_POST as $key => $value) { // Parse input for question ids
|
||||
if (substr($key, 0, 1) == "q") {
|
||||
$key = substr($key,1);
|
||||
if (!set_field('quiz_questions', 'category', $tocategory->id, 'id', $key)) {
|
||||
@@ -192,29 +236,7 @@
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['delete']) and confirm_sesskey()) { /// Remove a question from the quiz
|
||||
$questions = explode(",", $modform->questions);
|
||||
foreach ($questions as $key => $question) {
|
||||
if ($question == $delete) {
|
||||
unset($questions[$key]);
|
||||
unset($modform->grades[$question]);
|
||||
// Delete all responses associated with all attempts for this question in the quiz.
|
||||
if ($attempts = get_records('quiz_attempts', 'quiz', $modform->instance)) {
|
||||
foreach ($attempts as $attempt) {
|
||||
if (!delete_records('quiz_responses', 'question', $question, 'attempt', $attempt->id)) {
|
||||
error('Could not delete all responses for this question');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete question from quiz_question_grades table
|
||||
if (!delete_records('quiz_question_grades', 'quiz', $modform->instance, 'question', $question)) {
|
||||
error('Could not delete the question from this quiz');
|
||||
}
|
||||
}
|
||||
}
|
||||
$modform->questions = implode(",", $questions);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
quiz_delete_quiz_question($_REQUEST['delete'], $modform);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['deleteselected'])) { // delete selected questions from the category
|
||||
@@ -224,16 +246,13 @@
|
||||
if ($questionlist = explode(',', $deleteselected)) {
|
||||
// for each question either hide it if it is in use or delete it
|
||||
foreach ($questionlist as $questionid) {
|
||||
if (record_exists('quiz_responses', 'question', $questionid) or
|
||||
record_exists('quiz_responses', 'originalquestion', $questionid) or
|
||||
record_exists('quiz_question_grades', 'question', $questionid)) {
|
||||
if (record_exists('quiz_question_instances', 'question', $questionid) or
|
||||
record_exists('quiz_states', 'originalquestion', $questionid)) {
|
||||
if (!set_field('quiz_questions', 'hidden', 1, 'id', $questionid)) {
|
||||
error('Was not able to hide question');
|
||||
error('Was not able to hide question');
|
||||
}
|
||||
} else {
|
||||
if (!delete_records("quiz_questions", "id", $questionid)) {
|
||||
error("An error occurred trying to delete question (id $questionid)");
|
||||
}
|
||||
delete_records("quiz_questions", "id", $questionid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,9 +272,8 @@
|
||||
if (substr($key, 0, 1) == "q") {
|
||||
$key = substr($key,1);
|
||||
$questionlist .= $key.',';
|
||||
if (record_exists('quiz_responses', 'question', $key) or
|
||||
record_exists('quiz_responses', 'originalquestion', $key) or
|
||||
record_exists('quiz_question_grades', 'question', $key)) {
|
||||
if (record_exists('quiz_question_instances', 'question', $key) or
|
||||
record_exists('quiz_states', 'originalquestion', $key)) {
|
||||
$questionnames .= '* ';
|
||||
$inuse = true;
|
||||
}
|
||||
@@ -281,28 +299,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['setgrades']) and confirm_sesskey()) { /// The grades have been updated, so update our internal list
|
||||
if (isset($_REQUEST['setgrades']) and confirm_sesskey()) {
|
||||
/// The grades have been updated, so update our internal list
|
||||
$rawgrades = $_POST;
|
||||
unset($modform->grades);
|
||||
foreach ($rawgrades as $key => $value) { // Parse input for question -> grades
|
||||
if (substr($key, 0, 1) == "q") {
|
||||
$key = substr($key,1);
|
||||
$modform->grades[$key] = $value;
|
||||
quiz_update_question_instance($modform->grades[$key], $key, $modform->instance);
|
||||
}
|
||||
}
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
|
||||
// If rescaling is required save the new maximum
|
||||
if (isset($_REQUEST['maxgrade'])) {
|
||||
$modform->grade = optional_param('maxgrade', 0);
|
||||
if (!set_field('quiz', 'grade', $modform->grade, 'id', $modform->instance)) {
|
||||
error('Could not set new maximal grade for quiz');
|
||||
}
|
||||
}
|
||||
quiz_questiongrades_update($modform->grades, $modform->instance);
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['cat'])) { /// coming from category selection drop-down menu
|
||||
$modform->category = $cat;
|
||||
}
|
||||
|
||||
if(isset($_REQUEST['displayoptions'])) {
|
||||
$modform->recurse = isset($_REQUEST['recurse']) ? 1 : 0;
|
||||
$modform->showhidden = isset($_REQUEST['showhidden']);
|
||||
if(isset($_REQUEST['recurse'])) {
|
||||
$SESSION->quiz_recurse = optional_param('recurse', 0, PARAM_BOOL);
|
||||
}
|
||||
if(isset($_REQUEST['showbreaks'])) {
|
||||
$SESSION->quiz_showbreaks = optional_param('showbreaks', 0, PARAM_BOOL);
|
||||
}
|
||||
if(isset($_REQUEST['showhidden'])) {
|
||||
$SESSION->quiz_showhidden = optional_param('showhidden', 0, PARAM_BOOL);
|
||||
}
|
||||
|
||||
/// Delete any teacher preview attempts if the quiz has been modified
|
||||
if (isset($_REQUEST['delete']) or isset($_REQUEST['repaginate']) or isset($_REQUEST['addrandom']) or isset($_REQUEST['addquestion']) or isset($_REQUEST['up']) or isset($_REQUEST['down']) or isset($_REQUEST['add'])) {
|
||||
delete_records('quiz_attempts', 'preview', '1', 'quiz', $modform->id);
|
||||
}
|
||||
|
||||
/// all commands have been dealt with, now print the page
|
||||
@@ -311,43 +345,54 @@
|
||||
$category = quiz_get_default_category($course->id);
|
||||
$modform->category = $category->id;
|
||||
}
|
||||
if (!isset($modform->recurse)) {
|
||||
$modform->recurse = 1;
|
||||
if (!isset($SESSION->quiz_recurse)) {
|
||||
$SESSION->quiz_recurse = 1;
|
||||
}
|
||||
if (!isset($modform->showhidden)) {
|
||||
$modform->showhidden = false;
|
||||
if (!isset($SESSION->quiz_showhidden)) {
|
||||
$SESSION->quiz_showhidden = false;
|
||||
}
|
||||
if (!isset($SESSION->quiz_showbreaks)) {
|
||||
$SESSION->quiz_showbreaks = ($CFG->quiz_questionsperpage < 2) ? 0 : 1;
|
||||
}
|
||||
|
||||
$SESSION->modform = $modform;
|
||||
|
||||
// Print basic page layout.
|
||||
|
||||
if (isset($modform->instance) and record_exists_sql("SELECT * FROM {$CFG->prefix}quiz_attempts WHERE quiz = '$modform->instance' AND NOT (userid = '$USER->id') LIMIT 1")){
|
||||
// one column layout with table of questions used in this quiz
|
||||
if (isset($modform->instance) and record_exists_sql("SELECT * FROM {$CFG->prefix}quiz_attempts WHERE quiz = '$modform->instance' AND preview = '0' LIMIT 1")){
|
||||
// one column layout with table of questions used in this quiz
|
||||
$strupdatemodule = isteacheredit($course->id)
|
||||
? update_module_button($modform->cmid, $course->id, get_string('modulename', 'quiz'))
|
||||
: "";
|
||||
print_header_simple($streditingquiz, '',
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>".
|
||||
" -> <a href=\"view.php?q=$modform->instance\">".format_string($modform->name,true)."</a>".
|
||||
" -> $streditingquiz");
|
||||
" -> <a href=\"view.php?q=$modform->instance\">".format_string($modform->name).'</a>'.
|
||||
" -> $streditingquiz", "", "",
|
||||
true, $strupdatemodule);
|
||||
|
||||
$currenttab = 'edit';
|
||||
$quiz = &$modform;
|
||||
include('tabs.php');
|
||||
|
||||
print_simple_box_start("center");
|
||||
print_heading(format_string($modform->name));
|
||||
$attemptcount = count_records_select("quiz_attempts", "quiz = '$modform->instance' AND timefinish > 0");
|
||||
|
||||
$attemptcount = count_records('quiz_attempts', 'quiz', $modform->instance, 'preview', 0);
|
||||
|
||||
$strviewallanswers = get_string("viewallanswers","quiz",$attemptcount);
|
||||
$strattemptsexist = get_string("attemptsexist","quiz");
|
||||
$usercount = count_records("quiz_grades", "quiz", "$modform->instance");
|
||||
$strusers = get_string("users");
|
||||
$usercount = count_records_select('quiz_attempts', "quiz = '$modform->id' AND preview = '0'", 'COUNT(DISTINCT userid)');
|
||||
$strusers = $course->students;
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $modform->instance, $course->id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
notify("$strattemptsexist<br /><a href=\"report.php?id=$cm->id\">$strviewallanswers ($usercount $strusers)</a>");
|
||||
notify("$strattemptsexist<br /><a href=\"attempts.php?id=$cm->id\">$strviewallanswers ($usercount $strusers)</a>");
|
||||
|
||||
$sumgrades = quiz_print_question_list($modform->questions, $modform->grades, false, $modform->instance);
|
||||
$sumgrades = quiz_print_question_list($modform, false, $SESSION->quiz_showbreaks);
|
||||
if (!set_field('quiz', 'sumgrades', $sumgrades, 'id', $modform->instance)) {
|
||||
error('Failed to set sumgrades');
|
||||
}
|
||||
|
||||
print_simple_box_end();
|
||||
print_continue('view.php?q='.$modform->instance);
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
@@ -362,39 +407,46 @@
|
||||
|
||||
} else {
|
||||
// two column layout with quiz info in left column
|
||||
$strupdatemodule = isteacheredit($course->id)
|
||||
? update_module_button($modform->cmid, $course->id, get_string('modulename', 'quiz'))
|
||||
: "";
|
||||
print_header_simple($streditingquiz, '',
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>".
|
||||
" -> <a href=\"view.php?q=$modform->instance\">".format_string($modform->name,true)."</a>".
|
||||
" -> $streditingquiz");
|
||||
" -> <a href=\"view.php?q=$modform->instance\">".format_string($modform->name).'</a>'.
|
||||
" -> $streditingquiz",
|
||||
"", "", true, $strupdatemodule);
|
||||
|
||||
$currenttab = 'edit';
|
||||
$quiz = &$modform;
|
||||
include('tabs.php');
|
||||
|
||||
echo '<table border="0" width="100%" cellpadding="2" cellspacing="0">';
|
||||
echo '<tr><td width="50%" valign="top">';
|
||||
print_simple_box_start("center", "100%");
|
||||
print_heading(format_string($modform->name));
|
||||
$sumgrades = quiz_print_question_list($modform->questions, $modform->grades, true, $modform->instance);
|
||||
|
||||
$sumgrades = quiz_print_question_list($modform, true, $SESSION->quiz_showbreaks);
|
||||
if (!set_field('quiz', 'sumgrades', $sumgrades, 'id', $modform->instance)) {
|
||||
error('Failed to set sumgrades');
|
||||
}
|
||||
|
||||
print_simple_box_end();
|
||||
print_continue('view.php?q='.$modform->instance);
|
||||
|
||||
echo '</td><td valign="top" width="50%">';
|
||||
}
|
||||
// non-quiz-specific column
|
||||
print_simple_box_start("center", "100%");
|
||||
// starts with category selection form
|
||||
quiz_print_category_form($course, $modform->category, $modform->recurse, $modform->showhidden);
|
||||
quiz_print_category_form($course, $modform->category, $SESSION->quiz_recurse, $SESSION->quiz_showhidden);
|
||||
print_simple_box_end();
|
||||
|
||||
print_spacer(5,1);
|
||||
// continues with list of questions
|
||||
print_simple_box_start("center", "100%");
|
||||
quiz_print_cat_question_list($course, $modform->category,
|
||||
isset($modform->instance), $modform->recurse, $page, $perpage, $modform->showhidden);
|
||||
isset($modform->instance), $SESSION->quiz_recurse, $page, $perpage, $SESSION->quiz_showhidden);
|
||||
print_simple_box_end();
|
||||
if (!isset($modform->instance)) {
|
||||
print_continue("index.php?id=$modform->course");
|
||||
} else {
|
||||
print_continue('view.php?q='.$modform->instance);
|
||||
}
|
||||
echo '</td></tr>';
|
||||
echo '</table>';
|
||||
|
||||
@@ -0,0 +1,581 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* Functions used by edit.php to edit quizzes
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been extensively
|
||||
* rewritten by members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("locallib.php");
|
||||
|
||||
/**
|
||||
* Delete a question from a quiz
|
||||
*
|
||||
* Deletes a question or a pagebreak from a quiz by updating $modform
|
||||
* as well as the quiz, quiz_question_instances, quiz_attemtps, quiz_states
|
||||
* and quiz_newest_states tables.
|
||||
* @return boolean false if the question was not in the quiz
|
||||
* @param int $id The id of the question to be deleted
|
||||
* @param object $modform The extended quiz object as used by edit.php
|
||||
* This is updated by this function
|
||||
*/
|
||||
function quiz_delete_quiz_question($id, &$modform) {
|
||||
$questions = explode(",", $modform->questions);
|
||||
|
||||
// only do something if this question exists
|
||||
if (!isset($questions[$id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$question = $questions[$id];
|
||||
unset($questions[$id]);
|
||||
// If we deleted the question at the top and it was followed by
|
||||
// a page break then delete page break as well
|
||||
if ($id == 0 and $questions[1] == 0) {
|
||||
unset($questions[1]);
|
||||
}
|
||||
// if what we deleted was not a page break but a question then also
|
||||
// delete associated grades, instances, attempts, states
|
||||
if ($question != 0) {
|
||||
unset($modform->grades[$question]);
|
||||
// Delete all states associated with all attempts for this question in the quiz.
|
||||
if ($attempts = get_records('quiz_attempts', 'quiz', $modform->instance)) {
|
||||
foreach ($attempts as $attempt) {
|
||||
delete_records('quiz_states', 'question', $question, 'attempt', $attempt->id);
|
||||
delete_records('quiz_newest_states', 'questionid', $question, 'attemptid', $attempt->id);
|
||||
}
|
||||
}
|
||||
// Delete all instances of the question in the quiz (there
|
||||
// should only be one)
|
||||
delete_records('quiz_question_instances', 'quiz',
|
||||
$modform->instance, 'question', $question);
|
||||
}
|
||||
$modform->questions = implode(",", $questions);
|
||||
// Avoid duplicate page breaks
|
||||
$modform->questions = str_replace(',0,0', ',0', $modform->questions);
|
||||
// save new questionlist in database
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a question to a quiz
|
||||
*
|
||||
* Adds a question to a quiz by updating $modform as well as the
|
||||
* quiz and quiz_question_instances tables. It also adds a page break
|
||||
* if required.
|
||||
* @return boolean false if the question was already in the quiz
|
||||
* @param int $id The id of the question to be added
|
||||
* @param object $modform The extended quiz object as used by edit.php
|
||||
* This is updated by this function
|
||||
*/
|
||||
function quiz_add_quiz_question($id, &$modform) {
|
||||
$questions = explode(",", $modform->questions);
|
||||
|
||||
if (in_array($id, $questions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove ending page break if it is not needed
|
||||
if ($breaks = array_keys($questions, 0)) {
|
||||
// determine location of the last two page breaks
|
||||
$end = end($breaks);
|
||||
$last = prev($breaks);
|
||||
$last = $last ? $last : -1;
|
||||
if (!$modform->questionsperpage or (($end - $last -1) < $modform->questionsperpage)) {
|
||||
array_pop($questions);
|
||||
}
|
||||
}
|
||||
// add question
|
||||
$questions[] = $id;
|
||||
// add ending page break
|
||||
$questions[] = 0;
|
||||
|
||||
// Save new questionslist in database
|
||||
$modform->questions = implode(",", $questions);
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->id)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
|
||||
// update question grades
|
||||
$questionrecord = get_record("quiz_questions", "id", $id);
|
||||
if (!empty($questionrecord->defaultgrade)) {
|
||||
$modform->grades[$id] = $questionrecord->defaultgrade;
|
||||
} else if ($questionrecord->qtype == DESCRIPTION){
|
||||
$modform->grades[$id] = 0;
|
||||
} else {
|
||||
$modform->grades[$id] = 1;
|
||||
}
|
||||
quiz_update_question_instance($modform->grades[$id], $id, $modform->instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes to question instance
|
||||
*
|
||||
* Saves changes to the question grades in the quiz_question_instances table.
|
||||
* It does not update 'sumgrades' in the quiz table.
|
||||
* @return boolean Indicates success or failure.
|
||||
* @param integer grade The maximal grade for the question
|
||||
* @param integer $questionid The id of the question
|
||||
* @param integer $quizid The id of the quiz to update / add the instances for.
|
||||
*/
|
||||
function quiz_update_question_instance($grade, $questionid, $quizid) {
|
||||
if ($instance = get_record("quiz_question_instances", "quiz", $quizid, 'question', $questionid)) {
|
||||
$instance->grade = $grade;
|
||||
return update_record('quiz_question_instances', $instance);
|
||||
} else {
|
||||
unset($instance);
|
||||
$instance->quiz = $quizid;
|
||||
$instance->question = $questionid;
|
||||
$instance->grade = $grade;
|
||||
return insert_record("quiz_question_instances", $instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of quiz questions in a small layout form with knobs
|
||||
*
|
||||
* @return int sum of maximum grades
|
||||
* @param object $quiz This is not the standard quiz object used elsewhere but
|
||||
* it contains the quiz layout in $quiz->questions and the grades in
|
||||
* $quiz->grades
|
||||
* @param boolean $allowdelete Indicates whether the delete icons should be displayed
|
||||
* @param boolean $showbreaks Indicates whether the page breaks should be displayed
|
||||
*/
|
||||
function quiz_print_question_list($quiz, $allowdelete=true, $showbreaks=true) {
|
||||
global $USER, $CFG, $QUIZ_QTYPES;
|
||||
|
||||
$strorder = get_string("order");
|
||||
$strquestionname = get_string("questionname", "quiz");
|
||||
$strgrade = get_string("grade");
|
||||
$strremove = get_string('remove', 'quiz');
|
||||
$stredit = get_string("edit");
|
||||
$straction = get_string("action");
|
||||
$strmoveup = get_string("moveup");
|
||||
$strmovedown = get_string("movedown");
|
||||
$strsavegrades = get_string("savegrades", "quiz");
|
||||
$strtype = get_string("type", "quiz");
|
||||
$strpreview = get_string("preview", "quiz");
|
||||
|
||||
if (!$quiz->questions) {
|
||||
echo "<p align=\"center\">";
|
||||
print_string("noquestions", "quiz");
|
||||
echo "</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
$order = explode(",", $quiz->questions);
|
||||
|
||||
if (!$questions = get_records_sql("SELECT q.*,c.course
|
||||
FROM {$CFG->prefix}quiz_questions q,
|
||||
{$CFG->prefix}quiz_categories c
|
||||
WHERE q.id in ($quiz->questions)
|
||||
AND q.category = c.id")) {
|
||||
echo "<p align=\"center\">";
|
||||
print_string("noquestions", "quiz");
|
||||
echo "</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$qno = 1;
|
||||
$sumgrade = 0;
|
||||
$total = count($order);
|
||||
echo "<form method=\"post\" action=\"edit.php\">";
|
||||
echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
|
||||
|
||||
print_simple_box_start('center', '100%', '#ffffff', 0);
|
||||
echo "<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"100%\">\n";
|
||||
echo "<tr><th colspan=\"3\" nowrap=\"nowrap\" class=\"header\">$strorder</th><th align=\"left\" width=\"100%\" nowrap=\"nowrap\" class=\"header\">$strquestionname</th><th nowrap=\"nowrap\" class=\"header\">$strtype</th><th nowrap=\"nowrap\" class=\"header\">$strgrade</th><th align=\"center\" width=\"60\" nowrap=\"nowrap\" class=\"header\">$straction</th></tr>\n";
|
||||
foreach ($order as $qnum) {
|
||||
|
||||
if ($qnum == 0) { // This is a page break
|
||||
if ($showbreaks) {
|
||||
echo '<tr><td colspan ="3"> </td>';
|
||||
echo '<td><table width="100%" style="line-height:11px; font-size:9px; margin: -5px -5px;"><tr>';
|
||||
echo '<td><hr noshade="noshade" /></td>';
|
||||
echo '<td width="50">Page break</td>';
|
||||
echo '<td><hr noshade="noshade" /></td><td width="45">';
|
||||
if ($count != 1) {
|
||||
echo "<a title=\"$strmoveup\" href=\"edit.php?up=$count&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/up.gif\" border=\"0\" alt=\"$strmoveup\" /></a>";
|
||||
}
|
||||
echo ' ';
|
||||
if ($count < $total-1) {
|
||||
echo "<a title=\"$strmovedown\" href=\"edit.php?down=$count&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/down.gif\" border=\"0\" alt=\"$strmovedown\" /></a>";
|
||||
|
||||
echo "<a title=\"$strremove\" href=\"edit.php?delete=$count&sesskey=$USER->sesskey\">
|
||||
<img src=\"../../pix/t/delete.gif\" border=\"0\" alt=\"$strremove\" /></a>";
|
||||
}
|
||||
echo '</td></tr></table></td>';
|
||||
echo '<td colspan="2"> </td></tr>';
|
||||
}
|
||||
$count++;
|
||||
continue;
|
||||
}
|
||||
if (empty($questions[$qnum])) {
|
||||
continue;
|
||||
}
|
||||
$question = $questions[$qnum];
|
||||
$canedit = isteacheredit($question->course);
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>";
|
||||
if ($count != 0) {
|
||||
echo "<a title=\"$strmoveup\" href=\"edit.php?up=$count&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/up.gif\" border=\"0\" alt=\"$strmoveup\" /></a>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "<td>";
|
||||
if ($count != $total-2) {
|
||||
echo "<a title=\"$strmovedown\" href=\"edit.php?down=$count&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/down.gif\" border=\"0\" alt=\"$strmovedown\" /></a>";
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
if (!$quiz->shufflequestions) {
|
||||
// Print and increment question number
|
||||
echo '<td>'.($question->length ? $qno : ' ').'</td>';
|
||||
$qno += $question->length;
|
||||
} else {
|
||||
echo '<td> </td>';
|
||||
}
|
||||
|
||||
echo "<td>$question->name</td>";
|
||||
echo "<td align=\"center\">";
|
||||
quiz_print_question_icon($question, $canedit);
|
||||
echo "</td>";
|
||||
echo '<td align="left">';
|
||||
if ($question->qtype == DESCRIPTION) {
|
||||
echo "<input type=\"hidden\" name=\"q$qnum\" value=\"0\" /> \n";
|
||||
} else {
|
||||
echo '<input type="text" name="q'.$qnum.'" size="2" value="'.$quiz->grades[$qnum].
|
||||
'" tabindex="'.$qno.'" />';
|
||||
}
|
||||
echo '</td><td align="center">';
|
||||
|
||||
if ($canedit) {
|
||||
$context = $quiz->id ? '&contextquiz='.$quiz->id : '';
|
||||
$quiz_id = $quiz->id ? '&quizid=' . $quiz->id : '';
|
||||
echo "<a title=\"$strpreview\" href=\"javascript:void();\" onClick=\"openpopup('/mod/quiz/preview.php?id=$qnum$quiz_id','$strpreview','scrollbars=yes,resizable=yes,width=700,height=480', false)\">
|
||||
<img src=\"../../pix/t/preview.gif\" border=\"0\" alt=\"$strpreview\" /></a>";
|
||||
echo "<a title=\"$stredit\" href=\"question.php?id=$qnum$context\">
|
||||
<img src=\"../../pix/t/edit.gif\" border=\"0\" alt=\"$stredit\" /></a>";
|
||||
if ($allowdelete) {
|
||||
echo "<a title=\"$strremove\" href=\"edit.php?delete=$count&sesskey=$USER->sesskey\">
|
||||
<img src=\"../../pix/t/removeright.gif\" border=\"0\" alt=\"$strremove\" /></a>";
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
$count++;
|
||||
$sumgrade += $quiz->grades[$qnum];
|
||||
}
|
||||
|
||||
echo "<tr><td colspan=\"5\" align=\"right\">\n";
|
||||
print_string('total');
|
||||
echo ": <td align=\"left\">\n";
|
||||
echo "<b>$sumgrade</b>";
|
||||
echo "</td><td> \n</td></tr>\n";
|
||||
|
||||
echo "<tr><td colspan=\"5\" align=\"right\">\n";
|
||||
print_string('maximumgrade');
|
||||
echo ": <td align=\"left\">\n";
|
||||
echo '<input type="text" name="maxgrade" size="2" tabindex="'.($qno+1)
|
||||
.'" value="'.$quiz->grade.'" />';
|
||||
echo '</td><td align="left">';
|
||||
helpbutton("maxgrade", get_string("maximumgrade"), "quiz");
|
||||
echo "</td></tr>\n";
|
||||
|
||||
echo "<tr><td colspan=\"6\" align=\"right\">\n";
|
||||
|
||||
echo "<input type=\"submit\" value=\"$strsavegrades\" />\n";
|
||||
echo "<input type=\"hidden\" name=\"setgrades\" value=\"save\" />\n";
|
||||
echo "</td><td> </td><td>\n</td></tr>\n";
|
||||
|
||||
echo "</table>\n";
|
||||
print_simple_box_end();
|
||||
echo "</form>\n";
|
||||
|
||||
/// Form to choose to show pagebreaks and to repaginate quiz
|
||||
echo '<form method="post" action="edit.php" name="showbreaks">';
|
||||
echo '<input type="hidden" name="showbreaks" value="0" />';
|
||||
echo '<input type="checkbox" name="showbreaks" value="1"';
|
||||
if ($showbreaks) {
|
||||
echo ' checked="checked"';
|
||||
}
|
||||
echo ' onchange="document.showbreaks.submit(); return true;" />';
|
||||
print_string('showbreaks', 'quiz');
|
||||
echo ' <input type="submit" value="'. get_string('go') .'" />';
|
||||
|
||||
if ($showbreaks) {
|
||||
$perpage= array();
|
||||
for ($i=0; $i<=50; ++$i) {
|
||||
$perpage[$i] = $i;
|
||||
}
|
||||
$perpage[0] = get_string('allinone', 'quiz');
|
||||
echo '<br />';
|
||||
echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
|
||||
print_string('repaginate', 'quiz',
|
||||
choose_from_menu($perpage, 'questionsperpage', $quiz->questionsperpage, '', '', '', true));
|
||||
echo ' <input type="submit" name="repaginate" value="'. get_string('go') .'" />';
|
||||
}
|
||||
|
||||
echo '</form>';
|
||||
|
||||
return $sumgrade;
|
||||
}
|
||||
|
||||
function quiz_print_category_form($course, $current, $recurse=1, $showhidden=false) {
|
||||
/// Prints a form to choose categories
|
||||
|
||||
/// Make sure the default category exists for this course
|
||||
if (!$categories = get_records("quiz_categories", "course", $course->id, "id ASC")) {
|
||||
if (!$category = quiz_get_default_category($course->id)) {
|
||||
notify("Error creating a default category!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Get all the existing categories now
|
||||
if (!$categories = get_records_select("quiz_categories", "course = '{$course->id}' OR publish = '1'", "parent, sortorder, name ASC")) {
|
||||
notify("Could not find any question categories!");
|
||||
return false; // Something is really wrong
|
||||
}
|
||||
$categories = add_indented_names($categories);
|
||||
foreach ($categories as $key => $category) {
|
||||
if ($catcourse = get_record("course", "id", $category->course)) {
|
||||
if ($category->publish && $category->course != $course->id) {
|
||||
$category->indentedname .= " ($catcourse->shortname)";
|
||||
}
|
||||
$catmenu[$category->id] = $category->indentedname;
|
||||
}
|
||||
}
|
||||
$strcategory = get_string("category", "quiz");
|
||||
$strshow = get_string("show", "quiz");
|
||||
$streditcats = get_string("editcategories", "quiz");
|
||||
|
||||
echo "<table width=\"100%\"><tr><td width=\"20\" nowrap=\"nowrap\">";
|
||||
echo "<b>$strcategory:</b> ";
|
||||
echo "</td><td>";
|
||||
popup_form ("edit.php?cat=", $catmenu, "catmenu", $current, "", "", "", false, "self");
|
||||
echo "</td><td align=\"right\">";
|
||||
echo "<form method=\"get\" action=\"category.php\">";
|
||||
echo "<input type=\"hidden\" name=\"id\" value=\"$course->id\" />";
|
||||
echo "<input type=\"submit\" value=\"$streditcats\" />";
|
||||
echo "</form>";
|
||||
echo '</td></tr></table>';
|
||||
echo '<form method="post" action="edit.php" name="displayoptions">';
|
||||
echo '<table><tr><td>';
|
||||
echo '<input type="hidden" name="recurse" value="0" />';
|
||||
echo '<input type="checkbox" name="recurse" value="1"';
|
||||
if ($recurse) {
|
||||
echo ' checked="checked"';
|
||||
}
|
||||
echo ' onchange="document.displayoptions.submit(); return true;" />';
|
||||
print_string('recurse', 'quiz');
|
||||
// hide-feature
|
||||
echo '<br />';
|
||||
echo '<input type="hidden" name="showhidden" value="0" />';
|
||||
echo '<input type="checkbox" name="showhidden"';
|
||||
if ($showhidden) {
|
||||
echo ' checked="checked"';
|
||||
}
|
||||
echo ' onchange="document.displayoptions.submit(); return true;" />';
|
||||
print_string('showhidden', 'quiz');
|
||||
echo '</td><td valign="center">';
|
||||
echo ' <input type="submit" value="'. get_string('go') .'" />';
|
||||
echo '</td></tr></table></form>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints the table of questions in a category with interactions
|
||||
*
|
||||
* @param object $course The course object
|
||||
* @param int $categoryid The id of the question category to be displayed
|
||||
* @param boolean $quizselected True if we are in the context of a particular quiz
|
||||
* @param int $recurse This is 1 if subcategories should be included, 0 otherwise
|
||||
* @param int $page The number of the page to be displayed
|
||||
* @param int $perpage Number of questions to show per page
|
||||
* @param boolean $showhidden True if also hidden questions should be displayed
|
||||
*/
|
||||
function quiz_print_cat_question_list($course, $categoryid, $quizselected=true,
|
||||
$recurse=1, $page, $perpage, $showhidden=false) {
|
||||
global $QUIZ_QUESTION_TYPE, $USER;
|
||||
|
||||
$strcategory = get_string("category", "quiz");
|
||||
$strquestion = get_string("question", "quiz");
|
||||
$straddquestions = get_string("addquestions", "quiz");
|
||||
$strimportquestions = get_string("importquestions", "quiz");
|
||||
$strexportquestions = get_string("exportquestions", "quiz");
|
||||
$strnoquestions = get_string("noquestions", "quiz");
|
||||
$strselect = get_string("select", "quiz");
|
||||
$strselectall = get_string("selectall", "quiz");
|
||||
$strselectnone = get_string("selectnone", "quiz");
|
||||
$strcreatenewquestion = get_string("createnewquestion", "quiz");
|
||||
$strquestionname = get_string("questionname", "quiz");
|
||||
$strdelete = get_string("delete");
|
||||
$stredit = get_string("edit");
|
||||
$straction = get_string("action");
|
||||
$strrestore = get_string('restore');
|
||||
|
||||
$straddtoquiz = get_string("addtoquiz", "quiz");
|
||||
$strtype = get_string("type", "quiz");
|
||||
$strcreatemultiple = get_string("createmultiple", "quiz");
|
||||
$strpreview = get_string("preview","quiz");
|
||||
|
||||
if (!$categoryid) {
|
||||
echo "<p align=\"center\"><b>";
|
||||
print_string("selectcategoryabove", "quiz");
|
||||
echo "</b></p>";
|
||||
if ($quizselected) {
|
||||
echo "<p>";
|
||||
print_string("addingquestions", "quiz");
|
||||
echo "</p>";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$category = get_record("quiz_categories", "id", "$categoryid")) {
|
||||
notify("Category not found!");
|
||||
return;
|
||||
}
|
||||
echo "<center>";
|
||||
echo format_text($category->info, FORMAT_MOODLE);
|
||||
|
||||
echo '<table><tr>';
|
||||
|
||||
// check if editing of this category is allowed
|
||||
if (isteacheredit($category->course)) {
|
||||
echo "<td valign=\"top\"><b>$strcreatenewquestion:</b></td>";
|
||||
echo '<td valign="top" align="right">';
|
||||
popup_form ("question.php?category=$category->id&qtype=", $QUIZ_QUESTION_TYPE, "addquestion",
|
||||
"", "choose", "", "", false, "self");
|
||||
echo '</td><td width="10" valign="top" align="right">';
|
||||
helpbutton("questiontypes", $strcreatenewquestion, "quiz");
|
||||
echo '</td></tr>';
|
||||
}
|
||||
else {
|
||||
echo '<tr><td>';
|
||||
print_string("publishedit","quiz");
|
||||
echo '</td></tr>';
|
||||
}
|
||||
|
||||
echo '<tr><td colspan="3" align="right"><font size="2">';
|
||||
if (isteacheredit($category->course)) {
|
||||
echo '<a href="import.php?category='.$category->id.'">'.$strimportquestions.'</a>';
|
||||
helpbutton("import", $strimportquestions, "quiz");
|
||||
echo ' | ';
|
||||
}
|
||||
echo '<a href="export.php?category='.$category->id.'">'.$strexportquestions.'</a>';
|
||||
helpbutton("export", $strexportquestions, "quiz");
|
||||
echo '</font></td></tr>';
|
||||
|
||||
echo '</table>';
|
||||
|
||||
echo '</center>';
|
||||
|
||||
$categorylist = ($recurse) ? quiz_categorylist($category->id) : $category->id;
|
||||
|
||||
// hide-feature
|
||||
$showhidden = $showhidden ? '' : " AND hidden = '0'";
|
||||
if (!$questions = get_records_select('quiz_questions', "category IN ($categorylist) AND parent = '0' $showhidden", 'qtype, name ASC', '*', $page*$perpage, $perpage)) {
|
||||
echo "<p align=\"center\">";
|
||||
print_string("noquestions", "quiz");
|
||||
echo "</p>";
|
||||
return;
|
||||
}
|
||||
|
||||
$canedit = isteacheredit($category->course);
|
||||
|
||||
echo '<form method="post" action="edit.php">';
|
||||
echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
|
||||
print_simple_box_start('center', '100%', '#ffffff', 0);
|
||||
echo '<table id="categoryquestions" cellspacing="0"><tr>';
|
||||
if ($canedit) {
|
||||
echo "<th width=\"95\" nowrap=\"nowrap\" class=\"header\">$straction</th>";
|
||||
}
|
||||
echo "<th width=\"100%\" align=\"left\" nowrap=\"nowrap\" class=\"header\">$strquestionname</th>
|
||||
<th nowrap=\"nowrap\" class=\"header\">$strtype</th>";
|
||||
echo "</tr>\n";
|
||||
foreach ($questions as $question) {
|
||||
if ($question->qtype == RANDOM) {
|
||||
//continue;
|
||||
}
|
||||
echo "<tr>\n";
|
||||
if ($canedit) {
|
||||
echo "<td>\n";
|
||||
if ($quizselected) {
|
||||
echo "<a title=\"$straddtoquiz\" href=\"edit.php?addquestion=$question->id&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/moveleft.gif\" border=\"0\" alt=\"$straddtoquiz\" /></a> ";
|
||||
}
|
||||
echo "<a title=\"$strpreview\" href=\"javascript:void();\" onClick=\"openpopup('/mod/quiz/preview.php?id=$question->id','$strpreview','scrollbars=yes,resizable=yes,width=700,height=480', false)\"><img
|
||||
src=\"../../pix/t/preview.gif\" border=\"0\" alt=\"$strpreview\" /></a> ";
|
||||
echo "<a title=\"$stredit\" href=\"question.php?id=$question->id\"><img
|
||||
src=\"../../pix/t/edit.gif\" border=\"0\" alt=\"$stredit\" /></a> ";
|
||||
// hide-feature
|
||||
if($question->hidden) {
|
||||
echo "<a title=\"$strrestore\" href=\"question.php?id=$question->id&hide=0&sesskey=$USER->sesskey\"><img
|
||||
src=\"../../pix/t/restore.gif\" border=\"0\" alt=\"$strrestore\" /></a>";
|
||||
} else {
|
||||
echo "<a title=\"$strdelete\" href=\"question.php?id=$question->id&delete=$question->id\"><img
|
||||
src=\"../../pix/t/delete.gif\" border=\"0\" alt=\"$strdelete\" /></a>";
|
||||
}
|
||||
echo " <input title=\"$strselect\" type=\"checkbox\" name=\"q$question->id\" value=\"1\" />";
|
||||
echo "</td>\n";
|
||||
}
|
||||
if ($question->hidden) {
|
||||
echo '<td class="dimmed_text">'.$question->name."</td>\n";
|
||||
} else {
|
||||
echo "<td>".$question->name."</td>\n";
|
||||
}
|
||||
echo "<td align=\"center\">\n";
|
||||
quiz_print_question_icon($question, $canedit);
|
||||
echo "</td>\n";
|
||||
echo "</tr>\n";
|
||||
}
|
||||
echo "</table>\n";
|
||||
print_simple_box_end();
|
||||
|
||||
echo '<table class="quiz-edit-selected"><tr><td colspan="2">';
|
||||
echo '<a href="javascript:select_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectall.'</a> /'.
|
||||
' <a href="javascript:deselect_all_in(\'TABLE\', null, \'categoryquestions\');">'.$strselectnone.'</a>'.
|
||||
'</td><td align="right"><b>'.get_string('withselected', 'quiz').':</b></td></tr><tr><td>';
|
||||
if ($quizselected) {
|
||||
echo "<input type=\"submit\" name=\"add\" value=\"<< $straddtoquiz\" />\n";
|
||||
echo '</td><td>';
|
||||
}
|
||||
echo '<input type="submit" name="deleteselected" value="'.$strdelete."\" /></td><td>\n";
|
||||
echo '<input type="submit" name="move" value="'.get_string('moveto', 'quiz')."\" />\n";
|
||||
quiz_category_select_menu($course->id, false, true, $category->id);
|
||||
echo "</td></tr></table>";
|
||||
|
||||
$numquestions = count($questions);
|
||||
|
||||
print_paging_bar($numquestions, $page, $perpage,
|
||||
"edit.php?perpage=$perpage&");
|
||||
|
||||
if ($quizselected) {
|
||||
for ($i=1;$i<=10; $i++) {
|
||||
$randomcount[$i] = $i;
|
||||
}
|
||||
echo '<br />';
|
||||
print_string('addrandom', 'quiz',
|
||||
choose_from_menu($randomcount, 'randomcount', '1', '', '', '', true));
|
||||
echo '<input type="hidden" name="recurse" value="'.$recurse.'" />';
|
||||
echo "<input type=\"hidden\" name=\"categoryid\" value=\"$category->id\" />";
|
||||
echo ' <input type="submit" name="addrandom" value="'. get_string('add') .'" />';
|
||||
helpbutton('random', get_string('random', 'quiz'), 'quiz');
|
||||
}
|
||||
|
||||
echo "</form>\n";
|
||||
}
|
||||
|
||||
?>
|
||||
+19
-13
@@ -1,6 +1,12 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This page lists all the instances of quiz in a particular course
|
||||
/**
|
||||
* This page lists all the instances of quiz in a particular course
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others.
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
@@ -49,32 +55,32 @@
|
||||
$strbestgrade = get_string("bestgrade", "quiz");
|
||||
$strquizcloses = get_string("quizcloses", "quiz");
|
||||
$strattempts = get_string("attempts", "quiz");
|
||||
$strusers = get_string("users");
|
||||
$strusers = $course->students;
|
||||
|
||||
if (isteacher($course->id)) {
|
||||
$gradecol = $strattempts;
|
||||
} else {
|
||||
$gradecol = $strbestgrade;
|
||||
}
|
||||
|
||||
|
||||
if ($course->format == "weeks") {
|
||||
$table->head = array ($strweek, $strname, $strquizcloses, $gradecol);
|
||||
$table->align = array ("center", "left", "left", "left");
|
||||
$table->size = array (10, "*", "*", "*");
|
||||
$table->size = array (10, "", "", "");
|
||||
} else if ($course->format == "topics") {
|
||||
$table->head = array ($strtopic, $strname, $strquizcloses, $gradecol);
|
||||
$table->align = array ("center", "left", "left", "left");
|
||||
$table->size = array (10, "*", "*", "*");
|
||||
$table->size = array (10, "", "", "");
|
||||
} else {
|
||||
$table->head = array ($strname, $strquizcloses, $gradecol);
|
||||
$table->align = array ("left", "left", "left");
|
||||
$table->size = array ("*", "*", "*");
|
||||
$table->size = array ("", "", "");
|
||||
}
|
||||
|
||||
$currentsection = "";
|
||||
|
||||
foreach ($quizzes as $quiz) {
|
||||
if (!$quiz->visible) {
|
||||
if (!$quiz->visible) {
|
||||
//Show dimmed if the mod is hidden
|
||||
$link = "<a class=\"dimmed\" href=\"view.php?id=$quiz->coursemodule\">".format_string($quiz->name,true)."</a>";
|
||||
} else {
|
||||
@@ -98,15 +104,15 @@
|
||||
$closequiz = userdate($quiz->timeclose);
|
||||
|
||||
if (isteacher($course->id)) {
|
||||
if ($usercount = count_records('quiz_grades', 'quiz', $quiz->id)) {
|
||||
$attemptcount = count_records_select('quiz_attempts', 'quiz = '.$quiz->id.' AND timefinish > 0');
|
||||
$strviewallanswers = get_string('viewallanswers', 'quiz', $attemptcount);
|
||||
$gradecol = "<a href=\"report.php?q=$quiz->id\">$strviewallanswers ($usercount $strusers)</a>";
|
||||
if ($usercount = count_records_select('quiz_attempts', "quiz = '$quiz->id' AND preview = '0'", 'COUNT(DISTINCT userid)')) {
|
||||
$attemptcount = count_records('quiz_attempts', 'quiz', $quiz->id, 'preview', 0);
|
||||
$strviewallreports = get_string('viewallreports', 'quiz', $attemptcount);
|
||||
$gradecol = "<a href=\"attempts.php?q=$quiz->id\">$strviewallreports ($usercount $strusers)</a>";
|
||||
} else {
|
||||
$answercount = 0;
|
||||
$gradecol = "";
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
if ($bestgrade === NULL || $quiz->grade == 0) {
|
||||
$gradecol = "";
|
||||
} else {
|
||||
|
||||
+67
-67
@@ -1,67 +1,67 @@
|
||||
<?php // $Id$
|
||||
defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
|
||||
?>
|
||||
|
||||
<script language="javascript">
|
||||
<!--
|
||||
/// This Javascript clock provides a little countdown in the title bar
|
||||
|
||||
var timerID = null;
|
||||
var timerRunning = false;
|
||||
var secondsleft = <?php echo $secondsleft ?>;
|
||||
var titleafter = '<?php echo format_string($quiz->name) ?>';
|
||||
var titlebefore = '<?php echo get_string("countdown", "quiz").": " ?>';
|
||||
var alertmessage = '<?php print_string("countdownfinished", "quiz") ?>';
|
||||
var alertmessage10 = '<?php print_string("countdowntenminutes", "quiz") ?>';
|
||||
|
||||
function stopclock() {
|
||||
if (timerRunning) {
|
||||
clearTimeout(timerID);
|
||||
timerRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
function startclock() {
|
||||
stopclock();
|
||||
showtime();
|
||||
}
|
||||
|
||||
function showtime() {
|
||||
|
||||
secondsleft = secondsleft - 1;
|
||||
|
||||
if (secondsleft == 600) {
|
||||
alert(alertmessage10);
|
||||
}
|
||||
|
||||
if (secondsleft == 0) {
|
||||
stopclock();
|
||||
document.title = titleafter;
|
||||
return alert(alertmessage);
|
||||
|
||||
} else {
|
||||
current = secondsleft;
|
||||
|
||||
var hours = Math.floor( current / 3600 );
|
||||
current = current - (hours*3600);
|
||||
|
||||
var minutes = Math.floor( current / 60 );
|
||||
current = current - (minutes*60);
|
||||
|
||||
var seconds = current;
|
||||
|
||||
var timeValue = "" + hours;
|
||||
timeValue += ((minutes < 10) ? ":0" : ":") + minutes;
|
||||
timeValue += ((seconds < 10) ? ":0" : ":") + seconds;
|
||||
|
||||
document.title = titlebefore+timeValue+' '+titleafter;
|
||||
timerID = setTimeout("showtime()",1000);
|
||||
timerRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
document.onLoad = startclock();
|
||||
|
||||
|
||||
// -- End of JavaScript code -------------- -->
|
||||
</script>
|
||||
<?php // $Id$
|
||||
defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
|
||||
?>
|
||||
|
||||
<script language="javascript">
|
||||
<!--
|
||||
/// This Javascript clock provides a little countdown in the title bar
|
||||
|
||||
var timerID = null;
|
||||
var timerRunning = false;
|
||||
var secondsleft = <?php echo $secondsleft ?>;
|
||||
var titleafter = '<?php echo format_string($quiz->name) ?>';
|
||||
var titlebefore = '<?php echo get_string("countdown", "quiz").": " ?>';
|
||||
var alertmessage = '<?php print_string("countdownfinished", "quiz") ?>';
|
||||
var alertmessage10 = '<?php print_string("countdowntenminutes", "quiz") ?>';
|
||||
|
||||
function stopclock() {
|
||||
if (timerRunning) {
|
||||
clearTimeout(timerID);
|
||||
timerRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
function startclock() {
|
||||
stopclock();
|
||||
showtime();
|
||||
}
|
||||
|
||||
function showtime() {
|
||||
|
||||
secondsleft = secondsleft - 1;
|
||||
|
||||
if (secondsleft == 600) {
|
||||
alert(alertmessage10);
|
||||
}
|
||||
|
||||
if (secondsleft == 0) {
|
||||
stopclock();
|
||||
document.title = titleafter;
|
||||
return alert(alertmessage);
|
||||
|
||||
} else {
|
||||
current = secondsleft;
|
||||
|
||||
var hours = Math.floor( current / 3600 );
|
||||
current = current - (hours*3600);
|
||||
|
||||
var minutes = Math.floor( current / 60 );
|
||||
current = current - (minutes*60);
|
||||
|
||||
var seconds = current;
|
||||
|
||||
var timeValue = "" + hours;
|
||||
timeValue += ((minutes < 10) ? ":0" : ":") + minutes;
|
||||
timeValue += ((seconds < 10) ? ":0" : ":") + seconds;
|
||||
|
||||
document.title = titlebefore+timeValue+' '+titleafter;
|
||||
timerID = setTimeout("showtime()",1000);
|
||||
timerRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
document.onLoad = startclock();
|
||||
|
||||
|
||||
// -- End of JavaScript code -------------- -->
|
||||
</script>
|
||||
|
||||
@@ -8,11 +8,6 @@ defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
<!--
|
||||
function send_data() {
|
||||
document.forms[0].submit();
|
||||
return true;
|
||||
}
|
||||
|
||||
var timesup = "<?php print_string("timesup","quiz");?>";
|
||||
var quizclose = <?php echo ($quiz->timeclose - time()) - $timerstartvalue; ?>; // in seconds
|
||||
var quizTimerValue = <?php echo $timerstartvalue; ?>; // in seconds
|
||||
@@ -58,4 +53,3 @@ movecounter(this);
|
||||
document.onload = countdown_clock();
|
||||
// -->
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<p align="center"><b>Allow adaptive questions</b></p>
|
||||
|
||||
<p>If you choose Yes for this option then an additional <?php print_string('mark', 'quiz') ?> button is shown for each question.
|
||||
If the student presses this button then the answer to that particular question is submitted to be scored and the mark achieved
|
||||
is displayed to the student. If the question is an adaptive question then it is displayed in its new state that takes the
|
||||
student's answer into account and will in many cases ask the student for another input. In the simplest adaptive questions
|
||||
this new state may differ only in the feedback text and prompt the student to try again; in more
|
||||
complicated question also the question text and even the interaction elements can change.
|
||||
</p>
|
||||
|
||||
<p>For most adaptive questions the student will be able to interact with a single question repeatedly until he gets the right answer.
|
||||
However a penalty will usually be subtracted from the students score for each wrong attempt. This is under the teacher's control.
|
||||
The question may also close itself to new attempts. For example true/false questions will not allow
|
||||
the student to answer again after the first attempt because the answer would be obvious.</p>
|
||||
@@ -0,0 +1,3 @@
|
||||
<p align="center"><b>Penalty</b></p>
|
||||
|
||||
<p>You can specify what fraction of the achieved score should be subtracted for each wrong attempt. This is only relevant if the quiz is run in adaptive mode so that the student is allowed to have several attempts at the question. The penalty should be a number between 0 and 1. A penalty of 1 means that the student has to get the answer right on the first attempt to get any credit for it at all.</p>
|
||||
@@ -0,0 +1,6 @@
|
||||
<p align="center"><b>Penalty scheme</b></p>
|
||||
|
||||
<p>If a question allows a student to have repeated attempts then the question may impose a penalty for wrong attempts.
|
||||
This penalty is a number between 0 and 1 and denotes the fraction of the final score that should be deducted as a penalty.
|
||||
If the student makes several wrong attempts then the penalties for the individual attempts are combined before
|
||||
being used to calculate the final grade.</p>
|
||||
@@ -0,0 +1,4 @@
|
||||
<p align="center"><b>Questions per page</b></p>
|
||||
|
||||
<p>For longer quizzes it makes sense to stretch the quiz over several pages by limiting the number of questions per page. When adding questions to the quiz page breaks will automatically be inserted according to the setting you choose here. However you will also be able to move page breaks around by hand later on the editing page.
|
||||
</p>
|
||||
@@ -0,0 +1,5 @@
|
||||
<p align="center"><b>Remote Questions (RQP)</b></p>
|
||||
|
||||
|
||||
<p>The Remote Question Protocol may be used to include questions in a quiz with all processing occurring on a remote server. Questions may be included in any format for which a server is available without any change to Moodle.</p>
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php // $Id$
|
||||
// quiz.php - created with Moodle 1.4 development (2004080300)
|
||||
|
||||
$string['action'] = 'Action';
|
||||
$string['addingitemtype'] = 'Adding an Item Type';
|
||||
$string['additemtype'] = 'Add item type';
|
||||
$string['addrandom'] = 'Add $a random questions';
|
||||
$string['answers'] = 'Answers';
|
||||
$string['attemptnotcompleted'] = "Attempt has not been completed.";
|
||||
$string['attemptsallowed'] = 'Multiple attempts at quiz';
|
||||
$string['attemptsby'] = 'Attempts by $a';
|
||||
$string['capabilities'] = 'Capabilities';
|
||||
$string['chooseafile'] = 'Choose or upload a file';
|
||||
$string['cloning'] = 'Cloning';
|
||||
$string['cloningserver'] = 'Cloning Server';
|
||||
$string['cloningserverconnectfailed'] = 'The cloning server $a failed to process an RQP request. Check that the URL is correct.';
|
||||
$string['cloningserverdoesnt'] = 'The cloning server $a does not support cloning!';
|
||||
$string['cloningrenderingserverdoesnt'] = 'The cloning (and rendering) server $a does not support cloning (or implicit cloning).';
|
||||
$string['close'] = 'Close window';
|
||||
$string['closepreview'] = 'Close preview';
|
||||
$string['closereview'] = 'Close review';
|
||||
$string['completedon'] = 'Completed on';
|
||||
$string['confirmattemptclose'] = 'You still have an unfinished attempt. Do you want to close this and start a new attempt now?';
|
||||
$string['confirmclose'] = 'You are about to close this attempt. Once you close the attempt you will no longer be able to change your answers.';
|
||||
$string['continueattemptfail'] = 'Error: Could not load the last attempt';
|
||||
$string['continueattemptquiz'] = 'Continue the last attempt';
|
||||
$string['deleteitemtypecheck'] = 'Are you absolutely sure you want to delete \'$a\'?';
|
||||
$string['duplicateresponse'] = 'This submission has been ignored because you gave an equivalent answer earlier.';
|
||||
$string['editingitemtype'] = 'Editing an Item Type';
|
||||
$string['editingrqp'] = 'Editing a Remote Question (RQP)';
|
||||
$string['editquiz'] = 'Edit Quiz';
|
||||
$string['event1'] = 'Autosave';
|
||||
$string['event2'] = 'Save';
|
||||
$string['event3'] = 'Grade';
|
||||
$string['event5'] = 'Validate';
|
||||
$string['event6'] = 'Close';
|
||||
$string['fillcorrect'] = 'Fill with correct';
|
||||
$string['gradeavailable'] = 'Maximum possible marks: $a';
|
||||
$string['gradingdetails'] = 'Marks for this submission: $a->raw/$a->max.';
|
||||
$string['gradingdetailsnopenalty'] = 'Marks for this submission: $a->raw/$a->max. Counting all previous submissions this gives <strong>$a->cur/$a->max</strong>.';
|
||||
$string['gradingdetailszeropenalty'] = 'Marks for this submission: $a->raw/$a->max. You were not penalized for this submission. Counting all previous submissions this gives <strong>$a->cur/$a->max</strong>.';
|
||||
$string['gradingdetailspenalty'] = 'Marks for this submission: $a->raw/$a->max with a penalty of $a->penalty. Counting all previous submissions this gives <strong>$a->cur/$a->max</strong>.';
|
||||
$string['info'] = 'Info';
|
||||
$string['invalidsource'] = 'The source is not accepted as valid by the server $a->rendering_server.';
|
||||
$string['invalidsourcetype'] = 'Invalid source type.';
|
||||
$string['itemsource'] = 'Item Source';
|
||||
$string['itemsourceformat'] = 'Item Source Format';
|
||||
$string['itemrequirescloning'] = 'The item source supplied is for an item template but the server for the selected item type does not support cloning (or implicit cloning). Check that the correct item type was selected';
|
||||
$string['itemtype'] = 'Item Type';
|
||||
$string['itemtypes'] = 'Item Types';
|
||||
$string['lastanswer'] = 'Your last answer was';
|
||||
$string['managetypes'] = 'Manage question types and servers';
|
||||
$string['adaptive'] = 'Allow adaptive questions';
|
||||
$string['finishattempt'] = 'Submit all and finish';
|
||||
$string['markall'] = 'Submit page';
|
||||
$string['mark'] = 'Submit';
|
||||
$string['missingitemtypename'] = 'Missing name';
|
||||
$string['missingsource'] = 'Missing item source';
|
||||
$string['missingrenderingserver'] = 'Missing rendering server URL';
|
||||
$string['newattemptfail'] = 'Error: Could not start a new attempt at the quiz';
|
||||
$string['noattemptstoshow'] = 'There are no attempts to show';
|
||||
$string['noquestionsfound'] = 'No questions found';
|
||||
$string['notavailable'] = 'Sorry, this quiz is not available';
|
||||
$string['notavailabletostudents'] = 'Note: This quiz is not currently available to your students';
|
||||
$string['notimedependentitems'] = 'Time dependent items are not currently supported by the quiz module. As a work around, set a time limit for the whole quiz. Do you wish to choose a different item (or use the current item regardless)?';
|
||||
$string['notypes'] = 'No item types have been defined! The administrator must add some types and set the servers to be used to process them before RQP questions can be used';
|
||||
$string['notypeselected'] = 'No type selected';
|
||||
$string['numberabbr'] = '#';
|
||||
$string['outof'] = ' out of a maximum of ';
|
||||
$string['penalty'] = 'Penalty';
|
||||
$string['penaltynone'] = 'None';
|
||||
$string['penaltymultiply'] = 'Multiplicative';
|
||||
$string['penaltysubtract'] = 'Additive';
|
||||
$string['penaltyscheme'] = 'Penalty scheme';
|
||||
$string['previewquiz'] = 'Preview quiz';
|
||||
$string['previous'] = 'Previous state';
|
||||
$string['readytosend'] = 'You are about to finalize this quiz attempt. Are you sure you want to continue?';
|
||||
$string['rendering'] = 'Rendering';
|
||||
$string['renderingserver'] = 'Rendering Server';
|
||||
$string['renderingserverconnectfailed'] = 'The rendering server $a failed to process an RQP request. Check that the URL is correct.';
|
||||
$string['renderingserverdoesnt'] = 'The rendering server $a does not support rendering!';
|
||||
$string['repaginate'] = 'Repaginate with $a questions per page';
|
||||
$string['replacementoptions'] = 'Replacement Options';
|
||||
$string['reports'] = 'Reports';
|
||||
$string['response'] = 'Response';
|
||||
$string['responses'] = 'Responses';
|
||||
$string['reviewimmediately'] = 'Immediately after the attempt';
|
||||
$string['reviewofattempt'] = 'Review of Attempt $a';
|
||||
$string['reviewopen'] = 'Later, while the quiz is still open';
|
||||
$string['reviewclosed'] = 'After the quiz is closed';
|
||||
$string['reviewoptions'] = 'Students may review';
|
||||
$string['reviewresponse'] = 'Review response';
|
||||
$string['rqp'] = 'Remote Question';
|
||||
$string['rqps'] = 'Remote Questions';
|
||||
$string['savenosubmit'] = 'Save without submitting';
|
||||
$string['scores'] = 'Scores';
|
||||
$string['servers'] = 'Servers';
|
||||
$string['showall'] = 'Show all questions on one page';
|
||||
$string['showbreaks'] = 'Show page breaks';
|
||||
$string['startagain'] = 'Start again';
|
||||
$string['startedon'] = 'Started on';
|
||||
$string['subnetnotice'] = 'This quiz has been locked so that it is only accessible from certain locations. Your computer is not on an allowed subnet. As teacher you are allowed to preview anyway.';
|
||||
$string['timestr'] = '%%H:%%M:%%S on %%d/%%m/%%y';
|
||||
$string['unfinished'] = 'open';
|
||||
$string['validate'] = 'Validate';
|
||||
$string['withselected'] = 'With selected';
|
||||
$string['wronguse'] = 'You can not use this page like that';
|
||||
|
||||
?>
|
||||
+176
-4
@@ -1,13 +1,70 @@
|
||||
<?php // $Id$
|
||||
|
||||
/// Library of function for module quiz
|
||||
/**
|
||||
* Library of functions for the quiz module.
|
||||
*
|
||||
* This contains functions that are called also from outside the quiz module
|
||||
* Functions that are only called by the quiz module itself are in {@link locallib.php}
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others.
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once($CFG->libdir.'/pagelib.php');
|
||||
|
||||
/// CONSTANTS ///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**#@+
|
||||
* Option flags for $quiz->optionflags
|
||||
*/
|
||||
/**
|
||||
* Whether the quiz is to be run in adaptive mode
|
||||
*/
|
||||
define('QUIZ_ADAPTIVE', 1);
|
||||
|
||||
/**
|
||||
* Determines if when checking whether an answer should
|
||||
* be checked against all previous ones to see if it is duplicate
|
||||
*/
|
||||
define('QUIZ_IGNORE_DUPRESP', 2);
|
||||
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* If start and end date for the quiz are more than this many seconds apart
|
||||
* they will be represented by two separate events in the calendar
|
||||
*/
|
||||
define("QUIZ_MAX_EVENT_LENGTH", "432000"); // 5 days maximum
|
||||
|
||||
/**#@+
|
||||
* The different review options are stored in the bits of $quiz->review
|
||||
* These constants help to extract the options
|
||||
*/
|
||||
/**
|
||||
* The first 6 bits refer to the time immediately after the attempt
|
||||
*/
|
||||
define('QUIZ_REVIEW_IMMEDIATELY', 64-1);
|
||||
/**
|
||||
* the next 6 bits refer to the time after the attempt but while the quiz is open
|
||||
*/
|
||||
define('QUIZ_REVIEW_OPEN', 4096-64);
|
||||
/**
|
||||
* the final 6 bits refer to the time after the quiz closes
|
||||
*/
|
||||
define('QUIZ_REVIEW_CLOSED', 262144-4096);
|
||||
|
||||
// within each group of 6 bits we determine what should be shown
|
||||
define('QUIZ_REVIEW_RESPONSES', 1+64+4096); // Show responses
|
||||
define('QUIZ_REVIEW_SCORES', 2*4161); // Show scores
|
||||
define('QUIZ_REVIEW_FEEDBACK', 4*4161); // Show feedback
|
||||
define('QUIZ_REVIEW_ANSWERS', 8*4161); // Show correct answers
|
||||
// Some handling of worked solutions is already in the code but not yet fully supported
|
||||
// and not switched on in the user interface.
|
||||
define('QUIZ_REVIEW_SOLUTIONS', 16*4161); // Show solutions
|
||||
// the 6th bit is as yet unused
|
||||
/**#@-*/
|
||||
|
||||
/// FUNCTIONS ///////////////////////////////////////////////////////////////////
|
||||
|
||||
function quiz_add_instance($quiz) {
|
||||
@@ -16,6 +73,8 @@ function quiz_add_instance($quiz) {
|
||||
/// will create a new instance and return the id number
|
||||
/// of the new instance.
|
||||
|
||||
quiz_process_options($quiz);
|
||||
|
||||
$quiz->created = time();
|
||||
$quiz->timemodified = time();
|
||||
$quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
|
||||
@@ -80,6 +139,8 @@ function quiz_update_instance($quiz) {
|
||||
/// (defined by the form in mod.html or edit.php) this function
|
||||
/// will update an existing instance with new data.
|
||||
|
||||
quiz_process_options($quiz);
|
||||
|
||||
$quiz->timemodified = time();
|
||||
if (isset($quiz->openyear)) { // this would not be set if we come from edit.php
|
||||
$quiz->timeopen = make_timestamp($quiz->openyear, $quiz->openmonth, $quiz->openday,
|
||||
@@ -93,6 +154,9 @@ function quiz_update_instance($quiz) {
|
||||
return false; // some error occurred
|
||||
}
|
||||
|
||||
// Delete any preview attempts
|
||||
delete_records('quiz_attempts', 'preview', '1', 'quiz', $quiz->id);
|
||||
|
||||
if (isset($quiz->optionsettingspref)) {
|
||||
set_user_preference('quiz_optionsettingspref', $quiz->optionsettingspref);
|
||||
}
|
||||
@@ -150,7 +214,7 @@ function quiz_delete_instance($id) {
|
||||
|
||||
if ($attempts = get_records("quiz_attempts", "quiz", "$quiz->id")) {
|
||||
foreach ($attempts as $attempt) {
|
||||
if (! delete_records("quiz_responses", "attempt", "$attempt->id")) {
|
||||
if (! delete_records("quiz_states", "attempt", "$attempt->id")) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +228,7 @@ function quiz_delete_instance($id) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (! delete_records("quiz_question_grades", "quiz", "$quiz->id")) {
|
||||
if (! delete_records("quiz_question_instances", "quiz", "$quiz->id")) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
@@ -235,13 +299,32 @@ function quiz_user_outline($course, $user, $mod, $quiz) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
function quiz_user_complete($course, $user, $mod, $quiz) {
|
||||
/// Print a detailed representation of what a user has done with
|
||||
/// a given particular instance of this module, for user activity reports.
|
||||
|
||||
if ($attempts = get_records_select('quiz_attempts', "userid='$user->id' AND quiz='$quiz->id'", 'attempt ASC')) {
|
||||
if ($quiz->grade != 0 && $grade = get_record('quiz_grades', 'userid', $user->id, 'quiz', $quiz->id)) {
|
||||
echo get_string('grade').': '.format_float($grade->grade, $quiz->decimalpoints).'/'.$quiz->grade.'<br />';
|
||||
}
|
||||
foreach ($attempts as $attempt) {
|
||||
echo get_string('attempt', 'quiz').' '.$attempt->attempt.': ';
|
||||
if ($attempt->timefinish == 0) {
|
||||
print_string('unfinished');
|
||||
} else {
|
||||
echo format_float($attempt->sumgrades, $quiz->decimalpoints).'/'.$quiz->sumgrades;
|
||||
}
|
||||
echo ' - '.userdate($attempt->timemodified).'<br />';
|
||||
}
|
||||
} else {
|
||||
print_string('noattempts', 'quiz');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function quiz_cron () {
|
||||
/// Function to be run periodically according to the moodle cron
|
||||
/// This function searches for things that need to be done, such
|
||||
@@ -481,4 +564,93 @@ function quiz_print_recent_mod_activity($activity, $course, $detail=false) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-process the options form data
|
||||
*
|
||||
* Encode the review options from the setup form into the bits of $form->review
|
||||
* and other options into $form->optionflags
|
||||
* The form data is passed by reference and modified by this function
|
||||
* @return integer
|
||||
* @param object $form The variables set on the form.
|
||||
*/
|
||||
function quiz_process_options(&$form) {
|
||||
$optionflags = 0;
|
||||
$review = 0;
|
||||
|
||||
if (!empty($form->adaptive)) {
|
||||
$optionflags += QUIZ_ADAPTIVE;
|
||||
}
|
||||
|
||||
if (isset($form->responsesimmediately)) {
|
||||
$review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_IMMEDIATELY);
|
||||
unset($form->responsesimmediately);
|
||||
}
|
||||
if (isset($form->responsesopen)) {
|
||||
$review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_OPEN);
|
||||
unset($form->responsesopen);
|
||||
}
|
||||
if (isset($form->responsesclosed)) {
|
||||
$review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_CLOSED);
|
||||
unset($form->responsesclosed);
|
||||
}
|
||||
|
||||
if (isset($form->scoreimmediately)) {
|
||||
$review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_IMMEDIATELY);
|
||||
unset($form->scoreimmediately);
|
||||
}
|
||||
if (isset($form->scoreopen)) {
|
||||
$review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN);
|
||||
unset($form->scoreopen);
|
||||
}
|
||||
if (isset($form->scoreclosed)) {
|
||||
$review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED);
|
||||
unset($form->scoreclosed);
|
||||
}
|
||||
|
||||
if (isset($form->feedbackimmediately)) {
|
||||
$review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
|
||||
unset($form->feedbackimmediately);
|
||||
}
|
||||
if (isset($form->feedbackopen)) {
|
||||
$review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN);
|
||||
unset($form->feedbackopen);
|
||||
}
|
||||
if (isset($form->feedbackclosed)) {
|
||||
$review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED);
|
||||
unset($form->feedbackclosed);
|
||||
}
|
||||
|
||||
if (isset($form->answersimmediately)) {
|
||||
$review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY);
|
||||
unset($form->answersimmediately);
|
||||
}
|
||||
if (isset($form->answersopen)) {
|
||||
$review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_OPEN);
|
||||
unset($form->answersopen);
|
||||
}
|
||||
if (isset($form->answersclosed)) {
|
||||
$review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_CLOSED);
|
||||
unset($form->answersclosed);
|
||||
}
|
||||
|
||||
if (isset($form->solutionsimmediately)) {
|
||||
$review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_IMMEDIATELY);
|
||||
unset($form->solutionsimmediately);
|
||||
}
|
||||
if (isset($form->solutionsopen)) {
|
||||
$review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_OPEN);
|
||||
unset($form->solutionsopen);
|
||||
}
|
||||
if (isset($form->solutionsclosed)) {
|
||||
$review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_CLOSED);
|
||||
unset($form->solutionsclosed);
|
||||
}
|
||||
|
||||
$form->review = $review;
|
||||
$form->optionflags = $optionflags;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
+1822
-1220
File diff suppressed because it is too large
Load Diff
+108
-146
@@ -1,9 +1,38 @@
|
||||
<!-- This page defines the form to create or edit an instance of this module -->
|
||||
<!-- It is used from /course/mod.php. The whole instance is available as $form. -->
|
||||
|
||||
<!-- Some javascript to allow for the hiding of advanced options
|
||||
This code was derived from mod.html in the resource module -->
|
||||
<script language="javascript" type="text/javascript">
|
||||
function showhide (id, set) {
|
||||
divobj = document.getElementById(id);
|
||||
butobj = document.getElementById(id+'button');
|
||||
prefobj = document.getElementById(id+'pref');
|
||||
if (set == true) {
|
||||
if (prefobj.value == '1') {
|
||||
divobj.style.display = 'block';
|
||||
butobj.value = '<?php print_string("hideadvancedsettings") ?>';
|
||||
} else {
|
||||
divobj.style.display = 'none';
|
||||
butobj.value = '<?php print_string("showadvancedsettings") ?>...';
|
||||
}
|
||||
} else {
|
||||
if (prefobj.value == '1') {
|
||||
divobj.style.display = 'none';
|
||||
butobj.value = '<?php print_string("showadvancedsettings") ?>...';
|
||||
prefobj.value = '0';
|
||||
} else {
|
||||
divobj.style.display = 'block';
|
||||
butobj.value = '<?php print_string("hideadvancedsettings") ?>';
|
||||
prefobj.value = '1';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
require_once("$CFG->dirroot/mod/quiz/locallib.php");
|
||||
// Ensure that all form variables are initialized
|
||||
/// Ensure that all form variables are initialized
|
||||
if (!isset($form->name)) {
|
||||
$form->name = "";
|
||||
}
|
||||
@@ -28,17 +57,11 @@
|
||||
if (!isset($form->decimalpoints)) {
|
||||
$form->decimalpoints = $CFG->quiz_decimalpoints;
|
||||
}
|
||||
if (!isset($form->feedback)) {
|
||||
$form->feedback = $CFG->quiz_showfeedback;
|
||||
}
|
||||
if (!isset($form->correctanswers)) {
|
||||
$form->correctanswers = $CFG->quiz_showanswer;
|
||||
}
|
||||
if (!isset($form->review)) {
|
||||
$form->review = $CFG->quiz_allowreview;
|
||||
$form->review = $CFG->quiz_review;
|
||||
}
|
||||
if (!isset($form->questionsperpage)) {
|
||||
$form->questionsperpage = 0;
|
||||
$form->questionsperpage = $CFG->quiz_questionsperpage;
|
||||
}
|
||||
if (!isset($form->shufflequestions)) {
|
||||
$form->shufflequestions = $CFG->quiz_shufflequestions;
|
||||
@@ -64,15 +87,16 @@
|
||||
if (!isset($form->popup)) {
|
||||
$form->popup = $CFG->quiz_popup;
|
||||
}
|
||||
if (!isset($form->optionflags)) {
|
||||
$form->optionflags = $CFG->quiz_optionflags;
|
||||
}
|
||||
if (!isset($form->penaltyscheme)) {
|
||||
$form->penaltyscheme = $CFG->quiz_penaltyscheme;
|
||||
}
|
||||
|
||||
$fix = 0; // This will later be set to 1 if some of the variables have been fixed by the admin.
|
||||
|
||||
// the following are used for drop-down menus
|
||||
$reviewoptions = array();
|
||||
$reviewoptions[0] = get_string('reviewnever', 'quiz');
|
||||
$reviewoptions[1] = get_string('reviewafter', 'quiz');
|
||||
$reviewoptions[2] = get_string('reviewbefore', 'quiz');
|
||||
$reviewoptions[3] = get_string('reviewalways', 'quiz');
|
||||
|
||||
$yesnooptions = array();
|
||||
$yesnooptions[0] = get_string("no");
|
||||
@@ -85,11 +109,11 @@
|
||||
$attemptoptions[$i] = "$i ".strtolower(get_string("attempts", "quiz"));
|
||||
}
|
||||
|
||||
$grades = array();
|
||||
for ($i=100; $i>=1; $i--) {
|
||||
$grades[$i] = $i;
|
||||
}
|
||||
$grades[0] = get_string("nograde");
|
||||
$penaltyoptions = array();
|
||||
$penaltyoptions[QUIZ_PENALTYNONE] = get_string('penaltynone', 'quiz');
|
||||
$penaltyoptions[QUIZ_PENALTYMULTIPLY] = get_string('penaltymultiply', 'quiz');
|
||||
$penaltyoptions[QUIZ_PENALTYSUBTRACT] = get_string('penaltysubtract', 'quiz');
|
||||
|
||||
?>
|
||||
|
||||
|
||||
@@ -220,7 +244,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<!--
|
||||
<?php if (!$CFG->quiz_fix_attemptonlast) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("eachattemptbuildsonthelast", "quiz") ?>:</b></td>
|
||||
@@ -235,7 +259,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
-->
|
||||
<?php if (!$CFG->quiz_fix_grademethod) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("grademethod", "quiz") ?>:</b></td>
|
||||
@@ -248,6 +272,30 @@
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_adaptive) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("adaptive", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "adaptive", $form->optionflags & QUIZ_ADAPTIVE, "");
|
||||
helpbutton("adaptive", get_string("adaptive","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_penaltyscheme) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("penaltyscheme", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($penaltyoptions, "penaltyscheme", "$form->penaltyscheme", "");
|
||||
helpbutton("penaltyscheme", get_string("penaltyscheme","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_decimalpoints) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("decimalpoints", "quiz") ?>:</b></td>
|
||||
@@ -265,53 +313,11 @@
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_showfeedback) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("showfeedback", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "feedback", "$form->feedback", "");
|
||||
helpbutton("feedback", get_string("showfeedback","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_showanswer) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("showcorrectanswer", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "correctanswers", "$form->correctanswers", "");
|
||||
helpbutton("correctanswers", get_string("showcorrectanswer","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_allowreview) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("allowreview", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($reviewoptions, "review", "$form->review", "");
|
||||
helpbutton("review2", get_string("allowreview","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_maximumgrade) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("maximumgrade") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($grades, "grade", "$form->grade", "");
|
||||
helpbutton("maxgrade", get_string("maximumgrade"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } else $fix=1 ?>
|
||||
<?php if (!$CFG->quiz_fix_review) {
|
||||
echo '<tr valign="top">';
|
||||
include('reviewoptions.html');
|
||||
echo '</tr>';
|
||||
} else $fix=1 ?>
|
||||
|
||||
<?php if (!$CFG->quiz_fix_popup) { ?>
|
||||
<tr valign="top">
|
||||
@@ -352,35 +358,6 @@
|
||||
|
||||
<?php if ($fix) { ?>
|
||||
|
||||
<!-- Some javascript to allow for the hiding of advanced options
|
||||
This code was derived from mod.html in the resource module -->
|
||||
<script language="javascript" type="text/javascript">
|
||||
function showhide (id, set) {
|
||||
divobj = document.getElementById(id);
|
||||
butobj = document.getElementById(id+'button');
|
||||
prefobj = document.getElementById(id+'pref');
|
||||
if (set == true) {
|
||||
if (prefobj.value == '1') {
|
||||
divobj.style.display = 'block';
|
||||
butobj.value = '<?php print_string("hideadvancedsettings") ?>';
|
||||
} else {
|
||||
divobj.style.display = 'none';
|
||||
butobj.value = '<?php print_string("showadvancedsettings") ?>...';
|
||||
}
|
||||
} else {
|
||||
if (prefobj.value == '1') {
|
||||
divobj.style.display = 'none';
|
||||
butobj.value = '<?php print_string("showadvancedsettings") ?>...';
|
||||
prefobj.value = '0';
|
||||
} else {
|
||||
divobj.style.display = 'block';
|
||||
butobj.value = '<?php print_string("hideadvancedsettings") ?>';
|
||||
prefobj.value = '1';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Now comes a copy of the above but enclosed in a div with id optionsettings and with
|
||||
the conditions reversed, so that it shows exactly the options that are usually hidden.
|
||||
The visibility of this div is controlled by javascript. -->
|
||||
@@ -466,7 +443,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<!--
|
||||
<?php if ($CFG->quiz_fix_attemptonlast) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("eachattemptbuildsonthelast", "quiz") ?>:</b></td>
|
||||
@@ -481,7 +458,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
-->
|
||||
<?php if ($CFG->quiz_fix_grademethod) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("grademethod", "quiz") ?>:</b></td>
|
||||
@@ -494,6 +471,30 @@
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_adaptive) { $fix = 1; ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("adaptive", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "adaptive", $form->optionflags & QUIZ_ADAPTIVE, "");
|
||||
helpbutton("adaptive", get_string("adaptive","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_penaltyscheme) { $fix = 1; ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("penaltyscheme", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($penaltyoptions, "penaltyscheme", "$form->penaltyscheme", "");
|
||||
helpbutton("penaltyscheme", get_string("penaltyscheme","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_decimalpoints) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("decimalpoints", "quiz") ?>:</b></td>
|
||||
@@ -511,53 +512,11 @@
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_showfeedback) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("showfeedback", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "feedback", "$form->feedback", "");
|
||||
helpbutton("feedback", get_string("showfeedback","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_showanswer) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("showcorrectanswer", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($yesnooptions, "correctanswers", "$form->correctanswers", "");
|
||||
helpbutton("correctanswers", get_string("showcorrectanswer","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_allowreview) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("allowreview", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($reviewoptions, "review", "$form->review", "");
|
||||
helpbutton("review", get_string("allowreview","quiz"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_maximumgrade) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("maximumgrade") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
choose_from_menu($grades, "grade", "$form->grade", "");
|
||||
helpbutton("maxgrade", get_string("maximumgrade"), "quiz");
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($CFG->quiz_fix_review) {
|
||||
echo '<tr valign="top">';
|
||||
include('reviewoptions.html');
|
||||
echo '</tr>';
|
||||
} ?>
|
||||
|
||||
<?php if ($CFG->quiz_fix_popup) { ?>
|
||||
<tr valign="top">
|
||||
@@ -591,6 +550,8 @@
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
</div></td></tr>
|
||||
@@ -602,6 +563,7 @@
|
||||
|
||||
</table>
|
||||
<br />
|
||||
<input type="hidden" name="grade" value="<?php echo $form->grade; ?>" />
|
||||
|
||||
<!-- these hidden variables are always the same -->
|
||||
<input type="hidden" name="course" value="<?php p($form->course) ?>" />
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
<?php // $Id$
|
||||
// A quick way to add lots of questions to a category (and a quiz)
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once('locallib.php');
|
||||
|
||||
$category = required_param('category');
|
||||
|
||||
// This script can only be called while editing a quiz
|
||||
|
||||
if (!isset($SESSION->modform)) {
|
||||
error('You have used this page incorrectly!');
|
||||
} else {
|
||||
$modform = $SESSION->modform;
|
||||
}
|
||||
|
||||
if (! $category = get_record('quiz_categories', 'id', $category)) {
|
||||
error('Course ID is incorrect');
|
||||
}
|
||||
|
||||
if (! $course = get_record('course', 'id', $category->course)) {
|
||||
error('Course ID is incorrect');
|
||||
}
|
||||
|
||||
require_login($course->id, false);
|
||||
|
||||
if (!isteacher($course->id)) {
|
||||
error('Only teachers can use this page!');
|
||||
}
|
||||
|
||||
|
||||
/// If data submitted, then process and store.
|
||||
|
||||
if ($form = data_submitted() and confirm_sesskey()) {
|
||||
if ($form->randomcreate > 0) {
|
||||
$newquestionids = array(); // this will hold the ids of the random questions
|
||||
|
||||
// find existing random questions in this category
|
||||
$random = RANDOM;
|
||||
if ($existingquestions = get_records_select('quiz_questions', "qtype = '$random' AND category = '$category->id'")) {
|
||||
// now remove the ones that are already used in this quiz
|
||||
if ($questionids = explode(',', $modform->questions)) {
|
||||
foreach ($questionids as $questionid) {
|
||||
unset($existingquestions[$questionid]);
|
||||
}
|
||||
}
|
||||
// now take as many of these as needed
|
||||
$i = 0;
|
||||
while (($existingquestion = array_pop($existingquestions)) and ($i < $form->randomcreate)) {
|
||||
if ($existingquestion->questiontext == "$form->recurse") {
|
||||
// this question has the right recurse property, so use it
|
||||
$newquestionids[] = $existingquestion->id;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
$randomcreate = $form->randomcreate - $i; // the number of additional random questions needed.
|
||||
} else {
|
||||
$randomcreate = $form->randomcreate;
|
||||
}
|
||||
|
||||
if ($randomcreate > 0) {
|
||||
|
||||
$question->qtype = RANDOM;
|
||||
$question->category = $category->id;
|
||||
$question->name = get_string('random', 'quiz') .' ('. $category->name .')';
|
||||
$question->questiontext = "$form->recurse"; // we use the questiontext field to store the info
|
||||
// on whether to include questions in subcategories
|
||||
$question->image = '';
|
||||
$question->defaultgrade = $form->randomgrade;
|
||||
$question->hidden = 1;
|
||||
for ($i=0; $i<$randomcreate; $i++) {
|
||||
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
|
||||
if (!$newquestionids[] = insert_record('quiz_questions', $question)) {
|
||||
error('Could not insert new random question!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add them to the quiz
|
||||
if (!empty($modform->questions)) {
|
||||
$questionids = explode(',', $modform->questions);
|
||||
} else {
|
||||
$questionids = array();
|
||||
}
|
||||
|
||||
foreach ($newquestionids as $newquestionid) {
|
||||
$modform->grades[$newquestionid] = $form->randomgrade;
|
||||
$modform->sumgrades += $form->randomgrade;
|
||||
}
|
||||
|
||||
$newquestionids = array_merge($questionids, $newquestionids);
|
||||
$modform->questions = implode(',', $newquestionids);
|
||||
$SESSION->modform = $modform;
|
||||
if (!set_field('quiz', 'questions', $modform->questions, 'id', $modform->instance)) {
|
||||
error('Could not save question list');
|
||||
}
|
||||
quiz_questiongrades_update($modform->grades, $modform->instance);
|
||||
}
|
||||
redirect('edit.php');
|
||||
}
|
||||
|
||||
|
||||
/// Otherwise print the form
|
||||
|
||||
/// Print headings
|
||||
|
||||
$strquestions = get_string('questions', 'quiz');
|
||||
$strpublish = get_string('publish', 'quiz');
|
||||
$strdelete = get_string('delete');
|
||||
$straction = get_string('action');
|
||||
$stradd = get_string('add');
|
||||
$strcancel = get_string('cancel');
|
||||
$strsavechanges = get_string('savechanges');
|
||||
$strbacktoquiz = get_string('backtoquiz', 'quiz');
|
||||
$strquizzes = get_string('modulenameplural', 'quiz');
|
||||
$streditingquiz = get_string(isset($SESSION->modform->instance) ? 'editingquiz' : 'editquestions', 'quiz');
|
||||
$strcreatemultiple = get_string('createmultiple', 'quiz');
|
||||
|
||||
print_header_simple($strcreatemultiple, $strcreatemultiple,
|
||||
"<a href=\"$CFG->wwwroot/mod/quiz/index.php?id=$course->id\">$strquizzes</a>".
|
||||
" -> <a href=\"edit.php\">$streditingquiz</a> -> $strcreatemultiple");
|
||||
|
||||
|
||||
print_heading_with_help($strcreatemultiple, 'createmultiple', 'quiz');
|
||||
|
||||
if (!$categories = quiz_get_category_menu($course->id, false)) {
|
||||
error('No categories!');
|
||||
}
|
||||
|
||||
for ($i=1;$i<=100; $i++) {
|
||||
$randomcount[$i] = $i;
|
||||
}
|
||||
for ($i=1;$i<=10; $i++) {
|
||||
$gradecount[$i] = $i;
|
||||
}
|
||||
$options = array();
|
||||
$options[0] = get_string('no');
|
||||
$options[1] = get_string('yes');
|
||||
|
||||
print_simple_box_start('center');
|
||||
echo '<form method="POST" action="multiple.php">';
|
||||
echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\">";
|
||||
echo '<table cellpadding="5">';
|
||||
echo '<tr><td align="right">';
|
||||
print_string('category', 'quiz');
|
||||
echo ':</td><td>';
|
||||
// choose_from_menu($categories, "category", "$category->id", "");
|
||||
quiz_category_select_menu($course->id, false, true, $category->id );
|
||||
echo '</tr>';
|
||||
|
||||
echo '<tr><td align="right">';
|
||||
print_string('randomcreate', 'quiz');
|
||||
echo ':</td><td>';
|
||||
choose_from_menu($randomcount, 'randomcreate', '10', '');
|
||||
echo '</tr>';
|
||||
|
||||
echo '<tr><td align="right">';
|
||||
print_string('defaultgrade', 'quiz');
|
||||
echo ':</td><td>';
|
||||
choose_from_menu($gradecount, 'randomgrade', '1', '');
|
||||
echo '</tr>';
|
||||
|
||||
echo '<tr><td> </td><td>';
|
||||
echo ' <input type="hidden" name="category" value="'. $category->id .'" />';
|
||||
echo ' <input type="submit" name="save" value="'. get_string('add') .'" />';
|
||||
echo '</td></tr>';
|
||||
echo '</table>';
|
||||
echo '</form>';
|
||||
print_simple_box_end();
|
||||
|
||||
print_footer($course);
|
||||
|
||||
?>
|
||||
@@ -63,9 +63,7 @@ class page_quiz extends page_generic_activity {
|
||||
}
|
||||
|
||||
if(empty($morebreadcrumbs) && $this->user_allowed_editing()) {
|
||||
$buttons = '<table><tr><td><form target="'.$CFG->framename.'" method="get" action="edit.php">'.
|
||||
'<input type="hidden" name="quizid" value="'.$this->activityrecord->id.'" />'.
|
||||
'<input type="submit" value="'.get_string('editquestions', 'quiz').'" /></form></td><td>'.
|
||||
$buttons = '<table><tr><td>'.
|
||||
update_module_button($this->modulerecord->id, $this->courserecord->id, get_string('modulename', 'quiz')).'</td>';
|
||||
if(!empty($CFG->showblocksonmodpages)) {
|
||||
$buttons .= '<td><form target="'.$CFG->framename.'" method="get" action="view.php">'.
|
||||
@@ -87,4 +85,4 @@ class page_quiz extends page_generic_activity {
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
+209
-88
@@ -1,104 +1,225 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* This page displays a preview of a question
|
||||
*
|
||||
* The preview uses the option settings from the quiz within which the question
|
||||
* is previewed or the default settings if no quiz is specified. The question session
|
||||
* information is stored in the session as an array of subsequent states rather
|
||||
* than in the database.
|
||||
* @version $Id$
|
||||
* @author Alex Smith as part of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
|
||||
require_variable($id); // question id
|
||||
optional_variable($quizid, 0);
|
||||
|
||||
if (! $question = get_record("quiz_questions", "id", $id)) {
|
||||
error("This question doesn't exist");
|
||||
}
|
||||
if (! $category = get_record("quiz_categories", "id", $question->category)) {
|
||||
error("This question doesn't belong to a valid category!");
|
||||
}
|
||||
if ($quizid) {
|
||||
if (! $quiz = get_record('quiz', 'id', $quizid)) {
|
||||
error('Incorrect quiz id');
|
||||
$id = required_param('id', PARAM_INT); // question id
|
||||
// if no quiz id is specified then a dummy quiz with default options is used
|
||||
$quizid = optional_param('quizid', 0, PARAM_INT);
|
||||
// Test if we are continuing an attempt at a question
|
||||
$continue = optional_param('continue', 0, PARAM_BOOL);
|
||||
// Check for any of the submit buttons
|
||||
$fillcorrect = optional_param('fillcorrect', 0, PARAM_BOOL);
|
||||
$markall = optional_param('markall', 0, PARAM_BOOL);
|
||||
$finishattempt = optional_param('finishattempt', 0, PARAM_BOOL);
|
||||
$back = optional_param('back', 0, PARAM_BOOL);
|
||||
$startagain = optional_param('startagain', 0, PARAM_BOOL);
|
||||
// We are always continuing an attempt if a submit button was pressed with the
|
||||
// exception of the start again button
|
||||
if ($fillcorrect || $markall || $finishattempt || $back) {
|
||||
$continue = true;
|
||||
} else if ($startagain) {
|
||||
$continue = false;
|
||||
}
|
||||
|
||||
require_login();
|
||||
|
||||
if (!isteacherinanycourse()) {
|
||||
error('This page is for teachers only');
|
||||
}
|
||||
|
||||
if (!$continue) {
|
||||
// Start a new attempt; delete the old session
|
||||
unset($SESSION->quizpreview);
|
||||
// Redirect to ourselves but with continue=1; prevents refreshing the page
|
||||
// from restarting an attempt (needed so that random questions don't change)
|
||||
$quizid = $quizid ? '&quizid=' . $quizid : '';
|
||||
redirect($CFG->wwwroot . '/mod/quiz/preview.php?id=' . $id . $quizid .
|
||||
'&continue=1');
|
||||
}
|
||||
|
||||
if (empty($quizid)) {
|
||||
// get a sample quiz to be used as a skeleton
|
||||
// this should really be done properly by instantiating a quiz object
|
||||
if (!$quiz = get_records('quiz')) {
|
||||
error('You have to create at least one quiz before using this preview');
|
||||
}
|
||||
$quiz = array_values($quiz);
|
||||
$quiz = $quiz[0];
|
||||
|
||||
// set everything to the default values
|
||||
foreach($quiz as $field => $value) {
|
||||
$quizfield = "quiz_".$field;
|
||||
if(isset($CFG->$quizfield)) {
|
||||
$quiz->$field = $CFG->$quizfield;
|
||||
}
|
||||
else {
|
||||
$quiz->$field = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!$quiz = get_record('quiz', 'id', $quizid)) {
|
||||
error("Quiz id $quizid does not exist");
|
||||
}
|
||||
|
||||
$quiz->id = 0; // just for safety
|
||||
$quiz->questions = $id;
|
||||
|
||||
// Load the question information
|
||||
if (!$questions = get_records('quiz_questions', 'id', $id)) {
|
||||
error('Could not load question');
|
||||
}
|
||||
$questions[$id]->quiz = 0;
|
||||
$questions[$id]->maxgrade = 1;
|
||||
|
||||
if (!$category = get_record("quiz_categories", "id", $questions[$id]->category)) {
|
||||
error("This question doesn't belong to a valid category!");
|
||||
}
|
||||
|
||||
if (!isteacher($category->course) and !$category->publish) {
|
||||
error("You can't preview these questions!");
|
||||
}
|
||||
} else {
|
||||
// make fake quiz
|
||||
$quiz->id = 0;
|
||||
$quiz->course = $category->course;
|
||||
$quiz->name = "";
|
||||
$quiz->intro = "";
|
||||
$quiz->timeopen = 0;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->attempts = $CFG->quiz_attempts;
|
||||
$quiz->attemptonlast = $CFG->quiz_attemptonlast;
|
||||
$quiz->feedback = $CFG->quiz_showfeedback;
|
||||
$quiz->correctanswers = $CFG->quiz_showanswer;
|
||||
$quiz->grademethod = $CFG->quiz_grademethod;
|
||||
$quiz->review = $CFG->quiz_allowreview;
|
||||
$quiz->shufflequestions = $CFG->quiz_shufflequestions;
|
||||
$quiz->shuffleanswers = $CFG->quiz_shuffleanswers;
|
||||
$quiz->questions = "$question->id";
|
||||
$quiz->sumgrades = $question->defaultgrade;
|
||||
$quiz->grade = 0;
|
||||
$quiz->timecreated = 0;
|
||||
$quiz->timemodified = 0;
|
||||
$quiz->timelimit = $CFG->quiz_timelimit;
|
||||
$quiz->password = $CFG->quiz_password;
|
||||
$quiz->subnet = $CFG->quiz_subnet;
|
||||
$quiz->popup = $CFG->quiz_popup;
|
||||
}
|
||||
|
||||
$qtype = $question->qtype;
|
||||
|
||||
require_login();
|
||||
|
||||
if (!isteacherinanycourse()) {
|
||||
error('This page is for teachers only');
|
||||
}
|
||||
|
||||
if (!isteacher($category->course) and !$category->publish) {
|
||||
error("You can't preview these questions!");
|
||||
}
|
||||
|
||||
print_header();
|
||||
print_heading(get_string("previewquestion","quiz"));
|
||||
|
||||
if ($rawanswers = data_submitted()) {
|
||||
$rawanswers = (array)$rawanswers;
|
||||
|
||||
foreach ($rawanswers as $key => $value) { // Parse input for question->response
|
||||
$postedId = quiz_extract_posted_id($key);
|
||||
if ($id == $postedId) $question->response[$key] = trim($value);
|
||||
// Load the question type specific information
|
||||
if (!quiz_get_question_options($questions)) {
|
||||
error(get_string('newattemptfail', 'quiz'));
|
||||
}
|
||||
|
||||
print_simple_box_start("center", "90%");
|
||||
|
||||
$resultdetails = $QUIZ_QTYPES[$question->qtype]->grade_response($question, quiz_qtype_nameprefix($question));
|
||||
|
||||
$question->maxgrade = 1;
|
||||
$quiz->correctanswers=1;
|
||||
$quiz->feedback=1;
|
||||
$quiz->grade=1;
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question(1, $quiz, $question, true, $resultdetails);
|
||||
// Create a dummy quiz attempt
|
||||
$attempt = quiz_create_attempt($quiz, 0);
|
||||
$attempt->id = 0;
|
||||
|
||||
print_simple_box_end();
|
||||
echo "<p align=\"center\"><input name=\"backbtn\" type=\"button\"
|
||||
onclick=\"history.back()\" value=\"".get_string("back", "quiz")."\" />\n";
|
||||
echo "<input type=\"button\" onClick=\"window.close()\" value=\"" . get_string("close", "quiz") . "\" /></p>\n";
|
||||
// Restore the history of question sessions from the moodle session or create
|
||||
// new sessions. Make $states a reference to the states array in the moodle
|
||||
// session.
|
||||
if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid
|
||||
== $id) {
|
||||
// Reload the question session history from the moodle session
|
||||
$states =& $SESSION->quizpreview->states;
|
||||
$historylength = count($states) - 1;
|
||||
if ($back && $historylength > 0) {
|
||||
// Go back one step in the history
|
||||
unset($states[$historylength]);
|
||||
$historylength--;
|
||||
}
|
||||
} else {
|
||||
// Record the question id in the moodle session
|
||||
$SESSION->quizpreview->questionid = $id;
|
||||
// Create an empty session for the question
|
||||
if (!$newstates =
|
||||
quiz_restore_question_sessions($questions, $quiz, $attempt)) {
|
||||
error(get_string('newattemptfail', 'quiz'));
|
||||
}
|
||||
$SESSION->quizpreview->states = array($newstates);
|
||||
$states =& $SESSION->quizpreview->states;
|
||||
$historylength = 0;
|
||||
}
|
||||
|
||||
} else { //show question list
|
||||
if (!$fillcorrect && !$back && ($form = data_submitted())) {
|
||||
$form = (array)$form;
|
||||
$submitted = true;
|
||||
|
||||
// Create a new item in the history of question states (don't simplify!)
|
||||
$states[$historylength + 1] = array();
|
||||
$states[$historylength + 1][$id] = clone($states[$historylength][$id]);
|
||||
$historylength++;
|
||||
$curstate =& $states[$historylength][$id];
|
||||
|
||||
// Process the responses
|
||||
unset($form['id']);
|
||||
unset($form['quizid']);
|
||||
unset($form['continue']);
|
||||
unset($form['markall']);
|
||||
unset($form['finishattempt']);
|
||||
unset($form['back']);
|
||||
unset($form['startagain']);
|
||||
|
||||
$event = $finishattempt ? QUIZ_EVENTCLOSE : ($markall ? QUIZ_EVENTGRADE : QUIZ_EVENTSAVE);
|
||||
if ($actions = quiz_extract_responses($questions, $form, $event)) {
|
||||
quiz_process_responses($questions[$id], $states[$historylength][$id], $actions[$id], $quiz, $attempt);
|
||||
if (QUIZ_EVENTGRADE != $curstate->event && QUIZ_EVENTCLOSE != $curstate->event) {
|
||||
// Update the current state rather than creating a new one
|
||||
$historylength--;
|
||||
unset($states[$historylength]);
|
||||
$states = array_values($states);
|
||||
$curstate =& $states[$historylength][$id];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$submitted = false;
|
||||
$curstate =& $states[$historylength][$id];
|
||||
}
|
||||
|
||||
$options = quiz_get_renderoptions($quiz, $curstate);
|
||||
|
||||
// Fill in the correct responses (unless the question is in readonly mode)
|
||||
if ($fillcorrect && !$options->readonly) {
|
||||
$curstate->responses = $QUIZ_QTYPES[$questions[$id]->qtype]
|
||||
->get_correct_responses($questions[$id], $curstate);
|
||||
}
|
||||
|
||||
$strpreview = get_string('previewquestion', 'quiz');
|
||||
print_header($strpreview);
|
||||
print_heading($strpreview);
|
||||
|
||||
echo '<p align="center">' . get_string('modulename', 'quiz') . ': ';
|
||||
if (empty($quizid)) {
|
||||
echo '[' . get_string('default', 'quiz') . ']';
|
||||
} else {
|
||||
p($quiz->name);
|
||||
}
|
||||
echo '<br />'.get_string('question', 'quiz').': ';
|
||||
p($questions[$id]->name);
|
||||
echo "</p>\n";
|
||||
$number = 1;
|
||||
echo "<form method=\"post\" action=\"preview.php\">\n";
|
||||
echo "<input type=\"hidden\" name=\"id\" value=\"$id\" />\n";
|
||||
echo "<input type=\"hidden\" name=\"quizid\" value=\"$quizid\" />\n";
|
||||
echo "<input type=\"hidden\" name=\"continue\" value=\"1\" />\n";
|
||||
|
||||
print_simple_box_start("center", "90%");
|
||||
$readonly = false;
|
||||
$resultdetails = NULL;
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question(1, $quiz, $question, $readonly, $resultdetails);
|
||||
print_simple_box_end();
|
||||
quiz_print_quiz_question($questions[$id], $curstate, $number, $quiz, $options);
|
||||
|
||||
echo "<br />";
|
||||
echo "<center>";
|
||||
echo "<input name=\"submit\" type=\"submit\" value=\"".get_string("checkanswer", "quiz")."\" />\n";
|
||||
echo "<input type=\"button\" onClick=\"window.close()\" value=\"" . get_string("close", "quiz") . "\" />";
|
||||
echo "</center>";
|
||||
echo "</form>";
|
||||
}
|
||||
|
||||
print_footer();
|
||||
echo '<br />';
|
||||
echo '<center>';
|
||||
|
||||
// Print the mark and finish attempt buttons
|
||||
echo '<input name="markall" type="submit" value="' . get_string('markall',
|
||||
'quiz') . "\" />\n";
|
||||
echo '<input name="finishattempt" type="submit" value="' .
|
||||
get_string('finishattempt', 'quiz') . "\" />\n";
|
||||
echo '<br />';
|
||||
echo '<br />';
|
||||
// Print the fill correct button (unless the question is in readonly mode)
|
||||
if (!$options->readonly) {
|
||||
echo '<input name="fillcorrect" type="submit" value="' .
|
||||
get_string('fillcorrect', 'quiz') . "\" />\n";
|
||||
}
|
||||
// Print the navigation buttons
|
||||
if ($historylength > 0) {
|
||||
echo '<input name="back" type="submit" value="' . get_string('previous',
|
||||
'quiz') . "\" />\n";
|
||||
}
|
||||
// Print the start again button
|
||||
echo '<input name="startagain" type="submit" value="' .
|
||||
get_string('startagain', 'quiz') . "\" />\n";
|
||||
// Print the close window button
|
||||
echo '<input type="button" onclick="window.close()" value="' .
|
||||
get_string('closepreview', 'quiz') . "\" />";
|
||||
echo '</center>';
|
||||
echo '</form>';
|
||||
print_footer();
|
||||
?>
|
||||
|
||||
+81
-55
@@ -1,14 +1,22 @@
|
||||
<?php // $Id$
|
||||
/* Responsibilities of question.php
|
||||
* - create new question (category, qtype)
|
||||
* - edit question (id, contextquiz (o))
|
||||
* - delete question from quiz (delete, sesskey)
|
||||
* - delete question (in two steps)
|
||||
* - if question is in use: display this conflict (allow to hide the question?)
|
||||
* - else: confirm deletion and delete from database (sesskey, id, delete, confirm)
|
||||
* - cancel (cancel)
|
||||
* (o) = optional
|
||||
*/
|
||||
/*
|
||||
* Page for editing questions
|
||||
*
|
||||
* This page shows the question editing form or processes the following actions:
|
||||
* - create new question (category, qtype)
|
||||
* - edit question (id, contextquiz (optional))
|
||||
* - delete question from quiz (delete, sesskey)
|
||||
* - delete question (in two steps)
|
||||
* - if question is in use: display this conflict (allow to hide the question?)
|
||||
* - else: confirm deletion and delete from database (sesskey, id, delete, confirm)
|
||||
* - cancel (cancel)
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been extensively
|
||||
* rewritten by members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
@@ -18,7 +26,7 @@
|
||||
|
||||
$qtype = optional_param('qtype');
|
||||
$category = optional_param('category');
|
||||
|
||||
|
||||
$contextquiz = optional_param('contextquiz'); // the quiz from which this question is being edited
|
||||
|
||||
if (isset($_REQUEST['cancel'])) {
|
||||
@@ -93,9 +101,8 @@
|
||||
if (isset($_REQUEST['delete'])) {
|
||||
if (isset($confirm) and confirm_sesskey()) {
|
||||
if ($confirm == md5($delete)) {
|
||||
if (record_exists('quiz_responses', 'question', $question->id) or
|
||||
record_exists('quiz_responses', 'originalquestion', $question->id) or
|
||||
record_exists('quiz_question_grades', 'question', $question->id)) {
|
||||
if (record_exists('quiz_question_instances', 'question', $question->id) or
|
||||
record_exists('quiz_states', 'originalquestion', $question->id)) {
|
||||
if (!set_field('quiz_questions', 'hidden', 1, 'id', $delete)) {
|
||||
error('Was not able to hide question');
|
||||
}
|
||||
@@ -103,20 +110,23 @@
|
||||
if (!delete_records("quiz_questions", "id", $question->id)) {
|
||||
error("An error occurred trying to delete question (id $question->id)");
|
||||
}
|
||||
if (!delete_records("quiz_questions", "parent", $question->id)) {
|
||||
error("An error occurred trying to delete question (id $question->id)");
|
||||
}
|
||||
}
|
||||
redirect("edit.php");
|
||||
} else {
|
||||
error("Confirmation string was incorrect");
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
if ($quiznames = quizzes_question_used($id)) {
|
||||
$a->questionname = $question->name;
|
||||
$a->quiznames = implode(', ', $quiznames);
|
||||
notify(get_string('questioninuse', 'quiz', $a));
|
||||
}
|
||||
|
||||
notice_yesno(get_string("deletequestioncheck", "quiz", $question->name),
|
||||
|
||||
notice_yesno(get_string("deletequestioncheck", "quiz", $question->name),
|
||||
"question.php?sesskey=$USER->sesskey&id=$question->id&delete=$delete&confirm=".md5($delete), "edit.php");
|
||||
}
|
||||
print_footer($course);
|
||||
@@ -124,12 +134,13 @@
|
||||
}
|
||||
|
||||
if ($form = data_submitted() and confirm_sesskey()) {
|
||||
|
||||
if (isset($form->versioning)) { // use new code that
|
||||
// handles whether to overwrite or copy a question and keeps
|
||||
// track of the versions in the quiz_question_versions table
|
||||
|
||||
// $replaceinquiz is an array with the ids of all quizzes in which the teacher has chosen to replace the old version
|
||||
|
||||
if (isset($form->versioning) && isset($question->id)) {
|
||||
// use new code that handles whether to overwrite or copy a question
|
||||
// and keeps track of the versions in the quiz_question_version table
|
||||
|
||||
// $replaceinquiz is an array with the ids of all quizzes in which
|
||||
// the teacher has chosen to replace the old version
|
||||
$replaceinquiz = array();
|
||||
foreach($form as $key => $val) {
|
||||
if ($tmp = quiz_parse_fieldname($key, 'q')) {
|
||||
@@ -142,23 +153,28 @@
|
||||
|
||||
// $quizlist is an array with the ids of quizzes which use this question
|
||||
$quizlist = array();
|
||||
if ($grades = get_records('quiz_question_grades', 'question', $form->id)) {
|
||||
foreach($grades as $grade) {
|
||||
$quizlist[$grade->quiz] = $grade->quiz;
|
||||
if ($instances = get_records('quiz_question_instances', 'question', $question->id)) {
|
||||
foreach($instances as $instance) {
|
||||
$quizlist[$instance->quiz] = $instance->quiz;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isset($form->makecopy)) { // explicitly requested copies should be unhidden
|
||||
$question->hidden = 0;
|
||||
}
|
||||
|
||||
// Logic to determine whether old version should be overwritten
|
||||
$makecopy = isset($form->makecopy) || (!$form->id); unset($form->makecopy);
|
||||
$noresponses = $makecopy || !(record_exists('quiz_responses', 'question', $form->id) or
|
||||
record_exists('quiz_responses', 'originalquestion', $form->id)); // Should be improved once the preview flag exists so that teacher preview responses are not taken into account
|
||||
$notused = $makecopy || !record_exists('quiz_question_grades', 'question', $form->id);
|
||||
$replaceinall = ($quizlist == $replaceinquiz); // question is being replaced in all quizzes
|
||||
$replaceold = (!$makecopy && $noresponses && ($notused || $replaceinall));
|
||||
$makecopy = isset($form->makecopy) || (!$form->id); unset($form->makecopy);
|
||||
if ($makecopy) {
|
||||
$replaceold = false;
|
||||
} else {
|
||||
// this should be improved to exclude teacher preview responses and empty responses
|
||||
// the current code leaves many unneeded questions in the database
|
||||
$hasresponses = record_exists('quiz_states', 'question', $form->id) or
|
||||
record_exists('quiz_states', 'originalquestion', $form->id);
|
||||
$replaceinall = ($quizlist == $replaceinquiz); // question is being replaced in all quizzes
|
||||
$replaceold = !$hasresponses && $replaceinall;
|
||||
}
|
||||
|
||||
if (!$replaceold) { // create a new question
|
||||
$oldquestionid = $question->id;
|
||||
@@ -169,10 +185,8 @@
|
||||
}
|
||||
unset($question->id);
|
||||
}
|
||||
unset($makecopy, $noresponses, $notused, $replaceinall, $replaceold);
|
||||
|
||||
unset($makecopy, $hasresponses, $replaceinall, $replaceold);
|
||||
$question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course);
|
||||
|
||||
if(!isset($question->id)) {
|
||||
error("Failed to save the question!");
|
||||
}
|
||||
@@ -182,7 +196,7 @@
|
||||
$version = new object();
|
||||
$version->oldquestion = $oldquestionid;
|
||||
$version->newquestion = $question->id;
|
||||
$version->userid = $USER->id; // field still needs to be added to table
|
||||
$version->userid = $USER->id;
|
||||
$version->timestamp = time();
|
||||
|
||||
foreach($replaceinquiz as $qid) {
|
||||
@@ -193,11 +207,10 @@
|
||||
}
|
||||
|
||||
/// now update the question references in the quizzes
|
||||
|
||||
$quizzes = implode(',', $replaceinquiz);
|
||||
if (empty($quizzes) || !$quizzes = get_records_list("quiz", "id", $quizzes)) {
|
||||
if (empty($replaceinquiz) || !$quizzes = get_records_list("quiz", "id", implode(',', $replaceinquiz))) {
|
||||
$quizzes = array();
|
||||
}
|
||||
|
||||
foreach($quizzes as $quiz) {
|
||||
$questionlist = ",$quiz->questions,"; // a little hack with the commas here. not nice but effective
|
||||
$questionlist = str_replace(",$oldquestionid,", ",$question->id,", $questionlist);
|
||||
@@ -205,9 +218,10 @@
|
||||
if (!set_field("quiz", 'questions', $questionlist, 'id', $quiz->id)) {
|
||||
error("Could not update questionlist in quiz $quiz->id!");
|
||||
}
|
||||
// the quiz_question_grades table needs to be updated too (aah, the joys of duplication :)
|
||||
if (!set_field('quiz_question_grades', 'question', $question->id, 'quiz', $quiz->id, 'question', $oldquestionid)) {
|
||||
error("Could not update question grade!");
|
||||
|
||||
// the quiz_question_instances table needs to be updated too (aah, the joys of duplication :)
|
||||
if (!set_field('quiz_question_instances', 'question', $question->id, 'quiz', $quiz->id, 'question', $oldquestionid)) {
|
||||
error("Could not update question instance!");
|
||||
}
|
||||
if (isset($SESSION->modform) && (int)$SESSION->modform->instance === (int)$quiz->id) {
|
||||
$SESSION->modform->questions = $questionlist;
|
||||
@@ -215,24 +229,29 @@
|
||||
unset($SESSION->modform->grades[$oldquestionid]);
|
||||
}
|
||||
}
|
||||
|
||||
// fix responses
|
||||
|
||||
// set originalquestion in states
|
||||
if ($attempts = get_records_list('quiz_attempts', 'quiz', implode(',', $replaceinquiz))) {
|
||||
foreach ($attempts as $attempt) {
|
||||
if (!set_field('quiz_responses', 'originalquestion', $oldquestionid, 'attempt', $attempt->id, 'question', $oldquestionid)
|
||||
or !set_field('quiz_responses', 'question', $question->id, 'attempt', $attempt->id, 'question', $oldquestionid)) {
|
||||
error("Could not point responses to new question");
|
||||
}
|
||||
set_field('quiz_states', 'originalquestion', $oldquestionid, 'attempt', $attempt->id, 'question', $question->id, 'originalquestion', '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redirect("edit.php");
|
||||
} else { // use the old code which simply overwrites old versions
|
||||
} else {
|
||||
// use the old code which simply overwrites old versions
|
||||
// it is also used for creating new questions
|
||||
$question = $QUIZ_QTYPES[$qtype]->save_question($question, $form, $course);
|
||||
$quizlist = array();
|
||||
}
|
||||
|
||||
if (empty($question->errors) &&
|
||||
$QUIZ_QTYPES[$qtype]->finished_edit_wizard($form)) {
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
// Automagically regrade all attempts (and states) in the affected quizzes
|
||||
quiz_regrade_question_in_quizzes($question, $quizlist);
|
||||
redirect("edit.php");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$grades = array(1,0.9,0.8,0.75,0.70,0.66666,0.60,0.50,0.40,0.33333,0.30,0.25,0.20,0.16666,0.142857,0.10,0.05,0);
|
||||
foreach ($grades as $grade) {
|
||||
@@ -274,6 +293,9 @@
|
||||
if (empty($question->image)) {
|
||||
$question->image = "";
|
||||
}
|
||||
if (!isset($question->penalty)) {
|
||||
$question->penalty = 0.1;
|
||||
}
|
||||
|
||||
// Set up some Richtext editing if necessary
|
||||
if ($usehtmleditor = can_use_richtext_editor()) {
|
||||
@@ -281,13 +303,17 @@
|
||||
} else {
|
||||
$defaultformat = FORMAT_MOODLE;
|
||||
}
|
||||
|
||||
|
||||
if (isset($question->errors)) {
|
||||
$err = $question->errors;
|
||||
}
|
||||
|
||||
echo '<br />';
|
||||
print_simple_box_start('center');
|
||||
require('questiontypes/'.$QUIZ_QTYPES[$qtype]->name().'/editquestion.php');
|
||||
print_simple_box_end();
|
||||
|
||||
if ($usehtmleditor) {
|
||||
if ($usehtmleditor) {
|
||||
use_html_editor('questiontext');
|
||||
}
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ if ($form) {
|
||||
or $answers[0]->answer
|
||||
and $calculatedmessages[] = $formulaerrors
|
||||
or $calculatedmesages[] = get_string('missingformula', 'quiz');
|
||||
|
||||
|
||||
$answers[0]->tolerance = array_shift($form->tolerance)
|
||||
or $answers[0]->tolerance = 0.0;
|
||||
is_numeric($answers[0]->tolerance)
|
||||
or $calculatedmessages[] = get_string('tolerancemustbenumeric', 'quiz');
|
||||
|
||||
$answers[0]->feedback = array_shift($form->feedback);
|
||||
|
||||
|
||||
// Let's trust the drop down menus.
|
||||
|
||||
$answers[0]->tolerancetype = array_shift($form->tolerancetype);
|
||||
@@ -80,7 +80,7 @@ if ($form) {
|
||||
$units[0]->unit = array_shift($form->unit);
|
||||
array_shift($form->multiplier); // In case it is not 1.0
|
||||
$units[0]->multiplier = 1.0; // Must!
|
||||
|
||||
|
||||
// Accept other units if they have legal multipliers
|
||||
$i = 1;
|
||||
foreach ($form->multiplier as $key => $multiplier) {
|
||||
@@ -108,7 +108,7 @@ if ($form) {
|
||||
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/datasetitems.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
/***** Now continue by preparing for the second page ******
|
||||
***** in the question wizard: "questiondatasets.html" ******/
|
||||
$datasetmessage = '';
|
||||
@@ -138,7 +138,7 @@ if ($form) {
|
||||
$hiddeninputnames[] = 'multiplier[]';
|
||||
$hiddeninputvalues[] = $unit->multiplier;
|
||||
}
|
||||
|
||||
|
||||
/*** Determine possible and mandatory datasets... ***/
|
||||
$possibledatasets = $qtypeobj->find_dataset_names($question->questiontext);
|
||||
$mandatorydatasets = array();
|
||||
@@ -154,7 +154,7 @@ if ($form) {
|
||||
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/questiondatasets.html");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
/*********************************************************/
|
||||
/***** First page in question wizard - calculated.html! **/
|
||||
@@ -163,7 +163,7 @@ if ($form) {
|
||||
// The layout of the editing page will only support
|
||||
// one formula alternative for calculated questions.
|
||||
// However, the code behind supports up to six formulas
|
||||
// and the database store and attempt/review framework
|
||||
// and the database store and attempt/review framework
|
||||
// does not have any limit.
|
||||
$answers= array();
|
||||
for ($i=0; $i<6; $i++) {
|
||||
@@ -177,7 +177,8 @@ if ($form) {
|
||||
$answers[$i]->correctanswerformat = "1"; // ... decimals
|
||||
}
|
||||
if (!empty($question->id)) {
|
||||
$answersraw = $qtypeobj->get_answers($question);
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
$answersraw = $question->options->answers;
|
||||
if (!empty($answersraw)) {
|
||||
/*** Overwrite the default valued answer slots ***
|
||||
*** with correct values from database ***/
|
||||
@@ -233,7 +234,6 @@ if ($form) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_heading_with_help(get_string("editingcalculated", "quiz"), "calculated", "quiz");
|
||||
require("$CFG->dirroot/mod/quiz/questiontypes/calculated/calculated.html");
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php // $Id$
|
||||
|
||||
///////////////////////////////
|
||||
/// CALCULATED HELPER CLASS ///
|
||||
///////////////////////////////
|
||||
|
||||
/// OVERRIDDEN EDITION OF THE CLASS FOR QUESTION TYPE NUMERICAL ///
|
||||
|
||||
class quiz_calculated_qtype_numerical_helper extends quiz_numerical_qtype {
|
||||
/// A question with qtype=CALCULATED will appear as a NUMERICAL
|
||||
/// question in a quiz and it is therefore desirable to reuse
|
||||
/// most of the grading and printing framework.
|
||||
/// However, the CALCULATED functions will be fed with data
|
||||
/// that differs from what the NUMERICAL qtype can handle.
|
||||
/// Therefore the CALCULATED qtype often act by modifying the data
|
||||
/// it has been fed and then pass it on to the NUMERICAL equivalent.
|
||||
|
||||
/// The NUMERICAL equivalent are called through an instance of this class,
|
||||
/// for which the method get_answers has been modified so that its
|
||||
/// caller will be fed with data fed to the qtype CALCULATED and then
|
||||
/// modified to fit qtype NUMERICAL.
|
||||
|
||||
// This solution assumes a single-threaded environment
|
||||
// for each instance...
|
||||
|
||||
var $answers = false;
|
||||
|
||||
function get_answers($question, $addedcondition='') {
|
||||
return $this->answers;
|
||||
}
|
||||
|
||||
function set_answers($calculatedanswers) {
|
||||
$this->answers = $calculatedanswers;
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
?>
|
||||
@@ -11,23 +11,153 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
|
||||
// Used by the function custom_generator_tools:
|
||||
var $calcgenerateidhasbeenadded = false;
|
||||
|
||||
function get_answers($question) {
|
||||
global $CFG;
|
||||
return get_records_sql(
|
||||
"SELECT a.*, c.tolerance, c.tolerancetype, c.correctanswerlength, c.correctanswerformat, c.id as calcid
|
||||
FROM {$CFG->prefix}quiz_answers a,
|
||||
{$CFG->prefix}quiz_calculated c
|
||||
WHERE c.question = $question->id AND a.id = c.answer");
|
||||
}
|
||||
|
||||
|
||||
function name() {
|
||||
return 'calculated';
|
||||
}
|
||||
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// First get the datasets and default options
|
||||
if(false === parent::get_question_options($question)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$options = get_record('quiz_calculated', 'question', $question->id)) {
|
||||
notify("No options were found for calculated question
|
||||
#{$question->id}! Proceeding with defaults.");
|
||||
$options = new stdClass;
|
||||
$options->tolerance = 0.01;
|
||||
$options->tolerancetype = 1; // relative
|
||||
$options->correctanswerlength = 2;
|
||||
$options->correctanswerformat = 1; // decimals
|
||||
}
|
||||
$question->options->tolerance = $options->tolerance;
|
||||
$question->options->tolerancetype = $options->tolerancetype;
|
||||
$question->options->correctanswerlength = $options->correctanswerlength;
|
||||
$question->options->correctanswerformat = $options->correctanswerformat;
|
||||
|
||||
// For historic reasons we also need these fields in the answer objects.
|
||||
// This should eventually be removed and related code changed to use
|
||||
// the values in $question->options instead.
|
||||
foreach ($question->options->answers as $key => $answer) {
|
||||
$answer = &$question->options->answers[$key]; // for PHP 4.x
|
||||
$answer->calcid = $options->id;
|
||||
$answer->tolerance = $options->tolerance;
|
||||
$answer->tolerancetype = $options->tolerancetype;
|
||||
$answer->correctanswerlength = $options->correctanswerlength;
|
||||
$answer->correctanswerformat = $options->correctanswerformat;
|
||||
}
|
||||
|
||||
$this->get_numerical_units($question);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_numerical_units(&$question) {
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
return $virtualqtype->get_numerical_units($question);
|
||||
}
|
||||
|
||||
function save_question_options($question, $options) {
|
||||
// Get old answers:
|
||||
$oldanswers = array();
|
||||
if ($this->get_question_options($question)) {
|
||||
$oldanswers = $question->options->answers;
|
||||
}
|
||||
|
||||
// Update with new answers
|
||||
$answerrec->question = $calcrec->question = $question->id;
|
||||
foreach ($options->answers as $newanswer) {
|
||||
$answerrec->answer = $newanswer->answer;
|
||||
$answerrec->fraction = $newanswer->fraction;
|
||||
$answerrec->feedback = $newanswer->feedback;
|
||||
$calcrec->tolerance = $newanswer->tolerance;
|
||||
$calcrec->tolerancetype = $newanswer->tolerancetype;
|
||||
$calcrec->correctanswerlength = $newanswer->correctanswerlength;
|
||||
$calcrec->correctanswerformat = $newanswer->correctanswerformat;
|
||||
if ($oldanswer = array_shift($oldanswers)) {
|
||||
// Reuse old record:
|
||||
$calcrec->answer = $answerrec->id = $oldanswer->id;
|
||||
$calcrec->id = $oldanswer->calcid;
|
||||
if (!update_record('quiz_answers', $answerrec)) {
|
||||
error("Unable to update answer for calculated question #{$question->id}!");
|
||||
} else {
|
||||
// notify("Answer updated successfully for calculated question $question->name");
|
||||
}
|
||||
if (!update_record('quiz_calculated', $calcrec)) {
|
||||
error("Unable to update options for calculated question #{$question->id}!");
|
||||
} else {
|
||||
// notify("Options updated successfully for calculated question $question->name");
|
||||
}
|
||||
} else {
|
||||
unset($answerrec->id);
|
||||
unset($calcrec->id);
|
||||
if (!($calcrec->answer = insert_record('quiz_answers',
|
||||
$answerrec))) {
|
||||
error("Unable to insert answer for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Answer inserted successfully for calculated question $question->name");
|
||||
}
|
||||
if (!insert_record('quiz_calculated', $calcrec)) {
|
||||
error("Unable to insert options calculared question $question->name");
|
||||
} else {
|
||||
// notify("Options inserted successfully for calculated question $question->name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete excessive records:
|
||||
foreach ($oldanswers as $oldanswer) {
|
||||
if (!delete_records('quiz_answers', 'id', $oldanswer->id)) {
|
||||
error("Unable to delete old answers for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Old answers deleted successfully for calculated question $question->name");
|
||||
}
|
||||
if (!delete_records('quiz_calculated', 'id', $oldanswer->calcid)) {
|
||||
error("Unable to delete old options for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Old options deleted successfully for calculated question $question->name");
|
||||
}
|
||||
}
|
||||
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$virtualqtype->save_numerical_units($question);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
// Substitute variables in questiontext before giving the data to the
|
||||
// virtual type for printing
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$unit = $virtualqtype->get_default_numerical_unit($question);
|
||||
foreach ($question->options->answers as $key => $answer) {
|
||||
$answer = &$question->options->answers[$key]; // for PHP 4.x
|
||||
$answer->answer = $this->substitute_variables($answer->answer,
|
||||
$state->options->dataset);
|
||||
// apply_unit
|
||||
}
|
||||
$question->questiontext = parent::substitute_variables(
|
||||
$question->questiontext, $state->options->dataset);
|
||||
$virtualqtype->print_question_formulation_and_controls($question,
|
||||
$state, $quiz, $options);
|
||||
}
|
||||
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
// Forward the grading to the virtual qtype
|
||||
foreach ($question->options->answers as $key => $answer) {
|
||||
$answer = &$question->options->answers[$key]; // for PHP 4.x
|
||||
$answer->answer = $this->substitute_variables($answer->answer,
|
||||
$state->options->dataset);
|
||||
}
|
||||
return parent::grade_responses($question, $state, $quiz);
|
||||
}
|
||||
|
||||
function create_virtual_qtype() {
|
||||
require('modifiednumericalqtype.php');
|
||||
return new quiz_calculated_qtype_numerical_helper();
|
||||
global $CFG;
|
||||
require_once("$CFG->dirroot/mod/quiz/questiontypes/numerical/questiontype.php");
|
||||
return new quiz_numerical_qtype();
|
||||
}
|
||||
|
||||
function supports_dataset_item_generation() {
|
||||
@@ -116,7 +246,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
|
||||
// Reformat according to the precision $regs[4]:
|
||||
|
||||
// Determine the format 0.[1-9][0-9]* for the nbr...
|
||||
// Determine the format 0.[1-9][0-9]* for the nbr...
|
||||
$p10 = 0;
|
||||
while ($nbr < 1) {
|
||||
--$p10;
|
||||
@@ -157,7 +287,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
}
|
||||
}
|
||||
|
||||
// The larger of the values decide the sign in case the
|
||||
// The larger of the values decide the sign in case the
|
||||
// have equal different signs (which they really must not have)
|
||||
if ($regs[2] + $regs[3] > 0) {
|
||||
return $nbr;
|
||||
@@ -172,7 +302,8 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
}
|
||||
|
||||
function comment_header($question) {
|
||||
$answers = $this->get_answers($question);
|
||||
$this->get_question_options($question);
|
||||
$answers = $question->options->answers;
|
||||
$strheader = '';
|
||||
$delimiter = '';
|
||||
foreach ($answers as $answer) {
|
||||
@@ -193,17 +324,25 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
}
|
||||
|
||||
// Get answers
|
||||
$answers = $this->get_answers($question);
|
||||
$answers = $question->options->answers;
|
||||
$stranswers = get_string('answer', 'quiz');
|
||||
$strmin = get_string('min', 'quiz');
|
||||
$strmax = get_string('max', 'quiz');
|
||||
$errors = '';
|
||||
$delimiter = ': ';
|
||||
$state = new stdClass;
|
||||
$state->responses = array();
|
||||
$state->options = new stdClass;
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
foreach ($answers as $answer) {
|
||||
$calculated = quiz_qtype_calculated_calculate_answer(
|
||||
$answer->answer, $data, $answer->tolerance,
|
||||
$answer->tolerancetype, $answer->correctanswerlength,
|
||||
$answer->correctanswerformat, $unit);
|
||||
$state->responses[''] = $calculated->answer;
|
||||
$virtualqtype->get_tolerance_interval($question, $state);
|
||||
$calculated->min = $state->options->min;
|
||||
$calculated->max = $state->options->max;
|
||||
if ($calculated->min === '') {
|
||||
// This should mean that something is wrong
|
||||
$errors .= " -$calculated->answer";
|
||||
@@ -213,6 +352,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
}
|
||||
$strmin .= $delimiter.$calculated->min;
|
||||
$strmax .= $delimiter.$calculated->max;
|
||||
|
||||
$delimiter = ', ';
|
||||
}
|
||||
return "$stranswers<br/>$strmin<br/>$strmax<br/>$errors";
|
||||
@@ -224,104 +364,6 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
'3' => get_string('geometric', 'quiz'));
|
||||
}
|
||||
|
||||
function save_question_options($question, $options) {
|
||||
// Get old answers:
|
||||
$oldanswers = $this->get_answers($question)
|
||||
or $oldanswers = array(); // if there are none
|
||||
|
||||
// Update with new answers
|
||||
$answerrec->question = $calcrec->question = $question->id;
|
||||
foreach ($options->answers as $newanswer) {
|
||||
$answerrec->answer = $newanswer->answer;
|
||||
$answerrec->fraction = $newanswer->fraction;
|
||||
$answerrec->feedback = $newanswer->feedback;
|
||||
$calcrec->tolerance = $newanswer->tolerance;
|
||||
$calcrec->tolerancetype = $newanswer->tolerancetype;
|
||||
$calcrec->correctanswerlength = $newanswer->correctanswerlength;
|
||||
$calcrec->correctanswerformat = $newanswer->correctanswerformat;
|
||||
if ($oldanswer = array_shift($oldanswers)) {
|
||||
// Reuse old record:
|
||||
$calcrec->answer = $answerrec->id = $oldanswer->id;
|
||||
$calcrec->id = $oldanswer->calcid;
|
||||
if (!update_record('quiz_answers', $answerrec)) {
|
||||
error("Unable to update answer for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Answer updated successfully for calculated question $question->name");
|
||||
}
|
||||
if (!update_record('quiz_calculated', $calcrec)) {
|
||||
error("Unable to update options calculared question $question->name");
|
||||
} else {
|
||||
// notify("Options updated successfully for calculated question $question->name");
|
||||
}
|
||||
} else {
|
||||
unset($answerrec->id);
|
||||
unset($calcrec->id);
|
||||
if (!($calcrec->answer = insert_record('quiz_answers',
|
||||
$answerrec))) {
|
||||
error("Unable to insert answer for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Answer inserted successfully for calculated question $question->name");
|
||||
}
|
||||
if (!insert_record('quiz_calculated', $calcrec)) {
|
||||
error("Unable to insert options calculared question $question->name");
|
||||
} else {
|
||||
// notify("Options inserted successfully for calculated question $question->name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete excessive records:
|
||||
foreach ($oldanswers as $oldanswer) {
|
||||
if (!delete_records('quiz_answers', 'id', $oldanswer->id)) {
|
||||
error("Unable to delete old answers for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Old answers deleted successfully for calculated question $question->name");
|
||||
}
|
||||
if (!delete_records('quiz_calculated', 'id', $oldanswer->calcid)) {
|
||||
error("Unable to delete old options for calculated question $question->name");
|
||||
} else {
|
||||
// notify("Old options deleted successfully for calculated question $question->name");
|
||||
}
|
||||
}
|
||||
|
||||
// Get old units (just like for numerical questions)
|
||||
$oldunits = get_records('quiz_numerical_units',
|
||||
'question', $question->id)
|
||||
or $oldunits = array(); // if there are none
|
||||
if (1 == count($options->units) && !$options->units[0]->unit) {
|
||||
/// Only default unit and it is empty, so drop it:
|
||||
$options->units = array();
|
||||
}
|
||||
foreach ($options->units as $newunit) {
|
||||
$newunit->question = $question->id;
|
||||
if ($oldunit = array_shift($oldunits)) {
|
||||
$newunit->id = $oldunit->id;
|
||||
if (!update_record('quiz_numerical_units', $newunit)) {
|
||||
error("Unable to update unit $newunit->unit for $question->name");
|
||||
} else {
|
||||
// notify("Unit $newunit->unit was updated successfully for $question->name");
|
||||
}
|
||||
} else {
|
||||
if (!insert_record('quiz_numerical_units', $newunit)) {
|
||||
error("Unable to insert unit $newunit->unit for $question->name");
|
||||
} else {
|
||||
// notify("Unit $newunit->unit was inserted successfully for question $question->name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete excessive unit records
|
||||
foreach ($oldunits as $oldunit) {
|
||||
if (!delete_records('quiz_numerical_units', 'id', $oldunit->id)) {
|
||||
error("Unable to delete old unit $oldunit->unit for question $question->name");
|
||||
} else {
|
||||
notify("Deleted old unit $oldunit->unit successfully for question $question->name");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function dataset_options($question, $name, $renameabledatasets=false) {
|
||||
// Takes datasets from the parent implementation but
|
||||
// filters options that are currently not accepted by calculated
|
||||
@@ -361,48 +403,34 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
|
||||
return $datasetmenus;
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
/// Determines the answers and then lets the
|
||||
/// NUMERICAL question type take care of the
|
||||
/// grading...
|
||||
|
||||
list($datasetnumber, $individualdata) =
|
||||
$this->parse_datasetinput($question->response[$nameprefix]);
|
||||
|
||||
// find the raw answer material
|
||||
global $CFG;
|
||||
if (!($answers = $this->get_answers($question))) {
|
||||
notify("Error no answers found for question $question->id");
|
||||
}
|
||||
|
||||
/// Find a default unit:
|
||||
if ($unit = get_record('quiz_numerical_units',
|
||||
'question', $question->id, 'multiplier', 1.0)) {
|
||||
$unit = $unit->unit;
|
||||
} else {
|
||||
$unit = '';
|
||||
}
|
||||
|
||||
// Construct answers for the numerical question type
|
||||
foreach ($answers as $aid => $answer) {
|
||||
$answernumerical = quiz_qtype_calculated_calculate_answer(
|
||||
$answer->answer, $individualdata,
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
foreach ($question->options->answers as $key => $answer) {
|
||||
if (((float) $answer->fraction) === 1.0) {
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$unit = $virtualqtype->get_default_numerical_unit($question);
|
||||
$answernumerical = quiz_qtype_calculated_calculate_answer(
|
||||
$answer->answer, $state->options->dataset,
|
||||
$answer->tolerance, $answer->tolerancetype,
|
||||
$answer->correctanswerlength,
|
||||
$answer->correctanswerformat, $unit);
|
||||
$answers[$aid]->answer = $answernumerical->answer;
|
||||
$answers[$aid]->min = $answernumerical->min;
|
||||
$answers[$aid]->max = $answernumerical->max;
|
||||
return array('' => $answernumerical->answer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Forward the grading to the virtual qtype
|
||||
$virtualnameprefix = $this->create_virtual_nameprefix(
|
||||
$nameprefix, $question->response[$nameprefix]);
|
||||
unset($question->response[$nameprefix]);
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$virtualqtype->set_answers($answers);
|
||||
return $virtualqtype->grade_response($question,
|
||||
$virtualnameprefix);
|
||||
function substitute_variables($str, $dataset) {
|
||||
$formula = parent::substitute_variables($str, $dataset);
|
||||
if ($error = quiz_qtype_calculated_find_formula_errors($formula)) {
|
||||
return $error;
|
||||
}
|
||||
/// Calculate the correct answer
|
||||
eval('$str = '.$formula.';');
|
||||
return $str;
|
||||
}
|
||||
|
||||
function print_question_grading_details(&$question, &$state, $quiz, $options) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
@@ -418,57 +446,10 @@ function quiz_qtype_calculated_calculate_answer($formula, $individualdata,
|
||||
/// ->answer the correct answer
|
||||
/// ->min the lower bound for an acceptable response
|
||||
/// ->max the upper bound for an accetpable response
|
||||
|
||||
|
||||
/// Exchange formula variables with the correct values...
|
||||
foreach ($individualdata as $name => $value) {
|
||||
$formula = str_replace('{'.$name.'}', "($value)", $formula);
|
||||
}
|
||||
if (ereg('\\{([^}]*)\\}', $formula, $regs)) {
|
||||
// This is the normal case for a recently added question.
|
||||
// Return a notification about it
|
||||
$calculated->answer = $calculated->min = $calculated->max = '';
|
||||
return $calculated;
|
||||
|
||||
} else if ($error = quiz_qtype_calculated_find_formula_errors($formula)) {
|
||||
$calculated->answer = $error;
|
||||
$calculated->min = $calculated->max = '';
|
||||
return $calculated;
|
||||
}
|
||||
|
||||
/// Calculate the correct answer
|
||||
eval('$answer = '.$formula.';');
|
||||
|
||||
/// Calculate min and max
|
||||
switch ($tolerancetype) {
|
||||
case '1': case 'relative':
|
||||
/// Recalculate the tolerance and fall through
|
||||
/// to the nominal case:
|
||||
$tolerance = $answer * $tolerance;
|
||||
|
||||
// Falls through to the nominal case -
|
||||
case '2': case 'nominal':
|
||||
$tolerance = abs($tolerance); // important
|
||||
$max = $answer + $tolerance;
|
||||
$min = $answer - $tolerance;
|
||||
break;
|
||||
|
||||
case '3': case 'geometric':
|
||||
$quotient = 1 + abs($tolerance);
|
||||
if ($answer >= 0) {
|
||||
$max = $answer * $quotient;
|
||||
$min = $answer / $quotient;
|
||||
} else {
|
||||
$min = $answer * $quotient;
|
||||
$max = $answer / $quotient;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Unknown tolerance type $tolerancetype");
|
||||
}
|
||||
$calculated->min = $min;
|
||||
$calculated->max = $max;
|
||||
|
||||
global $QUIZ_QTYPES;
|
||||
$answer = $QUIZ_QTYPES[CALCULATED]->substitute_variables($formula, $individualdata);
|
||||
if ('1' == $answerformat) { /* Answer is to have $answerlength decimals */
|
||||
/*** Adjust to the correct number of decimals ***/
|
||||
|
||||
@@ -476,7 +457,7 @@ function quiz_qtype_calculated_calculate_answer($formula, $individualdata,
|
||||
|
||||
if ($answerlength) {
|
||||
/* Try to include missing zeros at the end */
|
||||
|
||||
|
||||
if (ereg('^(.*\\.)(.*)$', $calculated->answer, $regs)) {
|
||||
$calculated->answer = $regs[1] . substr(
|
||||
$regs[2] . '00000000000000000000000000000000000000000x',
|
||||
@@ -502,7 +483,7 @@ function quiz_qtype_calculated_calculate_answer($formula, $individualdata,
|
||||
$sign = '';
|
||||
}
|
||||
|
||||
// Determine the format 0.[1-9][0-9]* for the answer...
|
||||
// Determine the format 0.[1-9][0-9]* for the answer...
|
||||
$p10 = 0;
|
||||
while ($answer < 1) {
|
||||
--$p10;
|
||||
@@ -547,7 +528,7 @@ function quiz_qtype_calculated_calculate_answer($formula, $individualdata,
|
||||
} else {
|
||||
$calculated->answer = 0.0;
|
||||
}
|
||||
|
||||
|
||||
/// Return the result
|
||||
return $calculated;
|
||||
}
|
||||
@@ -566,7 +547,7 @@ function quiz_qtype_calculated_find_formula_errors($formula) {
|
||||
// Strip away empty space and lowercase it
|
||||
$formula = strtolower(str_replace(' ', '', $formula));
|
||||
|
||||
$safeoperatorchar = '-+/*%>:^~<?=&|!';
|
||||
$safeoperatorchar = '-+/*%>:^~<?=&|!'; /* */
|
||||
$operatorornumber = "[$safeoperatorchar.0-9eE]";
|
||||
|
||||
|
||||
@@ -580,7 +561,7 @@ function quiz_qtype_calculated_find_formula_errors($formula) {
|
||||
return get_string('illegalformulasyntax', 'quiz', $regs[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// Zero argument functions
|
||||
case 'pi':
|
||||
if ($regs[3]) {
|
||||
@@ -607,7 +588,7 @@ function quiz_qtype_calculated_find_formula_errors($formula) {
|
||||
return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// Functions that must have two arguments
|
||||
case 'atan2': case 'fmod': case 'pow':
|
||||
if ($regs[5] || empty($regs[4])) {
|
||||
@@ -636,7 +617,7 @@ function quiz_qtype_calculated_find_formula_errors($formula) {
|
||||
$formula = ereg_replace("^$regs[2]\\([^)]*\\)", '1', $formula);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ereg("[^$safeoperatorchar.0-9eE]+", $formula, $regs)) {
|
||||
return get_string('illegalformulasyntax', 'quiz', $regs[0]);
|
||||
} else {
|
||||
|
||||
@@ -12,16 +12,115 @@ define("LINK", "3");
|
||||
|
||||
class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
|
||||
var $virtualqtype= false;
|
||||
|
||||
// Contains picked dataset numbers by category. The idea is to
|
||||
// reuse dataset number for each category within a quiz.
|
||||
var $datasetnumbers = array();
|
||||
var $virtualqtype = false;
|
||||
|
||||
function name() {
|
||||
return 'datasetdependent';
|
||||
}
|
||||
|
||||
function save_question_options($question, $options) {
|
||||
// Default does nothing...
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
// Find out how many datasets are available
|
||||
global $CFG;
|
||||
if(!$maxnumber = (int)get_field_sql(
|
||||
"SELECT MAX(a.itemcount)
|
||||
FROM {$CFG->prefix}quiz_dataset_definitions a,
|
||||
{$CFG->prefix}quiz_question_datasets b
|
||||
WHERE b.question = $question->id
|
||||
AND a.id = b.datasetdefinition")) {
|
||||
error("Couldn't get the specified dataset for a calculated " .
|
||||
"question! (question: {$question->id}, " .
|
||||
"datasetnumber: {$datasetnumber})");
|
||||
}
|
||||
|
||||
// Choose a random dataset
|
||||
$state->options->datasetnumber = rand(1, $maxnumber);
|
||||
$state->options->dataset =
|
||||
$this->pick_question_dataset($question,$state->options->datasetnumber);
|
||||
$state->responses = array('' => '');
|
||||
return true;
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
if (!ereg('^dataset([0-9]+)[^-]*-(.*)$',
|
||||
$state->responses[''], $regs)) {
|
||||
notify ("Wrongly formatted raw response answer " .
|
||||
"{$state->responses['']}! Could not restore session for " .
|
||||
" question #{$question->id}.");
|
||||
$state->options->datasetnumber = 1;
|
||||
$state->options->dataset = array();
|
||||
$state->responses = array('' => '');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore the chosen dataset
|
||||
$state->options->datasetnumber = $regs[1];
|
||||
$state->options->dataset =
|
||||
$this->pick_question_dataset($question,$state->options->datasetnumber);
|
||||
$state->responses = array('' => $regs[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
$responses = 'dataset'.$state->options->datasetnumber.'-'.
|
||||
$state->responses[''];
|
||||
|
||||
// Set the legacy answer field
|
||||
if (!set_field('quiz_states', 'answer', $responses, 'id',
|
||||
$state->id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
// Substitute variables in questiontext before giving the data to the
|
||||
// virtual type for printing
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$unit = $virtualqtype->get_default_numerical_unit($question);
|
||||
foreach ($question->options->answers as $answer) {
|
||||
$answer->answer = $this->substitute_variables($answer->answer,
|
||||
$state->options->dataset);
|
||||
}
|
||||
$question->questiontext = $this->substitute_variables(
|
||||
$question->questiontext, $state->options->dataset);
|
||||
$virtualqtype->print_question_formulation_and_controls($question,
|
||||
$state, $quiz, $options);
|
||||
}
|
||||
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
foreach ($question->options->answers as $answer) {
|
||||
$answer->answer = $this->substitute_variables(
|
||||
$answer->answer, $state->options->dataset);
|
||||
if (((float) $answer->fraction) === 1.0) {
|
||||
return array('' => $answer->answer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
// Forward the grading to the virtual qtype
|
||||
foreach ($question->options->answers as $answer) {
|
||||
$answer->answer = $this->substitute_variables($answer->answer,
|
||||
$state->options->dataset);
|
||||
}
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
return $virtualqtype->grade_responses($question, $state, $quiz) ;
|
||||
}
|
||||
|
||||
function substitute_variables($str, $dataset) {
|
||||
foreach ($dataset as $name => $value) {
|
||||
$str = str_replace('{'.$name.'}', $value, $str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
function uses_quizfile($question, $relativefilepath) {
|
||||
// Check whether the specified file is available by any
|
||||
// dataset item on this question...
|
||||
@@ -41,7 +140,165 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
return parent::uses_quizfile($question, $relativefilepath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function finished_edit_wizard(&$form) {
|
||||
return isset($form->backtoquiz);
|
||||
}
|
||||
|
||||
function save_question($question, $form, $course, $subtypeoptions=false) {
|
||||
// For dataset dependent questions a wizard is used for editing
|
||||
// questions. Therefore calls from question.php are ignored.
|
||||
// Instead questions are saved when this method is called by
|
||||
// editquestion.php
|
||||
|
||||
if ($subtypeoptions) {
|
||||
// Let's save the question
|
||||
// We need to save the question first (in order to get the id)
|
||||
// We then save the dataset definitions and finally we
|
||||
// save the subtype options...
|
||||
|
||||
// Save question
|
||||
if (!empty($question->id)) { // Question already exists
|
||||
$question->version++; // Update version number of question
|
||||
if (!update_record("quiz_questions", $question)) {
|
||||
error("Could not update question!");
|
||||
}
|
||||
} else { // Question is a new one
|
||||
// Set the unique code (not to be changed)
|
||||
$question->stamp = make_unique_id_code();
|
||||
$question->version = 1;
|
||||
if (!$question->id=insert_record("quiz_questions", $question)) {
|
||||
error("Could not insert new question!");
|
||||
}
|
||||
}
|
||||
|
||||
// Save datasets
|
||||
global $CFG;
|
||||
$datasetdefinitions = get_records_sql( // Indexed by name...
|
||||
"SELECT a.name, a.id, a.type, a.category
|
||||
FROM {$CFG->prefix}quiz_dataset_definitions a,
|
||||
{$CFG->prefix}quiz_question_datasets b
|
||||
WHERE a.id = b.datasetdefinition
|
||||
AND b.question = $question->id");
|
||||
|
||||
foreach ($form->dataset as $dataset) {
|
||||
if (!$dataset) {
|
||||
continue; // The no dataset case...
|
||||
}
|
||||
|
||||
list($type, $category, $name) = explode('-', $dataset, 3);
|
||||
|
||||
if (isset($datasetdefinitions[$name])
|
||||
and $datasetdefinitions[$name]->type == $type
|
||||
and $datasetdefinitions[$name]->category == $category) {
|
||||
// Keep this dataset as it already fulfills our dreams
|
||||
// by preventing it from being deleted
|
||||
unset($datasetdefinitions[$name]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to create a new datasetdefinition
|
||||
unset ($datasetdef);
|
||||
$datasetdef->type = $type;
|
||||
$datasetdef->name = $name;
|
||||
$datasetdef->category = $category;
|
||||
|
||||
if (!$datasetdef->id = insert_record(
|
||||
'quiz_dataset_definitions', $datasetdef)) {
|
||||
error("Unable to create dataset $name");
|
||||
}
|
||||
|
||||
if ($category) {
|
||||
// We need to look for already existing
|
||||
// datasets in the category.
|
||||
// By first creating the datasetdefinition above we
|
||||
// can manage to automatically take care of
|
||||
// some possible realtime concurrence
|
||||
while ($olderdatasetdef = get_record_select(
|
||||
'quiz_dataset_definitions',
|
||||
" type = '$type' AND name = '$name'
|
||||
AND category = '$category'
|
||||
AND id < $datasetdef->id ")) {
|
||||
// Use older dataset instead:
|
||||
delete_records('quiz_dataset_definitions',
|
||||
'id', $datasetdef->id);
|
||||
$datasetdef = $olderdatasetdef;
|
||||
}
|
||||
}
|
||||
|
||||
// Create relation to this dataset:
|
||||
unset($questiondataset);
|
||||
$questiondataset->question = $question->id;
|
||||
$questiondataset->datasetdefinition = $datasetdef->id;
|
||||
if (!insert_record('quiz_question_datasets',
|
||||
$questiondataset)) {
|
||||
error("Unable to create relation to dataset $name");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove local obsolete datasets as well as relations
|
||||
// to datasets in other categories:
|
||||
if (!empty($datasetdefinitions)) {
|
||||
foreach ($datasetdefinitions as $def) {
|
||||
delete_records('quiz_question_datasets',
|
||||
'question', $question->id,
|
||||
'datasetdefinition', $def->id);
|
||||
|
||||
if ($def->category == 0) { // Question local dataset
|
||||
delete_records('quiz_dataset_definitions', 'id', $def->id);
|
||||
delete_records('quiz_dataset_items',
|
||||
'definition', $def->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save subtype options
|
||||
$this->save_question_options($question, $subtypeoptions);
|
||||
return $question;
|
||||
|
||||
} else if (empty($form->editdatasets)) {
|
||||
// Parse for common question entries and
|
||||
// continue with editquestion.php by returning the question
|
||||
$question->name = $form->name;
|
||||
$question->questiontext = $form->questiontext;
|
||||
$question->questiontextformat = $form->questiontextformat;
|
||||
if (empty($form->image)) {
|
||||
$question->image = "";
|
||||
} else {
|
||||
$question->image = $form->image;
|
||||
}
|
||||
if (isset($form->defaultgrade)) {
|
||||
$question->defaultgrade = $form->defaultgrade;
|
||||
}
|
||||
return $question;
|
||||
} else {
|
||||
return $question;
|
||||
}
|
||||
}
|
||||
|
||||
/// Dataset functionality
|
||||
function pick_question_dataset($question, $datasetnumber) {
|
||||
// Select a dataset in the following format:
|
||||
// An array indexed by the variable names (d.name) pointing to the value
|
||||
// to be substituted
|
||||
global $CFG;
|
||||
if (!$dataset = get_records_sql(
|
||||
"SELECT d.name, i.value
|
||||
FROM {$CFG->prefix}quiz_dataset_definitions d,
|
||||
{$CFG->prefix}quiz_dataset_items i,
|
||||
{$CFG->prefix}quiz_question_datasets q
|
||||
WHERE q.question = $question->id
|
||||
AND q.datasetdefinition = d.id
|
||||
AND d.id = i.definition
|
||||
AND i.number = $datasetnumber")) {
|
||||
error("Couldn't get the specified dataset for a dataset dependent " .
|
||||
"question! (question: {$question->id}, " .
|
||||
"datasetnumber: {$datasetnumber})");
|
||||
}
|
||||
array_walk($dataset, create_function('&$val', '$val = $val->value;'));
|
||||
return $dataset;
|
||||
}
|
||||
|
||||
function create_virtual_qtype() {
|
||||
error("No vitrual question type for question type ".$this->name());
|
||||
}
|
||||
@@ -92,10 +349,10 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
}
|
||||
|
||||
function dataset_options($question, $name) {
|
||||
|
||||
|
||||
// First options - it is not a dataset...
|
||||
$options['0'] = get_string('nodataset', 'quiz');
|
||||
|
||||
|
||||
// Construct question local options
|
||||
global $CFG;
|
||||
$currentdatasetdef = get_record_sql(
|
||||
@@ -105,7 +362,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
WHERE a.id = b.datasetdefinition
|
||||
AND b.question = '$question->id'
|
||||
AND a.name = '$name'")
|
||||
or $currentdatasetdef->type = '0';
|
||||
or $currentdatasetdef->type = '0';
|
||||
foreach (array( LITERAL, FILE, LINK) as $type) {
|
||||
$key = "$type-0-$name";
|
||||
if ($currentdatasetdef->type == $type
|
||||
@@ -115,7 +372,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
$options[$key] = get_string("newlocal$type", 'quiz');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Construct question category options
|
||||
$categorydatasetdefs = get_records_sql(
|
||||
"SELECT a.type, a.id
|
||||
@@ -145,142 +402,6 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
: '');
|
||||
}
|
||||
|
||||
function save_question_options($question, $options) {
|
||||
// Default does nothing...
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question($question, $form, $course, $subtypeoptions=false) {
|
||||
// For dataset dependent questions a wizard is used for editing
|
||||
// questions. Therefore calls from question.php are ignored.
|
||||
// Instead questions are saved when this method is called by
|
||||
// editquestion.php
|
||||
|
||||
if ($subtypeoptions) {
|
||||
// Let's save the question
|
||||
// We need to save the question first (in order to get the id)
|
||||
// We then save the dataset definitions and finally we
|
||||
// save the subtype options...
|
||||
|
||||
// Save question
|
||||
if (!empty($question->id)) { // Question already exists
|
||||
$question->version++; // Update version number of question
|
||||
if (!update_record("quiz_questions", $question)) {
|
||||
error("Could not update question!");
|
||||
}
|
||||
} else { // Question is a new one
|
||||
// Set the unique code (not to be changed)
|
||||
$question->stamp = make_unique_id_code();
|
||||
$question->version = 1;
|
||||
if (!$question->id=insert_record("quiz_questions", $question)) {
|
||||
error("Could not insert new question!");
|
||||
}
|
||||
}
|
||||
|
||||
// Save datasets
|
||||
global $CFG;
|
||||
$datasetdefinitions = get_records_sql( // Indexed by name...
|
||||
"SELECT a.name, a.id, a.type, a.category
|
||||
FROM {$CFG->prefix}quiz_dataset_definitions a,
|
||||
{$CFG->prefix}quiz_question_datasets b
|
||||
WHERE a.id = b.datasetdefinition
|
||||
AND b.question = $question->id");
|
||||
|
||||
foreach ($form->dataset as $dataset) {
|
||||
if (!$dataset) {
|
||||
continue; // The no dataset case...
|
||||
}
|
||||
|
||||
list($type, $category, $name) = explode('-', $dataset, 3);
|
||||
|
||||
if (isset($datasetdefinitions[$name])
|
||||
and $datasetdefinitions[$name]->type == $type
|
||||
and $datasetdefinitions[$name]->category == $category) {
|
||||
// Keep this dataset as it already fulfills our dreams
|
||||
// by preventing it from being deleted
|
||||
unset($datasetdefinitions[$name]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to create a new datasetdefinition
|
||||
unset ($datasetdef);
|
||||
$datasetdef->type = $type;
|
||||
$datasetdef->name = $name;
|
||||
$datasetdef->category = $category;
|
||||
|
||||
if (!$datasetdef->id = insert_record(
|
||||
'quiz_dataset_definitions', $datasetdef)) {
|
||||
error("Unable to create dataset $name");
|
||||
}
|
||||
|
||||
if ($category) {
|
||||
// We need to look for already existing
|
||||
// datasets in the category.
|
||||
// By first creating the datasetdefinition above we
|
||||
// can manage to automatically take care of
|
||||
// some possible realtime concurrence
|
||||
while ($olderdatasetdef = get_record_select(
|
||||
'quiz_dataset_definitions',
|
||||
" type = '$type' AND name = '$name'
|
||||
AND category = '$category'
|
||||
AND id < $datasetdef->id ")) {
|
||||
// Use older dataset instead:
|
||||
delete_records('quiz_dataset_definitions',
|
||||
'id', $datasetdef->id);
|
||||
$datasetdef = $olderdatasetdef;
|
||||
}
|
||||
}
|
||||
|
||||
// Create relation to this dataset:
|
||||
unset($questiondataset);
|
||||
$questiondataset->question = $question->id;
|
||||
$questiondataset->datasetdefinition = $datasetdef->id;
|
||||
if (!insert_record('quiz_question_datasets',
|
||||
$questiondataset)) {
|
||||
error("Unable to create relation to dataset $name");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove local obsolete datasets as well as relations
|
||||
// to datasets in other categories:
|
||||
if (!empty($datasetdefinitions)) {
|
||||
foreach ($datasetdefinitions as $def) {
|
||||
delete_records('quiz_question_datasets',
|
||||
'question', $question->id,
|
||||
'datasetdefinition', $def->id);
|
||||
|
||||
if ($def->category == 0) { // Question local dataset
|
||||
delete_records('quiz_dataset_definitions', 'id', $def->id);
|
||||
delete_records('quiz_dataset_items',
|
||||
'definition', $def->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save subtype options
|
||||
$this->save_question_options($question, $subtypeoptions);
|
||||
return $question;
|
||||
|
||||
} else if (empty($form->editdatasets)) {
|
||||
// Parse for common question entries and
|
||||
// continue with editquestion.php by returning the question
|
||||
$question->name = $form->name;
|
||||
$question->questiontext = $form->questiontext;
|
||||
$question->questiontextformat = $form->questiontextformat;
|
||||
if (empty($form->image)) {
|
||||
$question->image = "";
|
||||
} else {
|
||||
$question->image = $form->image;
|
||||
}
|
||||
if (isset($form->defaultgrade)) {
|
||||
$question->defaultgrade = $form->defaultgrade;
|
||||
}
|
||||
return $question;
|
||||
} else {
|
||||
return $question;
|
||||
}
|
||||
}
|
||||
|
||||
function find_dataset_names($text) {
|
||||
/// Returns the possible dataset names found in the text as an array
|
||||
/// The array has the dataset name for both key and value
|
||||
@@ -292,114 +413,8 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
return $datasetnames;
|
||||
}
|
||||
|
||||
function convert_to_response_answer_field($questionresponse) {
|
||||
// It does not look like all platforms support the ksort strategi
|
||||
// so gotta try something else...
|
||||
foreach ($questionresponse as $key => $response) {
|
||||
if (!isset($shortestkey)
|
||||
|| strlen($shortestkey) > strlen($key)) {
|
||||
$shortestkey = $key;
|
||||
}
|
||||
}
|
||||
$dataset = $questionresponse[$shortestkey];
|
||||
unset($questionresponse[$shortestkey]);
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
return "dataset$dataset-" . $virtualqtype
|
||||
->convert_to_response_answer_field($questionresponse);
|
||||
}
|
||||
|
||||
function create_response($question, $nameprefix, $questionsinuse) {
|
||||
/// This method must pick a dataset and have its number and
|
||||
/// data injected in the response keys
|
||||
|
||||
|
||||
// First we retrieve the dataset definitions for this questions
|
||||
// and check how many datasets we have available ($maxnumber)
|
||||
global $CFG;
|
||||
$datasetdefinitions = get_records_sql(
|
||||
"SELECT a.* FROM {$CFG->prefix}quiz_dataset_definitions a,
|
||||
{$CFG->prefix}quiz_question_datasets b
|
||||
WHERE a.id = b.datasetdefinition
|
||||
AND b.question = $question->id");
|
||||
$definitionids = $delimiter = '';
|
||||
foreach ($datasetdefinitions as $datasetdef) {
|
||||
$definitionids .= $delimiter.$datasetdef->id;
|
||||
$delimiter = ',';
|
||||
if (!isset($maxnumber) || $datasetdef->itemcount < $maxnumber) {
|
||||
$maxnumber = $datasetdef->itemcount;
|
||||
}
|
||||
}
|
||||
|
||||
// We then pick dataset number and retrieve the datasetitems
|
||||
if (!isset($maxnumber) || 0 == $maxnumber) {
|
||||
notify("Error: Question $question->id does not
|
||||
have items for its datasets");
|
||||
$datasetinput = 0;
|
||||
$datasetitems = array();
|
||||
|
||||
} else {
|
||||
isset($this->datasetnumbers[$question->category])
|
||||
and $this->datasetnumbers[$question->category] <= $maxnumber
|
||||
or $this->datasetnumbers[$question->category] =
|
||||
quiz_qtype_dataset_pick_new($question->category,
|
||||
$maxnumber);
|
||||
$datasetinput = $this->datasetnumbers[$question->category];
|
||||
$datasetitems = get_records_select('quiz_dataset_items',
|
||||
"definition in ($definitionids)
|
||||
AND number = $datasetinput");
|
||||
}
|
||||
|
||||
// Build the rest of $datasetinput
|
||||
foreach ($datasetitems as $item) {
|
||||
$datasetdef = $datasetdefinitions[$item->definition];
|
||||
|
||||
// We here need to pay attention to whether the
|
||||
// data item is a link or an ordinary literal
|
||||
if ($datasetdef->type == LITERAL) {
|
||||
// The ordinary simple case
|
||||
$value = $item->value;
|
||||
|
||||
} else {
|
||||
$icon = "<img src=\"$CFG->wwwroot/pix/f/"
|
||||
. mimeinfo('icon', $item->value)
|
||||
. '" height="16" width="16" border="0" alt="File" />';
|
||||
if (substr(strtolower($item->value), 0, 7)=='http://') {
|
||||
$link = $item->value;
|
||||
|
||||
} else {
|
||||
global $quiz; // Try to reach this info globally
|
||||
if ($CFG->slasharguments) {
|
||||
// Use this method if possible for better caching
|
||||
$link = "quizfile.php/$quiz->id/$question->id/$item->value";
|
||||
|
||||
} else {
|
||||
$link = "quizfile.php?file=/$quiz->id/$question->id/$item->value";
|
||||
}
|
||||
}
|
||||
|
||||
if ($datasetdef->type == FILE
|
||||
and ereg('/([^/]+)$', $item->value, $regs)) {
|
||||
$linktext = $regs[1];
|
||||
} else {
|
||||
$linktext = $item->value;
|
||||
}
|
||||
$value = '<a target="_blank" href="' . $link
|
||||
. "\" title=\"$datasetdef->name\">$icon$linktext</a>";
|
||||
}
|
||||
|
||||
$datasetinput .= ';' . base64_encode($datasetdef->name)
|
||||
. ':' . base64_encode($value);
|
||||
}
|
||||
|
||||
// Use the virtual question type and have it ->create_response:
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$response = $virtualqtype->create_response($question,
|
||||
$this->create_virtual_nameprefix($nameprefix, $datasetinput),
|
||||
$questionsinuse);
|
||||
$response[$nameprefix] = $datasetinput;
|
||||
return $response;
|
||||
}
|
||||
|
||||
/// The following functions are needed for database upgrade script only
|
||||
// This is used by extract_responses, so it needs to stay
|
||||
function create_virtual_nameprefix($nameprefix, $datasetinput) {
|
||||
// This default implementation is sometimes overridden
|
||||
if (!ereg('([0-9]+)' . $this->name() . '$', $nameprefix, $regs)) {
|
||||
@@ -414,147 +429,16 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
|
||||
$rawresponse->answer, $regs)) {
|
||||
error ("Wrongly formatted raw response answer $rawresponse->answer");
|
||||
}
|
||||
|
||||
|
||||
// Truncate raw response to fit the virtual qtype
|
||||
$rawresponse->answer = $regs[2];
|
||||
|
||||
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$response = $virtualqtype->extract_response($rawresponse,
|
||||
$this->create_virtual_nameprefix($nameprefix, $regs[1]));
|
||||
$response[$nameprefix] = $regs[1];
|
||||
return $response;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
|
||||
// Replace wild-cards with dataset items
|
||||
$datasetinput = $question->response[$nameprefix];
|
||||
list($datasetnumber, $data) =
|
||||
$this->parse_datasetinput($datasetinput);
|
||||
foreach ($data as $name => $value) {
|
||||
$question->questiontext = str_replace
|
||||
('{'.$name.'}', $value, $question->questiontext);
|
||||
}
|
||||
|
||||
// Print hidden field with dataset info
|
||||
echo '<input type="hidden" name="' . $nameprefix
|
||||
. '" value="' . $datasetinput . '" />';
|
||||
|
||||
// Forward to the virtual qtype
|
||||
unset($question->response[$nameprefix]);
|
||||
$virtualqtype = $this->get_virtual_qtype();
|
||||
$virtualqtype->print_question_formulation_and_controls(
|
||||
$question, $quiz, $readonly, $answers, $correctanswers,
|
||||
$this->create_virtual_nameprefix($nameprefix, $datasetinput));
|
||||
}
|
||||
|
||||
function parse_datasetinput($datasetinput) {
|
||||
/// Returns an array consisting of three pieces of information
|
||||
/// In [0] there is the dataset number
|
||||
/// In [1] there is an array where the data items are mapped by name
|
||||
|
||||
/// The dataset related part of the response key
|
||||
/// follows right after the response key and end with :
|
||||
/// In order to avoid any conflict that can occur whenever anyone
|
||||
/// wish to use : in the data, the data dependent part
|
||||
/// has been converted to base64 in two steps
|
||||
|
||||
$rawdata = split('[:;]', $datasetinput);
|
||||
$rawlength = count($rawdata);
|
||||
$i = 0;
|
||||
$data = array();
|
||||
while (++$i < $rawlength) {
|
||||
$data[base64_decode($rawdata[$i])] =
|
||||
base64_decode($rawdata[++$i]);
|
||||
}
|
||||
return array($rawdata[0], $data);
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
function quiz_qtype_dataset_pick_new($category, $maxnumber) {
|
||||
//// Function used by ->create_response
|
||||
//// It takes care of picking a new datasetnumber for
|
||||
//// the user in the specified question category
|
||||
|
||||
// We need to know whether the attempt builds on the last or
|
||||
// not. It can not be determined by the function args to
|
||||
// ->create_response.
|
||||
// Instead of adding that argument to all implementations of
|
||||
// create_response we try to reach $quiz globally. That
|
||||
// should work because this function is only used by
|
||||
// attempt.php when starting an attempt.
|
||||
global $quiz; //// PATTERN VIOLATION ////
|
||||
if ($quiz->attemptonlast) {
|
||||
|
||||
// Dataset numbers for attemptonlast quizes are stored
|
||||
// in quiz_attemptonlast_datasets
|
||||
global $USER;
|
||||
if (!($attemptonlastdataset = get_record(
|
||||
'quiz_attemptonlast_datasets',
|
||||
'userid', $USER->id, 'category', $category))
|
||||
or $attemptonlastdataset->datasetnumber > $maxnumber) {
|
||||
|
||||
// No suitable $attemptonlastdataset
|
||||
|
||||
if ($attemptonlastdataset) {
|
||||
// Remove the unsuitable:
|
||||
delete_records('quiz_attemptonlast_datasets',
|
||||
'id', $attemptonlastdataset->id);
|
||||
unset($attemptonlastdataset->id);
|
||||
unset($attemptonlastdataset->datasetnumber);
|
||||
|
||||
} else {
|
||||
$attemptonlastdataset->userid = $USER->id;
|
||||
$attemptonlastdataset->category = $category;
|
||||
}
|
||||
|
||||
// Create without setting datasetnumber
|
||||
// so that this user gets its id
|
||||
$attemptonlastdataset->id = insert_record(
|
||||
'quiz_attemptonlast_datasets', $attemptonlastdataset);
|
||||
|
||||
// Pick the datasetnumber in a thread safe way
|
||||
// so that this can be done without having
|
||||
// concurrent users get the same datasetnumber!
|
||||
// The chosen pattern relies on
|
||||
// synchronization for autoincrement on the id
|
||||
// when the previous insert_record statement
|
||||
// was executed.
|
||||
if (!($latestdatasetpick = get_record_select(
|
||||
'quiz_attemptonlast_datasets',
|
||||
"category = $category AND datasetnumber > 0
|
||||
AND id < $attemptonlastdataset->id",
|
||||
' max(id) id '))) {
|
||||
// Smells like the current user is first:
|
||||
$latestdatasetpick->id = 0;
|
||||
}
|
||||
$latestattemptonlasts = get_records_select(
|
||||
'quiz_attemptonlast_datasets',
|
||||
"$latestdatasetpick->id <= id
|
||||
AND id < $attemptonlastdataset->id
|
||||
AND category = $category");
|
||||
$attemptonlastdataset->datasetnumber =
|
||||
(count($latestattemptonlasts)
|
||||
+ (isset($latestattemptonlasts[$latestdatasetpick->id])
|
||||
? $latestattemptonlasts[$latestdatasetpick->id]
|
||||
->datasetnumber - 1
|
||||
: 0))
|
||||
% $maxnumber + 1;
|
||||
if (!update_record('quiz_attemptonlast_datasets',
|
||||
$attemptonlastdataset)) {
|
||||
notify("Error unable to save the picked datasetnumber in
|
||||
quiz_attemptonlast_datasets for user $USER-id");
|
||||
}
|
||||
}
|
||||
return $attemptonlastdataset->datasetnumber;
|
||||
|
||||
} else {
|
||||
// When it is not an attemptonlast
|
||||
// we pick the dataset number randomly
|
||||
return rand ( 1 , $maxnumber );
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -17,27 +17,23 @@ class quiz_description_qtype extends quiz_default_questiontype {
|
||||
return 'description';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// No options to be restored for this question type
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
/// No options to be saved for this question type:
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_response($question, $nameprefix, $questionsinuse) {
|
||||
/// This question type does never have any responses,
|
||||
/// so do not return any...
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
function print_question($currentnumber, $quiz, $question,
|
||||
$readonly, $resultdetails) {
|
||||
echo '<p align="center">';
|
||||
function print_question(&$question, &$state, &$number, $quiz, $options) {
|
||||
print_simple_box_start('center', '90%');
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat,
|
||||
NULL, $quiz->course);
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
echo '</p>';
|
||||
return $currentnumber;
|
||||
print_simple_box_end('center', '90%');
|
||||
}
|
||||
|
||||
function actual_number_of_questions($question) {
|
||||
@@ -49,12 +45,11 @@ class quiz_description_qtype extends quiz_default_questiontype {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
return $result;
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
$state->raw_grade = 0;
|
||||
$state->penalty = 0;
|
||||
}
|
||||
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ class quiz_match_qtype extends quiz_default_questiontype {
|
||||
return 'match';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
|
||||
if (!$oldsubquestions = get_records("quiz_match_sub", "question", $question->id, "id ASC")) {
|
||||
@@ -81,21 +85,104 @@ class quiz_match_qtype extends quiz_default_questiontype {
|
||||
return true;
|
||||
}
|
||||
|
||||
function convert_to_response_answer_field($questionresponse) {
|
||||
/// This method, together with extract_response, should be
|
||||
/// obsolete as soon as we get a better response storage
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
if (!$state->options->subquestions = get_records('quiz_match_sub',
|
||||
'question', $question->id)) {
|
||||
notify('Error: Missing subquestions!');
|
||||
return false;
|
||||
}
|
||||
|
||||
$delimiter = '';
|
||||
$responseanswerfield = '';
|
||||
foreach ($questionresponse as $key => $value) {
|
||||
if ($matchid = $this->extract_response_id($key)) {
|
||||
$responseanswerfield .= "$delimiter$matchid-$value";
|
||||
$delimiter = ',';
|
||||
} else {
|
||||
notify("Error: Illegal match key $key detected");
|
||||
foreach ($state->options->subquestions as $key => $subquestion) {
|
||||
// This seems rather over complicated, but it is useful for the
|
||||
// randomsamatch questiontype, which can then inherit the print
|
||||
// and grading functions. This way it is possible to define multiple
|
||||
// answers per question, each with different marks and feedback.
|
||||
$answer = new stdClass();
|
||||
$answer->id = $subquestion->id;
|
||||
$answer->answer = $subquestion->answertext;
|
||||
$answer->fraction = 1.0;
|
||||
$state->options->subquestions[$key]->options
|
||||
->answers[$subquestion->id] = clone($answer);
|
||||
}
|
||||
|
||||
// Shuffle the answers if required
|
||||
$subquestionids = array_values(array_map(create_function('$val',
|
||||
'return $val->id;'), $state->options->subquestions));
|
||||
if ($quiz->shuffleanswers) {
|
||||
$subquestionids = swapshuffle($subquestionids);
|
||||
}
|
||||
$state->options->order = $subquestionids;
|
||||
// Create empty responses
|
||||
foreach ($subquestionids as $val) {
|
||||
$state->responses[$val] = '';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
// The serialized format for matching questions is a comma separated
|
||||
// list of question answer pairs (e.g. 1-1,2-3,3-2), where the ids of
|
||||
// both refer to the id in the table quiz_match_sub.
|
||||
$responses = explode(',', $state->responses['']);
|
||||
$responses = array_map(create_function('$val',
|
||||
'return explode("-", $val);'), $responses);
|
||||
|
||||
// Restore the previous responses
|
||||
$state->responses = array();
|
||||
foreach ($responses as $response) {
|
||||
$state->responses[$response[0]] = $response[1];
|
||||
}
|
||||
|
||||
if (!$state->options->subquestions = get_records('quiz_match_sub',
|
||||
'question', $question->id)) {
|
||||
notify('Error: Missing subquestions!');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($state->options->subquestions as $key => $subquestion) {
|
||||
// This seems rather over complicated, but it is useful for the
|
||||
// randomsamatch questiontype, which can then inherit the print
|
||||
// and grading functions. This way it is possible to define multiple
|
||||
// answers per question, each with different marks and feedback.
|
||||
$answer = new stdClass();
|
||||
$answer->id = $subquestion->id;
|
||||
$answer->answer = $subquestion->answertext;
|
||||
$answer->fraction = 1.0;
|
||||
$state->options->subquestions[$key]->options
|
||||
->answers[$subquestion->id] = clone($answer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
// Serialize responses
|
||||
$responses = array();
|
||||
foreach ($state->responses as $key => $val) {
|
||||
if ($key != '') {
|
||||
$responses[] = "$key-$val";
|
||||
}
|
||||
}
|
||||
return $responseanswerfield;
|
||||
$responses = implode(',', $responses);
|
||||
|
||||
// Set the legacy answer field
|
||||
if (!set_field('quiz_states', 'answer', $responses, 'id',
|
||||
$state->id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
$responses = array();
|
||||
foreach ($state->options->subquestions as $sub) {
|
||||
foreach ($sub->options->answers as $answer) {
|
||||
if (1 == $answer->fraction) {
|
||||
$responses[$sub->id] = $answer->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return empty($responses) ? null : $responses;
|
||||
}
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
@@ -114,8 +201,19 @@ class quiz_match_qtype extends quiz_default_questiontype {
|
||||
return $response;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz, $options) {
|
||||
$subquestions = $state->options->subquestions;
|
||||
$correctanswers = $this->get_correct_responses($question, $state);
|
||||
$nameprefix = $question->name_prefix;
|
||||
$answers = array();
|
||||
foreach ($subquestions as $subquestion) {
|
||||
foreach ($subquestion->options->answers as $sub) {
|
||||
$answers[$sub->id] = $sub->answer;
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle the answers
|
||||
$answers = draw_rand_array($answers, count($answers));
|
||||
|
||||
// Print question text and possible image
|
||||
if (!empty($question->questiontext)) {
|
||||
@@ -125,37 +223,7 @@ class quiz_match_qtype extends quiz_default_questiontype {
|
||||
}
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
// It so happens to be that $correctanswers for this question type also
|
||||
// contains the subqustions, which we need to make sure we have:
|
||||
if (empty($correctanswers)) {
|
||||
$options = get_record('quiz_match', 'question', $question->id)
|
||||
and $subquestions = get_records_list('quiz_match_sub', 'id',
|
||||
$options->subquestions);
|
||||
} else {
|
||||
$subquestions = $correctanswers;
|
||||
}
|
||||
|
||||
/// Check whether everything turned out alright:
|
||||
if (empty($subquestions)) {
|
||||
notify("Error: Missing subquestions for this question!");
|
||||
|
||||
} else {
|
||||
/// Everything is fine -
|
||||
/// Set up $subquestions and $answers and do the shuffling:
|
||||
|
||||
if ($quiz->shuffleanswers) {
|
||||
$subquestions = draw_rand_array($subquestions,
|
||||
count($subquestions));
|
||||
}
|
||||
foreach ($subquestions as $key => $subquestion) {
|
||||
unset($answers[$key]);
|
||||
$answers[$subquestion->id] = $subquestion->answertext;
|
||||
}
|
||||
$answers = draw_rand_array($answers, count($answers));
|
||||
}
|
||||
|
||||
///// Print the input controls //////
|
||||
|
||||
echo '<table border="0" cellpadding="10" align="right">';
|
||||
foreach ($subquestions as $subquestion) {
|
||||
|
||||
@@ -167,76 +235,60 @@ class quiz_match_qtype extends quiz_default_questiontype {
|
||||
|
||||
/// Drop-down list:
|
||||
$menuname = $nameprefix.$subquestion->id;
|
||||
$response = isset($question->response[$menuname])
|
||||
? $question->response[$menuname] : '0';
|
||||
if ($readonly
|
||||
and $quiz->correctanswers
|
||||
and isset($correctanswers[$menuname])
|
||||
and ($correctanswers[$menuname]->id == $response)) {
|
||||
$response = isset($state->responses[$subquestion->id])
|
||||
? $state->responses[$subquestion->id] : '0';
|
||||
if ($options->readonly
|
||||
and $options->correct_responses
|
||||
and isset($correctanswers[$subquestion->id])
|
||||
and ($correctanswers[$subquestion->id] == $response)) {
|
||||
$class = ' class="highlight" ';
|
||||
} else {
|
||||
$class = '';
|
||||
}
|
||||
echo "<td align=\"right\" valign=\"top\" $class>";
|
||||
choose_from_menu($answers, $menuname, $response);
|
||||
if ($quiz->feedback && isset($answers[$menuname])
|
||||
&& $answers[$menuname]->feedback) {
|
||||
quiz_print_comment($answers[$menuname]->feedback);
|
||||
|
||||
choose_from_menu($answers, $menuname, $response, 'choose', '', 0,
|
||||
false, $options->readonly);
|
||||
/* There is currently no feedback for this questiontype.
|
||||
if ($quiz->feedback && isset($subquestion->feedback)) {
|
||||
quiz_print_comment($subquestion->feedback);
|
||||
}
|
||||
*/
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
/// This question type does not use the table quiz_answers
|
||||
/// but we will take some measures to emulate that record anyway.
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
$subquestions = $state->options->subquestions;
|
||||
$responses = &$state->responses;
|
||||
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
|
||||
if (!($options = get_record('quiz_match', 'question', $question->id)
|
||||
and $subquestions = get_records_list('quiz_match_sub',
|
||||
'id', $options->subquestions))) {
|
||||
notify("Error: Cannot find match options and subquestions
|
||||
for question $question->id");
|
||||
return $result;
|
||||
}
|
||||
|
||||
$fraction = 1.0 / count($subquestions);
|
||||
|
||||
/// Populate correctanswers arrays:
|
||||
$answers = array();
|
||||
foreach ($subquestions as $subquestion) {
|
||||
$subquestion->fraction = $fraction;
|
||||
$subquestion->answer = $subquestion->answertext;
|
||||
$subquestion->feedback = '';
|
||||
$result->correctanswers[$nameprefix.$subquestion->id] =
|
||||
$subquestion;
|
||||
}
|
||||
|
||||
foreach ($question->response as $responsekey => $answerid) {
|
||||
|
||||
if ($answerid and $answer =
|
||||
$result->correctanswers[$nameprefix.$answerid]) {
|
||||
|
||||
if ($result->correctanswers[$responsekey]->answer
|
||||
== $answer->answer) {
|
||||
|
||||
/// The response was correct!
|
||||
$result->answers[$responsekey] =
|
||||
$result->correctanswers[$responsekey];
|
||||
$result->grade += $fraction;
|
||||
|
||||
} else {
|
||||
/// The response was incorrect:
|
||||
$answer->fraction = 0.0;
|
||||
$result->answers[$responsekey] = $answer;
|
||||
}
|
||||
|
||||
foreach ($subquestion->options->answers as $sub) {
|
||||
$answers[$sub->id] = $sub->answer;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
||||
$sumgrade = 0;
|
||||
foreach ($subquestions as $key => $sub) {
|
||||
if (isset($sub->options->answers[$responses[$key]])) {
|
||||
$sumgrade += $sub->options->answers[$responses[$key]]->fraction;
|
||||
}
|
||||
}
|
||||
|
||||
$state->raw_grade = $sumgrade/count($subquestions);
|
||||
if (empty($state->raw_grade)) {
|
||||
$state->raw_grade = 0;
|
||||
}
|
||||
|
||||
// Make sure we don't assign negative or too high marks
|
||||
$state->raw_grade = min(max((float) $state->raw_grade,
|
||||
0.0), 1.0) * $question->maxgrade;
|
||||
$state->penalty = 0;
|
||||
// Only allow one attempt at the question
|
||||
$state->event = QUIZ_EVENTCLOSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
@@ -1,41 +1,54 @@
|
||||
<?php // $Id$
|
||||
if ($question->questiontext and $question->id) {
|
||||
$answers = quiz_get_answers($question);
|
||||
if ($question->questiontext and $question->id and $question->qtype) {
|
||||
global $QUIZ_QTYPES;
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
|
||||
foreach ($answers as $multianswer) {
|
||||
$parsableanswerdef = '{' . $multianswer->norm . ':';
|
||||
switch ($multianswer->answertype) {
|
||||
case MULTICHOICE:
|
||||
$parsableanswerdef .= 'MULTICHOICE:';
|
||||
break;
|
||||
case SHORTANSWER:
|
||||
$parsableanswerdef .= 'SHORTANSWER:';
|
||||
break;
|
||||
case NUMERICAL:
|
||||
$parsableanswerdef .= 'NUMERICAL:';
|
||||
break;
|
||||
default:
|
||||
error("answertype $multianswer->answertype not recognized");
|
||||
}
|
||||
$separator= '';
|
||||
foreach ($multianswer->subanswers as $subanswer) {
|
||||
$parsableanswerdef .= $separator
|
||||
. '%' . round(100*$subanswer->fraction) . '%';
|
||||
$parsableanswerdef .= $subanswer->answer;
|
||||
if (isset($subanswer->min) && isset($subanswer->max)
|
||||
and $subanswer->min || $subanswer->max) {
|
||||
// Special for numerical answers:
|
||||
$errormargin = $subanswer->answer - $subanswer->min;
|
||||
$parsableanswerdef .= ":$errormargin";
|
||||
foreach ($question->options->questions as $key => $wrapped) {
|
||||
// The old way of restoring the definitions is kept to gradually
|
||||
// update all multianswer questions
|
||||
if (empty($wrapped->questiontext)) {
|
||||
$parsableanswerdef = '{' . $wrapped->defaultgrade . ':';
|
||||
switch ($wrapped->qtype) {
|
||||
case MULTICHOICE:
|
||||
$parsableanswerdef .= 'MULTICHOICE:';
|
||||
break;
|
||||
case SHORTANSWER:
|
||||
$parsableanswerdef .= 'SHORTANSWER:';
|
||||
break;
|
||||
case NUMERICAL:
|
||||
$parsableanswerdef .= 'NUMERICAL:';
|
||||
break;
|
||||
default:
|
||||
error("questiontype $wrapped->qtype not recognized");
|
||||
}
|
||||
if ($subanswer->feedback) {
|
||||
$parsableanswerdef .= "#$subanswer->feedback";
|
||||
$separator= '';
|
||||
foreach ($wrapped->options->answers as $subanswer) {
|
||||
$parsableanswerdef .= $separator
|
||||
. '%' . round(100*$subanswer->fraction) . '%';
|
||||
$parsableanswerdef .= $subanswer->answer;
|
||||
if (!empty($wrapped->options->tolerance)) {
|
||||
// Special for numerical answers:
|
||||
$parsableanswerdef .= ":{$wrapped->options->tolerance}";
|
||||
// We only want tolerance for the first alternative, it will
|
||||
// be applied to all of the alternatives.
|
||||
unset($wrapped->options->tolerance);
|
||||
}
|
||||
if ($subanswer->feedback) {
|
||||
$parsableanswerdef .= "#$subanswer->feedback";
|
||||
}
|
||||
$separator = '~';
|
||||
}
|
||||
$separator = '~';
|
||||
$parsableanswerdef .= '}';
|
||||
// Fix the questiontext fields of old questions
|
||||
set_field('quiz_questions', 'questiontext', $parsableanswerdef,
|
||||
'id', $wrapped->id);
|
||||
} else {
|
||||
$parsableanswerdef = $wrapped->questiontext;
|
||||
}
|
||||
$parsableanswerdef .= '}';
|
||||
//echo "{#$key}<br />";
|
||||
//echo $parsableanswerdef.'<br />';
|
||||
$question->questiontext = str_replace
|
||||
("{#$multianswer->positionkey}", $parsableanswerdef,
|
||||
("{#$key}", $parsableanswerdef,
|
||||
$question->questiontext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,99 +15,81 @@
|
||||
/// QUESTION TYPE CLASS //////////////////
|
||||
class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
|
||||
function get_answers($question) {
|
||||
// This function is not used by any other function within this class.
|
||||
// It is possible that it is used by some report that uses the
|
||||
// function quiz_get_answers in lib.php
|
||||
|
||||
/// The returned answers includes subanswers...
|
||||
// As this question type embedds some other question types,
|
||||
// it is necessary to have access to those:
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
$answers = array();
|
||||
|
||||
$virtualquestion->id = $question->id;
|
||||
|
||||
if ($multianswers = get_records('quiz_multianswers', 'question', $question->id)) {
|
||||
foreach ($multianswers as $multianswer) {
|
||||
$virtualquestion->qtype = $multianswer->answertype;
|
||||
// Call to other question type for subanswers
|
||||
$addedcondition = " AND a.id IN ($multianswer->answers) ";
|
||||
$multianswer->subanswers =
|
||||
$QUIZ_QTYPES[$multianswer->answertype]
|
||||
->get_answers($virtualquestion, $addedcondition);
|
||||
$answers[] = $multianswer;
|
||||
}
|
||||
}
|
||||
return $answers;
|
||||
}
|
||||
|
||||
function get_position_multianswer($questionid, $positionkey) {
|
||||
// As a separate function in order to make it overridable
|
||||
|
||||
return get_record('quiz_multianswers', 'question', $questionid,
|
||||
'positionkey', $positionkey);
|
||||
}
|
||||
|
||||
function get_multianswers($questionid) {
|
||||
// As a separate function in order to make it overridable
|
||||
return get_records('quiz_multianswers', 'question', $questionid);
|
||||
}
|
||||
|
||||
function name() {
|
||||
return 'multianswer';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// Get relevant data indexed by positionkey from the multianswers table
|
||||
if (!$sequence = get_field('quiz_multianswers', 'sequence', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
|
||||
global $QUIZ_QTYPES;
|
||||
// Temporary solution to be replaced with commented out version after
|
||||
// redundant wrapped questions are deleted when necessary.
|
||||
// $wrappedquestions = get_records('quiz_questions', 'parent', $question->id);
|
||||
$wrappedquestions = get_records_list('quiz_questions', 'id', $sequence);
|
||||
|
||||
// We want an array with question ids as index and the positions as values
|
||||
$sequence = array_flip(explode(',', $sequence));
|
||||
array_walk($sequence, create_function('&$val', '$val++;'));
|
||||
|
||||
foreach ($wrappedquestions as $wrapped) {
|
||||
if (!$QUIZ_QTYPES[$wrapped->qtype]
|
||||
->get_question_options($wrapped)) {
|
||||
error("Unable to recognized questiontype " .
|
||||
$wrapped->qtype);
|
||||
}
|
||||
$wrapped->maxgrade = $wrapped->defaultgrade;
|
||||
$question->options->questions[$sequence[$wrapped->id]] = clone($wrapped);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
if (!$oldmultianswers = get_records("quiz_multianswers", "question", $question->id, "id ASC")) {
|
||||
$oldmultianswers = array();
|
||||
global $QUIZ_QTYPES;
|
||||
if (!$oldwrappedids =
|
||||
get_records('quiz_questions', 'parent', $question->id, '', 'id, id')) {
|
||||
// We need to select 'id, id' because the first one is consumed by
|
||||
// get_records.
|
||||
$oldwrappedids = array();
|
||||
}
|
||||
|
||||
if (empty($question->name)) {
|
||||
$result->notice = get_string("missingname", "quiz");
|
||||
return $result;
|
||||
$oldwrappedids = array_keys($oldwrappedids);
|
||||
$sequence = array();
|
||||
foreach($question->options->questions as $wrapped) {
|
||||
if ($oldwrappedid = array_shift($oldwrappedids)) {
|
||||
$wrapped->id = $oldwrappedid;
|
||||
}
|
||||
$wrapped->name = $question->name;
|
||||
$wrapped->parent = $question->id;
|
||||
$wrapped->category = $question->category;
|
||||
$wrapped->version = $question->version;
|
||||
$wrapped = $QUIZ_QTYPES[$wrapped->qtype]->save_question($wrapped,
|
||||
$wrapped, $question->course);
|
||||
$sequence[] = $wrapped->id;
|
||||
}
|
||||
if (!$question->answers) {
|
||||
$result->notice = get_string('noquestionintext', 'quiz');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Insert all the new multi answers
|
||||
foreach ($question->answers as $dataanswer) {
|
||||
if ($oldmultianswer = array_shift($oldmultianswers)) { // Existing answer, so reuse it
|
||||
$multianswer = $oldmultianswer;
|
||||
$multianswer->positionkey = $dataanswer->positionkey;
|
||||
$multianswer->norm = $dataanswer->norm;
|
||||
$multianswer->answertype = $dataanswer->answertype;
|
||||
|
||||
if (! $multianswer->answers =
|
||||
quiz_qtype_multianswer_save_alternatives
|
||||
($question->id, $dataanswer->answertype,
|
||||
$dataanswer->alternatives, $oldmultianswer->answers))
|
||||
{
|
||||
$result->error = "Could not update multianswer alternatives! (id=$multianswer->id)";
|
||||
return $result;
|
||||
}
|
||||
// Delete redundant wrapped questions
|
||||
$oldwrappedids = implode(',', $oldwrappedids);
|
||||
delete_records_select('quiz_questions', "id IN ($oldwrappedids)");
|
||||
|
||||
if (!empty($sequence)) {
|
||||
$multianswer = new stdClass;
|
||||
$multianswer->question = $question->id;
|
||||
$multianswer->sequence = implode(',', $sequence);
|
||||
if ($oldid =
|
||||
get_field('quiz_multianswers', 'id', 'question', $question->id)) {
|
||||
$multianswer->id = $oldid;
|
||||
if (!update_record("quiz_multianswers", $multianswer)) {
|
||||
$result->error = "Could not update quiz multianswer! (id=$multianswer->id)";
|
||||
return $result;
|
||||
}
|
||||
} else { // This is a completely new answer
|
||||
unset($multianswer);
|
||||
$multianswer->question = $question->id;
|
||||
$multianswer->positionkey = $dataanswer->positionkey;
|
||||
$multianswer->norm = $dataanswer->norm;
|
||||
$multianswer->answertype = $dataanswer->answertype;
|
||||
|
||||
if (! $multianswer->answers =
|
||||
quiz_qtype_multianswer_save_alternatives
|
||||
($question->id, $dataanswer->answertype,
|
||||
$dataanswer->alternatives))
|
||||
{
|
||||
$result->error = "Could not insert multianswer alternatives! (questionid=$question->id)";
|
||||
$result->error = "Could not update quiz multianswer! " .
|
||||
"(id=$multianswer->id)";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
if (!insert_record("quiz_multianswers", $multianswer)) {
|
||||
$result->error = "Could not insert quiz multianswer!";
|
||||
return $result;
|
||||
@@ -115,75 +97,66 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function save_question($authorizedquestion, $form, $course) {
|
||||
|
||||
$question = quiz_qtype_multianswer_extract_question
|
||||
($form->questiontext);
|
||||
function save_question($authorizedquestion, $form, $course) {
|
||||
$question =
|
||||
quiz_qtype_multianswer_extract_question ($form->questiontext);
|
||||
if (isset($authorizedquestion->id)) {
|
||||
$question->id = $authorizedquestion->id;
|
||||
}
|
||||
$question->qtype = $authorizedquestion->qtype;
|
||||
$question->category = $authorizedquestion->category;
|
||||
|
||||
$question->name = $form->name;
|
||||
if (empty($form->image)) {
|
||||
$question->image = "";
|
||||
$question->version = $form->version = $authorizedquestion->version;
|
||||
} else {
|
||||
$question->image = $form->image;
|
||||
$question->version = $form->version = 1;
|
||||
}
|
||||
|
||||
if (!empty($question->id)) { // Question already exists
|
||||
if (!update_record("quiz_questions", $question)) {
|
||||
error("Could not update question!");
|
||||
}
|
||||
} else { // Question is a new one
|
||||
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
|
||||
if (!$question->id = insert_record("quiz_questions", $question)) {
|
||||
error("Could not insert new question!");
|
||||
}
|
||||
}
|
||||
|
||||
// Now to save all the answers and type-specific options
|
||||
|
||||
$form->id = $question->id;
|
||||
$form->qtype = $question->qtype;
|
||||
$form->category = $question->category;
|
||||
|
||||
$result = $this->save_question_options($question);
|
||||
|
||||
if (!empty($result->error)) {
|
||||
error($result->error);
|
||||
}
|
||||
|
||||
if (!empty($result->notice)) {
|
||||
notice($result->notice, "question.php?id=$question->id");
|
||||
}
|
||||
|
||||
if (!empty($result->noticeyesno)) {
|
||||
notice_yesno($result->noticeyesno, "question.php?id=$question->id", "edit.php");
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
return $question;
|
||||
$question->category = $authorizedquestion->category;
|
||||
$form->course = $course; // To pass the course object to
|
||||
// save_question_options, where it is
|
||||
// needed to call type specific
|
||||
// save_question methods.
|
||||
$form->defaultgrade = $question->defaultgrade;
|
||||
$form->questiontext = $question->questiontext;
|
||||
$form->questiontextformat = 0;
|
||||
$form->options = clone($question->options);
|
||||
unset($question->options);
|
||||
return parent::save_question($question, $form, $course);
|
||||
}
|
||||
|
||||
function convert_to_response_answer_field($questionresponse) {
|
||||
/// This method, together with extract_response, should be
|
||||
/// obsolete as soon as we get a better response storage
|
||||
|
||||
$delimiter = '';
|
||||
$responseanswerfield = '';
|
||||
foreach ($questionresponse as $key => $value) {
|
||||
if ($multianswerid = $this->extract_response_id($key)) {
|
||||
$responseanswerfield .= "$delimiter$multianswerid-$value";
|
||||
$delimiter = ',';
|
||||
} else {
|
||||
notify("Error: Illegal match key $key detected");
|
||||
}
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
$state->responses = array();
|
||||
foreach ($question->options->questions as $key => $wrapped) {
|
||||
$state->responses[$key] = '';
|
||||
}
|
||||
return $responseanswerfield;
|
||||
return true;
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
$responses = explode(',', $state->responses['']);
|
||||
$state->responses = array();
|
||||
foreach ($responses as $response) {
|
||||
$tmp = explode("-", $response);
|
||||
// restore encoded characters
|
||||
$state->responses[$tmp[0]] =
|
||||
str_replace(array(",", "-"), array(",", "-"), $tmp[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
$responses = $state->responses;
|
||||
array_walk($responses, create_function('&$val, $key',
|
||||
// encode - (hyphen) and , (comma) to - because they are used as
|
||||
// delimiters
|
||||
'$val = str_replace(array(",", "-"), array(",", "-"), $val);
|
||||
$val = "$key-$val";'));
|
||||
$responses = implode(',', $responses);
|
||||
|
||||
// Set the legacy answer field
|
||||
if (!set_field('quiz_states', 'answer', $responses, 'id',
|
||||
$state->id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
@@ -204,8 +177,22 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
return $response;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
$responses = array();
|
||||
foreach($question->options->questions as $key => $wrapped) {
|
||||
$correct = $QUIZ_QTYPES[$wrapped->qtype]
|
||||
->get_correct_responses($wrapped, $state);
|
||||
$responses[$key] = $correct[''];
|
||||
}
|
||||
return $responses;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
|
||||
$nameprefix = $question->name_prefix;
|
||||
|
||||
// For this question type, we better print the image on top:
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
@@ -224,45 +211,77 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
echo $qtextsplits[0];
|
||||
$qtextremaining = $qtextsplits[1];
|
||||
|
||||
$multianswer = $this->get_position_multianswer($question->id, $regs[1]);
|
||||
$positionkey = $regs[1];
|
||||
$wrapped = &$question->options->questions[$positionkey];
|
||||
$answers = &$wrapped->options->answers;
|
||||
$correctanswers = $QUIZ_QTYPES[$wrapped->qtype]
|
||||
->get_correct_responses($wrapped, $state);
|
||||
|
||||
$inputname = $nameprefix.$multianswer->id;
|
||||
$response = isset($question->response[$inputname])
|
||||
? $question->response[$inputname] : '';
|
||||
$inputname = $nameprefix.$positionkey;
|
||||
$response = isset($state->responses[$positionkey])
|
||||
? $state->responses[$positionkey] : null;
|
||||
|
||||
/// Determine style
|
||||
if (!empty($correctanswers) && '' !== $response) {
|
||||
|
||||
if (!isset($answers[$inputname])
|
||||
|| $answers[$inputname]->fraction <= 0.0) {
|
||||
// The response must have been totally wrong:
|
||||
$style = ' style="background-color:red" ';
|
||||
|
||||
} else if ($answers[$inputname]->fraction >= 1.0) {
|
||||
// The response must was correct!!
|
||||
$style = 'style="background-color:lime"';
|
||||
|
||||
} else {
|
||||
// This response did at least give some credit:
|
||||
$style = 'style="background-color:yellow"';
|
||||
}
|
||||
} else {
|
||||
// No colorish feedback is to be used
|
||||
$style = '';
|
||||
}
|
||||
|
||||
// Determine feedback popup if any
|
||||
if ($quiz->feedback && isset($answers[$inputname])
|
||||
&& '' !== $answers[$inputname]->feedback) {
|
||||
$title = str_replace("'", "\\'", $answers[$inputname]->feedback);
|
||||
$popup = " onmouseover=\"return overlib('$title', CAPTION, '$strfeedback', FGCOLOR);\" ".
|
||||
" onmouseout=\"return nd();\" ";
|
||||
} else {
|
||||
$popup = '';
|
||||
$popup = '';
|
||||
$style = '';
|
||||
if ($options->feedback) {
|
||||
$chosenanswer = null;
|
||||
switch ($wrapped->qtype) {
|
||||
case NUMERICAL:
|
||||
case SHORTANSWER:
|
||||
$testedstate = clone($state);
|
||||
$testedstate->responses[''] = $response;
|
||||
$teststate = clone($state);
|
||||
$raw_grade = 0;
|
||||
foreach ($answers as $answer) {
|
||||
$teststate->responses[''] = trim($answer->answer);
|
||||
if($QUIZ_QTYPES[$wrapped->qtype]
|
||||
->compare_responses($wrapped, $testedstate, $teststate)) {
|
||||
if (empty($raw_grade) || $raw_grade < $answer->fraction) {
|
||||
//var_dump($teststate->responses['']);
|
||||
//var_dump($testedstate->responses['']);
|
||||
$chosenanswer = clone($answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MULTICHOICE:
|
||||
if (isset($answers[$response])) {
|
||||
$chosenanswer = clone($answers[$response]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($chosenanswer->feedback)) {
|
||||
$feedback = str_replace("'", "\\'", $chosenanswer->feedback);
|
||||
$popup = " onmouseover=\"return overlib('$feedback', CAPTION, '$strfeedback', FGCOLOR);\" ".
|
||||
" onmouseout=\"return nd();\" ";
|
||||
}
|
||||
|
||||
/// Determine style
|
||||
if (!empty($chosenanswer)) {
|
||||
if (!isset($chosenanswer->fraction)
|
||||
|| $chosenanswer->fraction <= 0.0) {
|
||||
// The response must have been totally wrong:
|
||||
$style = ' style="background-color:red" ';
|
||||
|
||||
} else if ($chosenanswer->fraction >= 1.0) {
|
||||
// The response was correct!!
|
||||
$style = 'style="background-color:lime"';
|
||||
|
||||
} else {
|
||||
// This response did at least give some credit:
|
||||
$style = 'style="background-color:yellow"';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Print the input control
|
||||
switch ($multianswer->answertype) {
|
||||
switch ($wrapped->qtype) {
|
||||
case SHORTANSWER:
|
||||
case NUMERICAL:
|
||||
echo " <input $style $readonly $popup name=\"$inputname\"
|
||||
@@ -270,13 +289,12 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
break;
|
||||
case MULTICHOICE:
|
||||
$outputoptions = '<option></option>'; // Default empty option
|
||||
$mcanswers = get_records_list("quiz_answers", "id", $multianswer->answers);
|
||||
foreach ($mcanswers as $mcanswer) {
|
||||
foreach ($answers as $mcanswer) {
|
||||
$selected = $response == $mcanswer->id
|
||||
? ' selected="selected" ' : '';
|
||||
$outputoptions .= "<option value=\"$mcanswer->id\" $selected>$mcanswer->answer</option>";
|
||||
}
|
||||
echo "<select $popup $style name=\"$inputname\">";
|
||||
echo "<select $popup $readonly $style name=\"$inputname\">";
|
||||
echo $outputoptions;
|
||||
echo '</select>';
|
||||
break;
|
||||
@@ -290,56 +308,30 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype {
|
||||
echo $qtextremaining;
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
|
||||
$multianswers = $this->get_multianswers($question->id);
|
||||
// Default settings:
|
||||
$subquestion->id = $question->id;
|
||||
$normsum = 0;
|
||||
|
||||
// Grade each multianswer
|
||||
foreach ($multianswers as $multianswer) {
|
||||
$name = $nameprefix.$multianswer->id;
|
||||
$subquestion->response[$nameprefix] =
|
||||
isset($question->response[$name])
|
||||
? $question->response[$name] : '';
|
||||
|
||||
$subresult = $QUIZ_QTYPES[$multianswer->answertype]
|
||||
->grade_response($subquestion, $nameprefix,
|
||||
" AND a.id IN ($multianswer->answers) ");
|
||||
|
||||
// Summarize subquestion results:
|
||||
|
||||
if (isset($subresult->answers[$nameprefix])) {
|
||||
|
||||
/// Answer was found:
|
||||
$result->answers[$name] = $subresult->answers[$nameprefix];
|
||||
|
||||
if ($result->answers[$name]->fraction >= 1.0) {
|
||||
// This is also the correct answer:
|
||||
$result->correctanswers[$name] = $result->answers[$name];
|
||||
}
|
||||
$teststate = clone($state);
|
||||
$state->raw_grade = 0;
|
||||
foreach($question->options->questions as $key => $wrapped) {
|
||||
$teststate->responses = array('' => $state->responses[$key]);
|
||||
$teststate->raw_grade = 0;
|
||||
if (false === $QUIZ_QTYPES[$wrapped->qtype]
|
||||
->grade_responses($wrapped, $teststate, $quiz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($result->correctanswers[$name])) {
|
||||
// Pick the first correctanswer:
|
||||
foreach ($subresult->correctanswers as $correctanswer) {
|
||||
$result->correctanswers[$name] = $correctanswer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$result->grade += $multianswer->norm * $subresult->grade;
|
||||
$normsum += $multianswer->norm;
|
||||
$state->raw_grade += $teststate->raw_grade;
|
||||
}
|
||||
$result->grade /= $normsum;
|
||||
$state->raw_grade /= $question->defaultgrade;
|
||||
$state->raw_grade = min(max((float) $state->raw_grade, 0.0), 1.0)
|
||||
* $question->maxgrade;
|
||||
|
||||
return $result;
|
||||
if (empty($state->raw_grade)) {
|
||||
$state->raw_grade = 0.0;
|
||||
}
|
||||
$state->penalty = 0;
|
||||
// Only allow one attempt at the question
|
||||
$state->event = QUIZ_EVENTCLOSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
@@ -360,6 +352,7 @@ $QUIZ_QTYPES[MULTIANSWER]= new quiz_embedded_cloze_qtype();
|
||||
//// likely to be subject for overriding.
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
function quiz_qtype_multianswer_extract_question($text) {
|
||||
|
||||
////////////////////////////////////////////////
|
||||
@@ -428,216 +421,77 @@ function quiz_qtype_multianswer_extract_question($text) {
|
||||
//// Start of the actual function
|
||||
////////////////////////////////////////
|
||||
|
||||
$question = NULL;
|
||||
$question = new stdClass;
|
||||
$question->qtype= MULTIANSWER;
|
||||
$question->questiontext= $text;
|
||||
$question->answers= array();
|
||||
$question->options->questions = array();
|
||||
$question->defaultgrade = 0; // Will be increased for each answer norm
|
||||
|
||||
for ($positionkey=1
|
||||
; ereg(ANSWER_REGEX, $question->questiontext, $answerregs)
|
||||
; ++$positionkey )
|
||||
{
|
||||
unset($multianswer);
|
||||
|
||||
$multianswer->positionkey = $positionkey;
|
||||
$multianswer->norm = $answerregs[ANSWER_REGEX_NORM]
|
||||
or $multianswer->norm = '1';
|
||||
; ++$positionkey ) {
|
||||
$wrapped = new stdClass;
|
||||
$wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM]
|
||||
or $wrapped->defaultgrade = '1';
|
||||
if ($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL]) {
|
||||
$multianswer->answertype = NUMERICAL;
|
||||
$wrapped->qtype = NUMERICAL;
|
||||
$wrapped->multiplier = array();
|
||||
$wrapped->units = array();
|
||||
} else if($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER]) {
|
||||
$multianswer->answertype = SHORTANSWER;
|
||||
$wrapped->qtype = SHORTANSWER;
|
||||
$wrapped->usecase = 0;
|
||||
} else if($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE]){
|
||||
$multianswer->answertype = MULTICHOICE;
|
||||
$wrapped->qtype = MULTICHOICE;
|
||||
$wrapped->single = 1;
|
||||
} else {
|
||||
error("Cannot identify answertype $answerregs[2]");
|
||||
error("Cannot identify qtype $answerregs[2]");
|
||||
return false;
|
||||
}
|
||||
|
||||
$multianswer->alternatives= array();
|
||||
// Each $wrapped simulates a $form that can be processed by the
|
||||
// respective save_question and save_question_options methods of the
|
||||
// wrapped questiontypes
|
||||
$wrapped->answer = array();
|
||||
$wrapped->fraction = array();
|
||||
$wrapped->feedback = array();
|
||||
$wrapped->questiontext = $answerregs[0];
|
||||
$wrapped->questiontextformat = 0;
|
||||
|
||||
$remainingalts = $answerregs[ANSWER_REGEX_ALTERNATIVES];
|
||||
while (ereg(ANSWER_ALTERNATIVE_REGEX, $remainingalts, $altregs)) {
|
||||
unset($alternative);
|
||||
|
||||
if ('=' == $altregs[ANSWER_ALTERNATIVE_REGEX_FRACTION]) {
|
||||
$alternative->fraction = '1';
|
||||
$wrapped->fraction[] = '1';
|
||||
} else if ($percentile =
|
||||
$altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION]){
|
||||
$wrapped->fraction[] = .01 * $percentile;
|
||||
} else {
|
||||
$alternative->fraction = .01 *
|
||||
$altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION]
|
||||
or $alternative->fraction = '0';
|
||||
$wrapped->fraction[] = '0';
|
||||
}
|
||||
$alternative->feedback = $altregs[ANSWER_ALTERNATIVE_REGEX_FEEDBACK];
|
||||
$wrapped->feedback[] = $altregs[ANSWER_ALTERNATIVE_REGEX_FEEDBACK];
|
||||
if ($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL]
|
||||
&& ereg(NUMERICAL_ALTERNATIVE_REGEX,
|
||||
$altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER],
|
||||
$numregs) )
|
||||
{
|
||||
$alternative->answer = $numregs[NUMERICAL_CORRECT_ANSWER];
|
||||
$wrapped->answer[] = $numregs[NUMERICAL_CORRECT_ANSWER];
|
||||
if ($numregs[NUMERICAL_ABS_ERROR_MARGIN]) {
|
||||
$alternative->min = $numregs[NUMERICAL_CORRECT_ANSWER]
|
||||
- $numregs[NUMERICAL_ABS_ERROR_MARGIN];
|
||||
$alternative->max = $numregs[NUMERICAL_CORRECT_ANSWER]
|
||||
+ $numregs[NUMERICAL_ABS_ERROR_MARGIN];
|
||||
$wrapped->tolerance = $numregs[NUMERICAL_ABS_ERROR_MARGIN];
|
||||
} else {
|
||||
$alternative->min = $numregs[NUMERICAL_CORRECT_ANSWER];
|
||||
$alternative->max = $numregs[NUMERICAL_CORRECT_ANSWER];
|
||||
$wrapped->tolerance = 0;
|
||||
}
|
||||
} else { // Min and max must stay undefined...
|
||||
$alternative->answer =
|
||||
$altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER];
|
||||
} else { // Tolerance can stay undefined for non numerical questions
|
||||
$wrapped->answer[] = $altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER];
|
||||
}
|
||||
|
||||
$multianswer->alternatives[] = $alternative;
|
||||
$tmp = explode($altregs[0], $remainingalts, 2);
|
||||
$remainingalts = $tmp[1];
|
||||
}
|
||||
|
||||
$question->defaultgrade += $multianswer->norm;
|
||||
$question->answers[] = $multianswer;
|
||||
$question->defaultgrade += $wrapped->defaultgrade;
|
||||
$question->options->questions[$positionkey] = clone($wrapped);
|
||||
$question->questiontext = implode("{#$positionkey}",
|
||||
explode($answerregs[0], $question->questiontext, 2));
|
||||
}
|
||||
return $question;
|
||||
}
|
||||
|
||||
function quiz_qtype_multianswer_save_alternatives($questionid,
|
||||
$answertype, $alternatives, $oldalternativeids= NULL) {
|
||||
// Returns false if something goes wrong,
|
||||
// otherwise the ids of the answers.
|
||||
|
||||
if (empty($oldalternativeids)
|
||||
or !($oldalternatives =
|
||||
get_records_list('quiz_answers', 'id', $oldalternativeids)))
|
||||
{
|
||||
$oldalternatives = array();
|
||||
}
|
||||
|
||||
$alternativeids = array();
|
||||
|
||||
foreach ($alternatives as $altdata) {
|
||||
|
||||
if ($altold = array_shift($oldalternatives)) { // Use existing one...
|
||||
$alt = $altold;
|
||||
$alt->answer = $altdata->answer;
|
||||
$alt->fraction = $altdata->fraction;
|
||||
$alt->feedback = $altdata->feedback;
|
||||
if (!update_record("quiz_answers", $alt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else { // Completely new one
|
||||
unset($alt);
|
||||
$alt->question= $questionid;
|
||||
$alt->answer = $altdata->answer;
|
||||
$alt->fraction = $altdata->fraction;
|
||||
$alt->feedback = $altdata->feedback;
|
||||
if (!($alt->id = insert_record("quiz_answers", $alt))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// For the answer type numerical, each alternative has individual options:
|
||||
if ($answertype == NUMERICAL) {
|
||||
if ($numericaloptions =
|
||||
get_record('quiz_numerical', 'answer', $alt->id))
|
||||
{
|
||||
// Reuse existing numerical options
|
||||
$numericaloptions->min = $altdata->min;
|
||||
$numericaloptions->max = $altdata->max;
|
||||
if (!update_record('quiz_numerical', $numericaloptions)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// New numerical options
|
||||
$numericaloptions->answer = $alt->id;
|
||||
$numericaloptions->question = $questionid;
|
||||
$numericaloptions->min = $altdata->min;
|
||||
$numericaloptions->max = $altdata->max;
|
||||
if (!insert_record("quiz_numerical", $numericaloptions)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else { // Delete obsolete numerical options
|
||||
delete_records('quiz_numerical', 'answer', $alt->id);
|
||||
} // end if NUMERICAL
|
||||
|
||||
$alternativeids[] = $alt->id;
|
||||
} // end foreach $alternatives
|
||||
$answers = implode(',', $alternativeids);
|
||||
|
||||
// Removal of obsolete alternatives from answers and quiz_numerical:
|
||||
while ($altobsolete = array_shift($oldalternatives)) {
|
||||
delete_records("quiz_answers", "id", $altobsolete->id);
|
||||
|
||||
// Possibly obsolute numerical options are also to be deleted:
|
||||
delete_records("quiz_numerical", 'answer', $altobsolete->id);
|
||||
}
|
||||
|
||||
// Common alternative options and removal of obsolete options
|
||||
switch ($answertype) {
|
||||
case NUMERICAL:
|
||||
if (!empty($oldalternativeids)) {
|
||||
delete_records('quiz_shortanswer', 'answers',
|
||||
$oldalternativeids);
|
||||
delete_records('quiz_multichoice', 'answers',
|
||||
$oldalternativeids);
|
||||
}
|
||||
break;
|
||||
case SHORTANSWER:
|
||||
if (!empty($oldalternativeids)) {
|
||||
delete_records('quiz_multichoice', 'answers',
|
||||
$oldalternativeids);
|
||||
$options = get_record('quiz_shortanswer',
|
||||
'answers', $oldalternativeids);
|
||||
} else {
|
||||
unset($options);
|
||||
}
|
||||
if (empty($options)) {
|
||||
// Create new shortanswer options
|
||||
$options->question = $questionid;
|
||||
$options->usecase = 0;
|
||||
$options->answers = $answers;
|
||||
if (!insert_record('quiz_shortanswer', $options)) {
|
||||
return false;
|
||||
}
|
||||
} else if ($answers != $oldalternativeids) {
|
||||
// Shortanswer options needs update:
|
||||
$options->answers = $answers;
|
||||
if (!update_record('quiz_shortanswer', $options)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MULTICHOICE:
|
||||
if (!empty($oldalternativeids)) {
|
||||
delete_records('quiz_shortanswer', 'answers',
|
||||
$oldalternativeids);
|
||||
$options = get_record('quiz_multichoice',
|
||||
'answers', $oldalternativeids);
|
||||
} else {
|
||||
unset($options);
|
||||
}
|
||||
if (empty($options)) {
|
||||
// Create new multichoice options
|
||||
$options->question = $questionid;
|
||||
$options->layout = 0;
|
||||
$options->single = 1;
|
||||
$options->answers = $answers;
|
||||
if (!insert_record('quiz_multichoice', $options)) {
|
||||
return false;
|
||||
}
|
||||
} else if ($answers != $oldalternativeids) {
|
||||
// Multichoice options needs update:
|
||||
$options->answers = $answers;
|
||||
if (!update_record('quiz_multichoice', $options)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return $answers;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -13,28 +13,30 @@
|
||||
|
||||
class quiz_multichoice_qtype extends quiz_default_questiontype {
|
||||
|
||||
function get_answers($question, $addedcondition= '') {
|
||||
// The added condition is one addition that has been added
|
||||
// to the behaviour of this question type in order to make
|
||||
// it embeddable within a multianswer (embedded cloze) question
|
||||
|
||||
global $CFG;
|
||||
|
||||
// There should be multiple answers
|
||||
return get_records_sql("SELECT a.*, mc.single
|
||||
FROM {$CFG->prefix}quiz_multichoice mc,
|
||||
{$CFG->prefix}quiz_answers a
|
||||
WHERE mc.question = '$question->id'
|
||||
AND mc.question = a.question "
|
||||
. $addedcondition);
|
||||
}
|
||||
|
||||
function name() {
|
||||
return 'multichoice';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// Get additional information from database
|
||||
// and attach it to the question object
|
||||
if (!$question->options = get_record('quiz_multichoice', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$question->options->answers = get_records('quiz_answers', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question answers!');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
|
||||
|
||||
if (!$oldanswers = get_records("quiz_answers", "question",
|
||||
$question->id, "id ASC")) {
|
||||
$oldanswers = array();
|
||||
@@ -130,6 +132,85 @@ class quiz_multichoice_qtype extends quiz_default_questiontype {
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
// Shuffle the answers if required
|
||||
$answerids = array_values(array_map(create_function('$val',
|
||||
'return $val->id;'), $question->options->answers));
|
||||
if ($quiz->shuffleanswers) {
|
||||
$answerids = swapshuffle($answerids);
|
||||
}
|
||||
$state->options->order = $answerids;
|
||||
// Create empty responses
|
||||
if ($question->options->single) {
|
||||
$state->responses = array('' => '');
|
||||
} else {
|
||||
$state->responses = array();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
// The serialized format for multiple choice quetsions
|
||||
// is (optionally) a comma separated list of answer ids
|
||||
// followed by a colon, followed by another comma separated
|
||||
// list of answer ids, which are the radio/checkboxes that were
|
||||
// ticked.
|
||||
// E.g. 1,3,2,4:2,4 means that the answers were shown in the order
|
||||
// 1, 3, 2 and then 4 and the answers 2 and 4 were checked.
|
||||
|
||||
$pos = strpos($state->responses[''], ':');
|
||||
if (false === $pos) { // No order of answers is given, so use the default
|
||||
$state->options->order = array_keys($question->options->answers);
|
||||
} else { // Restore the order of the answers
|
||||
$state->options->order = explode(',', substr($state->responses[''], 0,
|
||||
$pos));
|
||||
$state->responses[''] = substr($state->responses[''], $pos + 1);
|
||||
}
|
||||
// Restore the responses
|
||||
if (empty($state->responses[''])) { // No previous responses
|
||||
if ($question->options->single) {
|
||||
$state->responses = array('' => '');
|
||||
} else {
|
||||
$state->responses = array();
|
||||
}
|
||||
} else {
|
||||
if ($question->options->single) {
|
||||
$state->responses = array('' => $state->responses['']);
|
||||
} else {
|
||||
// Get array of answer ids
|
||||
$state->responses = explode(',', $state->responses);
|
||||
// Create an array indexed by these answer ids
|
||||
$state->responses = array_flip($state->responses);
|
||||
// Set the value of each element to be equal to the index
|
||||
array_walk($state->responses, create_function('&$a, $b',
|
||||
'$a = $b;'));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
// Bundle the answer order and the responses into the legacy answer
|
||||
// field.
|
||||
// The serialized format for multiple choice quetsions
|
||||
// is (optionally) a comma separated list of answer ids
|
||||
// followed by a colon, followed by another comma separated
|
||||
// list of answer ids, which are the radio/checkboxes that were
|
||||
// ticked.
|
||||
// E.g. 1,3,2,4:2,4 means that the answers were shown in the order
|
||||
// 1, 3, 2 and then 4 and the answers 2 and 4 were checked.
|
||||
$responses = implode(',', $state->options->order) . ':';
|
||||
$responses .= implode(',', $state->responses);
|
||||
|
||||
// Set the legacy answer field
|
||||
if (!set_field('quiz_states', 'answer', $responses, 'id',
|
||||
$state->id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
// Fetch additional details from the database...
|
||||
if (!$options = get_record("quiz_multichoice",
|
||||
@@ -153,16 +234,33 @@ class quiz_multichoice_qtype extends quiz_default_questiontype {
|
||||
}
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
if ($question->options->single) {
|
||||
foreach ($question->options->answers as $answer) {
|
||||
if (((int) $answer->fraction) === 1) {
|
||||
return array('' => $answer->id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
$responses = array();
|
||||
foreach ($question->options->answers as $answer) {
|
||||
if (((float) $answer->fraction) > 0.0) {
|
||||
$responses[$answer->id] = (string) $answer->id;
|
||||
}
|
||||
}
|
||||
return empty($responses) ? null : $responses;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch additional details from the database...
|
||||
if (!$options = get_record("quiz_multichoice", "question", $question->id)) {
|
||||
notify("Error: Missing question options!");
|
||||
}
|
||||
if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) {
|
||||
notify("Error: Missing question answers!");
|
||||
}
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz, $options) {
|
||||
|
||||
$answers = &$question->options->answers;
|
||||
$correctanswers = $this->get_correct_responses($question, $state);
|
||||
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
|
||||
|
||||
$formatoptions = new stdClass;
|
||||
$formatoptions->para = false;
|
||||
|
||||
// Print formulation
|
||||
echo format_text($question->questiontext,
|
||||
@@ -171,97 +269,106 @@ class quiz_multichoice_qtype extends quiz_default_questiontype {
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
// Print input controls and alternatives
|
||||
echo "<table align=\"right\">";
|
||||
$stranswer = get_string("answer", "quiz");
|
||||
echo '<table align="right">';
|
||||
$stranswer = get_string('answer', 'quiz');
|
||||
echo "<tr><td valign=\"top\">$stranswer: </td><td>";
|
||||
echo "<table>";
|
||||
$answerids = explode(",", $options->answers);
|
||||
echo '<table>';
|
||||
|
||||
if ($quiz->shuffleanswers) {
|
||||
$answerids = swapshuffle($answerids);
|
||||
}
|
||||
|
||||
// Handle the case of unanswered single-choice questions:
|
||||
if ($options->single) {
|
||||
$singleresponse = isset($question->response[$nameprefix])
|
||||
? $question->response[$nameprefix] : '0';
|
||||
}
|
||||
|
||||
foreach ($answerids as $key => $aid) {
|
||||
$answer = $answers[$aid];
|
||||
// Print each answer in a separate row
|
||||
foreach ($state->options->order as $key => $aid) {
|
||||
$answer = &$answers[$aid];
|
||||
$qnumchar = chr(ord('a') + $key);
|
||||
|
||||
echo '<tr><td valign="top">';
|
||||
|
||||
if ($options->single) {
|
||||
$type = ' type="radio" ';
|
||||
$name = " name=\"$nameprefix\" ";
|
||||
$checked = $singleresponse == $aid
|
||||
? ' checked="checked" ' : '';
|
||||
if ($question->options->single) {
|
||||
$type = 'type="radio"';
|
||||
$name = "name=\"{$question->name_prefix}\"";
|
||||
$checked = $aid == $state->responses['']
|
||||
? 'checked="checked"' : '';
|
||||
} else {
|
||||
$type = ' type="checkbox" ';
|
||||
$name = " name=\"$nameprefix$aid\" ";
|
||||
$checked = !empty($question->response[$nameprefix.$aid])
|
||||
? ' checked="checked" ' : '';
|
||||
$name = "name=\"{$question->name_prefix}$aid\"";
|
||||
$checked = isset($state->responses[$aid])
|
||||
? 'checked="checked"' : '';
|
||||
}
|
||||
echo "<input $readonly $name $checked $type value=\"$answer->id\" alt=\"".s($answer->answer)."\" />";
|
||||
|
||||
echo "</td>";
|
||||
|
||||
/// remove para tags - not needed
|
||||
$options->para = false;
|
||||
|
||||
if ($readonly and $quiz->correctanswers and !empty($correctanswers[$nameprefix.$aid])) {
|
||||
echo '<td valign="top" class="highlight">'.format_text("$qnumchar. $answer->answer", FORMAT_MOODLE , $options).'</td>';
|
||||
|
||||
|
||||
// Print the control
|
||||
echo "<input $readonly $name $checked $type value=\"$aid\" alt=\""
|
||||
. s($answer->answer) . '" />';
|
||||
|
||||
echo '</td>';
|
||||
|
||||
// Print the text by the control highlighting if correct responses
|
||||
// should be shown and the current answer is the correct answer in
|
||||
// the single selection case or has a positive score in the multiple
|
||||
// selection case
|
||||
if ($options->readonly && $options->correct_responses &&
|
||||
in_array($aid, $correctanswers)) {
|
||||
echo '<td valign="top" class="highlight">' .
|
||||
format_text("$qnumchar. $answer->answer", FORMAT_MOODLE ,
|
||||
$formatoptions) . '</td>';
|
||||
} else {
|
||||
echo '<td valign="top">'.format_text("$qnumchar. $answer->answer", FORMAT_MOODLE, $options).'</td>';
|
||||
echo '<td valign="top">' .
|
||||
format_text("$qnumchar. $answer->answer", FORMAT_MOODLE,
|
||||
$formatoptions) . '</td>';
|
||||
}
|
||||
if ($quiz->feedback) {
|
||||
echo "<td valign=\"top\"> ";
|
||||
if ($checked) { // Simpliest condition to use here
|
||||
quiz_print_comment($answer->feedback);
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
// Print feedback by selected options if feedback is on
|
||||
if (($options->feedback || $options->correct_responses) &&
|
||||
$checked) {
|
||||
echo '<td valign="top"> ';
|
||||
quiz_print_comment($answer->feedback);
|
||||
echo '</td>';
|
||||
}
|
||||
echo "</tr>";
|
||||
|
||||
echo '</tr>';
|
||||
}
|
||||
echo "</table>";
|
||||
echo "</td></tr></table>";
|
||||
echo '</table>';
|
||||
echo '</td></tr></table>';
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix, $addedanswercondition='') {
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
$answers = $question->options->answers;
|
||||
$testedstate = clone($state);
|
||||
$teststate = clone($state);
|
||||
|
||||
$result->correctanswers = array();
|
||||
$result->answers = array();
|
||||
$result->grade = 0.0;
|
||||
|
||||
$answers = $this->get_answers($question, $addedanswercondition);
|
||||
|
||||
/// Set ->answers[] and ->grade
|
||||
if (!empty($question->response)) {
|
||||
foreach ($question->response as $name => $response) {
|
||||
if (isset($answers[$response])) {
|
||||
$result->answers[$name] = $answers[$response];
|
||||
$result->grade += $answers[$response]->fraction;
|
||||
foreach($answers as $answer) {
|
||||
$teststate->responses[''] = $answer->id;
|
||||
if($question->options->single) {
|
||||
if($this->compare_responses($question, $testedstate,
|
||||
$teststate)) {
|
||||
$state->raw_grade = $answer->fraction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set ->correctanswers[]
|
||||
foreach ($answers as $answer) {
|
||||
|
||||
if ($answer->single) {
|
||||
$result->correctanswers =
|
||||
quiz_extract_correctanswers($answers, $nameprefix);
|
||||
break;
|
||||
|
||||
} else {
|
||||
if ($answer->fraction > 0.0) {
|
||||
$result->correctanswers[$nameprefix.$answer->id] = $answer;
|
||||
foreach($state->responses as $response) {
|
||||
$testedstate->responses = array('' => $response);
|
||||
if($this->compare_responses($question, $testedstate,
|
||||
$teststate)) {
|
||||
$state->raw_grade += $answer->fraction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
if (empty($state->raw_grade)) {
|
||||
$state->raw_grade = 0;
|
||||
}
|
||||
|
||||
// Make sure we don't assign negative or too high marks
|
||||
$state->raw_grade = min(max((float) $state->raw_grade,
|
||||
0.0), 1.0) * $question->maxgrade;
|
||||
// Apply the penalty for this attempt
|
||||
$state->penalty = 0;
|
||||
// Only allow one attempt at the question
|
||||
$state->event = QUIZ_EVENTCLOSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
function print_question_grading_details(&$question, &$state, $quiz, $options) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
@@ -1,83 +1,24 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This will only support one answer of the type NUMERICAL
|
||||
// However, lib.php has support for multiple answers
|
||||
if (!empty($question->id)) {
|
||||
$answersraw= quiz_get_answers($question);
|
||||
}
|
||||
$answers= array();
|
||||
for ($i=0; $i<6; $i++) {
|
||||
$answers[$i]->answer = ""; // Make answer slots, default as blank...
|
||||
$answers[$i]->min = "";
|
||||
$answers[$i]->max = "";
|
||||
$answers[$i]->feedback = "";
|
||||
}
|
||||
if (!empty($answersraw)) {
|
||||
$i=0;
|
||||
foreach ($answersraw as $answer) {
|
||||
$answers[$i] = $answer;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
$units = array();
|
||||
for ($i=0 ; $i<6 ; $i++) {
|
||||
$units[$i]->multiplier = ''; // Make unit slots, default as blank...
|
||||
$units[$i]->unit = '';
|
||||
}
|
||||
if (!empty($question->id) and $unitsraw = get_records(
|
||||
'quiz_numerical_units', 'question', $question->id)) {
|
||||
/// Find default unit and have it put in the zero slot
|
||||
/// This procedure might be overridden later when
|
||||
/// the unit is stripped form an answer...
|
||||
foreach ($unitsraw as $key => $unit) {
|
||||
if (1.0 == $unit->multiplier) {
|
||||
/// Default unit found:
|
||||
$units[0] = $unit;
|
||||
unset($unitsraw[$key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// Fill remaining answer slots with whatsever left
|
||||
if (!empty($unitsraw)) {
|
||||
$i = 1; // The zero slot got the default unit...
|
||||
foreach ($unitsraw as $unit) {
|
||||
$units[$i] = $unit;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
// Get all the extra information if we're editing
|
||||
if (!empty($question->id) && isset($question->qtype) &&
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question)) {
|
||||
$answer = array_values($question->options->answers);
|
||||
usort($answer, create_function('$a, $b',
|
||||
'if ($a->fraction == $a->fraction) { return 0; }' .
|
||||
'else { return $a->fraction < $b->fraction ? -1 : 1; }'));
|
||||
$answer = $answer[0]; // Get the answer with the highest fraction (i.e. 1)
|
||||
$units = array_values($question->options->units);
|
||||
usort($units, create_function('$a, $b', // make sure the default unit is at index 0
|
||||
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
|
||||
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
|
||||
$tolerance = $question->options->tolerance;
|
||||
} else {
|
||||
$units[0]->multiplier = 1.0;
|
||||
}
|
||||
|
||||
// Strip unit from answers, if they have any:
|
||||
foreach ($units as $key => $unit) {
|
||||
if (1.0 == $unit->multiplier && $unit->unit) {
|
||||
$ukey = $key;
|
||||
// Possible default unit found:
|
||||
foreach ($answers as $i => $answer) {
|
||||
if (($answer->min || $answer->max) && ereg(
|
||||
"^(.*)$unit->unit$", $answer->answer, $numreg)) {
|
||||
$answers[$i]->answer = $numreg[1];
|
||||
if (0!=$ukey) {
|
||||
// Make unit default by putting it first:
|
||||
$units[$ukey] = $units[0];
|
||||
$units[0] = $unit;
|
||||
$ukey = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip trailing zeros from multipliers
|
||||
foreach ($units as $i => $unit) {
|
||||
if (ereg('^(.*\\..(.*[^0])?)0+$', $unit->multiplier, $regs1)) {
|
||||
if (ereg('^(.+)\\.0$', $regs1[1], $regs2)) {
|
||||
$units[$i]->multiplier = $regs2[1];
|
||||
} else {
|
||||
$units[$i]->multiplier = $regs1[1];
|
||||
}
|
||||
}
|
||||
$answer = new stdClass;
|
||||
$answer->answer = '';
|
||||
$answer->feedback = '';
|
||||
$units = array();
|
||||
$tolerance = '';
|
||||
}
|
||||
|
||||
print_heading_with_help(get_string("editingnumerical", "quiz"), "numerical", "quiz");
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($err["questiontext"])) {
|
||||
formerr($err["questiontext"]);
|
||||
formerr($err["questiontext"]);
|
||||
echo "<br />";
|
||||
}
|
||||
|
||||
@@ -67,44 +67,36 @@
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("correctanswer", "quiz") ?>:</b></td>
|
||||
<?php
|
||||
// Even thou the rest of the module can handle up to six numerical answers,
|
||||
// this form will limit the number of numerical answers to one only.
|
||||
if (is_numeric($answers[0]->min) && is_numeric($answers[0]->answer)) {
|
||||
$acceptederror = (float)($answers[0]->answer)
|
||||
- (float)($answers[0]->min);
|
||||
} else {
|
||||
$acceptederror = "";
|
||||
}
|
||||
?>
|
||||
<td>
|
||||
<input align="left" type="text" id="correct0" name="answer[]" size="20" value="<?php p($answers[0]->answer) ?>" alt="<?php print_string("correctanswer", "quiz") ?>" />
|
||||
<input align="left" type="text" id="correct0" name="answer[]" size="10" value="<?php p($answer->answer) ?>" alt="<?php print_string("correctanswer", "quiz") ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("acceptederror", "quiz"); ?>:</b></td>
|
||||
<td>
|
||||
<input align="left" type="text" id="acceptederror0" name="acceptederror[]" size="15" value="<?php p($acceptederror) ?>" alt="<?php print_string("acceptederror", "quiz"); ?>" />±
|
||||
<!-- Values max and min will be determined when the form is submitted -->
|
||||
<input type="hidden" id="min0" name="min[]" value="" />
|
||||
<input type="hidden" id="max0" name="max[]" value="" />
|
||||
<input align="left" type="text" id="acceptederror0" name="tolerance" size="10" value="<?php p($tolerance); ?>" alt="<?php print_string("acceptederror", "quiz"); ?>" />±
|
||||
<input type="hidden" name="fraction[]" value="1" />
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("feedback", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<textarea name="feedback[]" rows="2" cols="50"><?php p($answers[0]->feedback) ?></textarea>
|
||||
<textarea name="feedback[]" rows="2" cols="50"><?php p($answer->feedback); ?></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("unit", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<input type="hidden" name="multiplier[]" value="1.0" />
|
||||
<input align="left" type="text" id="defaultunit" name="unit[]"
|
||||
size="5" value="<?php p($units[0]->unit) ?>" alt="<?php print_string("unit", "quiz") ?>" />
|
||||
<b>(<?php print_string("optional", "quiz") ?>)</b>
|
||||
<td><?php
|
||||
if (isset($units[0]) && 1.0 === (float)$units[0]->multiplier) {
|
||||
$unit = $units[0]->unit;
|
||||
} else {
|
||||
$unit = '';
|
||||
}
|
||||
$multiplier = '1.0';
|
||||
?>
|
||||
<input type="hidden" name="multiplier[]" value="<?php p($multiplier) ?>" />
|
||||
<input align="left" type="text" id="defaultunit" name="unit[]" size="10" value="<?php p($unit) ?>" alt="<?php print_string("unit", "quiz") ?>" />
|
||||
<b>(<?php print_string("optional", "quiz") ?>)</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
@@ -113,66 +105,31 @@
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php
|
||||
for ($i=1; $i<count($units); $i++) {
|
||||
$unit=$units[$i];
|
||||
for ($i = 1; $i < 6; $i++) {
|
||||
if (isset($units[$i])) {
|
||||
$multiplier = (float)$units[$i]->multiplier;
|
||||
$unit = $units[$i]->unit;
|
||||
} else {
|
||||
$multiplier = '';
|
||||
$unit = '';
|
||||
}
|
||||
?>
|
||||
<tr valign="top">
|
||||
<td></td>
|
||||
<td align="left">
|
||||
<b><?php print_string("multiplier", "quiz") ?>:</b>
|
||||
<input type="text" id="<?php p("multiplier$i") ?>" size="10"
|
||||
align="right" name="multiplier[]"
|
||||
value="<?php p($unit->multiplier) ?>"
|
||||
alt="<?php print_string("multiplier", "quiz") ?>" />
|
||||
<b> <?php print_string("unit", "quiz") ?>:</b>
|
||||
<input align="left" type="text" id="<?php p("unit$i") ?>"
|
||||
name="unit[]"
|
||||
size="5" value="<?php p($unit->unit) ?>" />
|
||||
<input type="text" size="10" align="right" name="multiplier[]" value="<?php p($multiplier) ?>"
|
||||
alt="<?php print_string("multiplier", "quiz") ?>" />
|
||||
<b> <?php p("unit", "quiz") ?>:</b>
|
||||
<input align="left" type="text" name="unit[]" size="5" value="<?php p($unit) ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
} /// END for
|
||||
|
||||
$QUIZ_QTYPES[$question->qtype]->print_replacement_options($question, $course, $contextquiz);
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question_form_end($question, 'onclick="return determineMinAndMax();"');
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question_form_end($question);
|
||||
?>
|
||||
</table>
|
||||
|
||||
</center>
|
||||
</form>
|
||||
<script language="javascript" type="text/javascript">
|
||||
function determineMinAndMax() {
|
||||
// This client-side script will determine the values for min and max
|
||||
// based on the input for answer and acceptederror.
|
||||
with(document.theform) {
|
||||
if (correct0.value=='') {
|
||||
alert('<?php print_string("missingcorrectanswer","quiz") ?>');
|
||||
return false;
|
||||
} else if (isNaN(correct0.value) && defaultunit.value) {
|
||||
alert('<?php print_string("unitonlyworksfornumericanswers","quiz") ?>');
|
||||
return false;
|
||||
} else if (''!=defaultunit.value && !isNaN(defaultunit.value)) {
|
||||
alert('<?php print_string("unitmustnotbenumeric","quiz") ?>');
|
||||
return false;
|
||||
} else if (acceptederror0.value=='') {
|
||||
var correct= parseFloat(correct0.value);
|
||||
if (!isNaN(correct)) {
|
||||
min0.value= correct;
|
||||
max0.value= correct;
|
||||
correct0.value += defaultunit.value;
|
||||
}
|
||||
return true;
|
||||
} else if (isNaN(acceptederror0.value) || isNaN(correct0.value)) {
|
||||
alert('<?php print_string("answerswithacceptederrormarginmustbenumeric", "quiz") ?>');
|
||||
return false;
|
||||
} else {
|
||||
var correct= parseFloat(correct0.value);
|
||||
var error= Math.abs(acceptederror0.value);
|
||||
min0.value= correct-error;
|
||||
max0.value= correct+error;
|
||||
correct0.value += defaultunit.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -18,223 +18,278 @@ require_once("$CFG->dirroot/mod/quiz/questiontypes/shortanswer/questiontype.php"
|
||||
|
||||
class quiz_numerical_qtype extends quiz_shortanswer_qtype {
|
||||
|
||||
function get_answers($question, $addedcondition='') {
|
||||
// The added condition is one addition that has been added
|
||||
// to the behaviour of this question type in order to make
|
||||
// it embeddable within a multianswer (embedded cloze) question
|
||||
|
||||
global $CFG;
|
||||
|
||||
// There can be multiple answers
|
||||
return get_records_sql("SELECT a.*, n.min, n.max
|
||||
FROM {$CFG->prefix}quiz_numerical n,
|
||||
{$CFG->prefix}quiz_answers a
|
||||
WHERE a.question = '$question->id'
|
||||
AND n.answer = a.id "
|
||||
. $addedcondition);
|
||||
}
|
||||
|
||||
function name() {
|
||||
return 'numerical';
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
function get_question_options(&$question) {
|
||||
if (!$question->options->answers = get_records('quiz_answers',
|
||||
'question', $question->id)) {
|
||||
notify('Error: Missing question answer!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
|
||||
if (false === ($question->options->tolerance = get_field('quiz_numerical',
|
||||
'tolerance', 'question', $question->id))) {
|
||||
$question->options->tolerance = 0;
|
||||
}
|
||||
$this->get_numerical_units($question);
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_numerical_units(&$question) {
|
||||
if ($question->options->units = get_records('quiz_numerical_units',
|
||||
'question', $question->id, 'id ASC')) {
|
||||
$question->options->units = array_values($question->options->units);
|
||||
usort($question->options->units, create_function('$a, $b', // make sure the default unit is at index 0
|
||||
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
|
||||
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
|
||||
array_walk($question->options->units, create_function('$val',
|
||||
'$val->multiplier = (float)$val->multiplier;'));
|
||||
} else {
|
||||
$question->options->units = array();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_default_numerical_unit(&$question) {
|
||||
if (!isset($question->options->units[0])) {
|
||||
return '';
|
||||
} else if (1.0 === (float)$question->options->units[0]->multiplier) {
|
||||
return $question->options->units[0];
|
||||
} else {
|
||||
foreach ($question->options->units as $unit) {
|
||||
if (1.0 === (float)$unit->multiplier) {
|
||||
return $unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
// save_question_options supports the definition of multiple answers
|
||||
// for numerical questions. This is not currently used by the editing
|
||||
// interface, but the GIFT format supports it. The multianswer qtype,
|
||||
// for example can make use of this feature.
|
||||
|
||||
// Get old versions of the objects
|
||||
if (!$oldanswers = get_records("quiz_answers", "question", $question->id)) {
|
||||
$oldanswers = array();
|
||||
}
|
||||
|
||||
$answers = array();
|
||||
$maxfraction = -1;
|
||||
if (!$oldoptions = get_record("quiz_numerical", "question", $question->id)) {
|
||||
$oldoptions = new stdClass;
|
||||
}
|
||||
|
||||
$result = $this->save_numerical_units($question);
|
||||
if (isset($result->error)) {
|
||||
return $result;
|
||||
} else {
|
||||
$units = &$result->units;
|
||||
}
|
||||
$answerids = array();
|
||||
// Insert all the new answers
|
||||
foreach ($question->answer as $key => $dataanswer) {
|
||||
if ($dataanswer != "") {
|
||||
$answer = new stdClass;
|
||||
$answer->question = $question->id;
|
||||
$answer->answer = trim($dataanswer);
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = trim($question->feedback[$key]);
|
||||
if (!is_numeric($answer->answer)) {
|
||||
$result->error = "The answer '$answer->answer' is not numeric!";
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
|
||||
$answer = $oldanswer;
|
||||
$answer->answer = $dataanswer;
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!update_record("quiz_answers", $answer)) {
|
||||
$answer->id = $oldanswer->id;
|
||||
if (! update_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not update quiz answer! (id=$answer->id)";
|
||||
return $result;
|
||||
}
|
||||
} else { // This is a completely new answer
|
||||
unset($answer);
|
||||
$answer->answer = $dataanswer;
|
||||
$answer->question = $question->id;
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!$answer->id = insert_record("quiz_answers", $answer)) {
|
||||
if (! $answer->id = insert_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not insert quiz answer!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$answers[] = $answer->id;
|
||||
if ($question->fraction[$key] > $maxfraction) {
|
||||
$maxfraction = $question->fraction[$key];
|
||||
}
|
||||
|
||||
if ($options = get_record("quiz_numerical", "answer", $answer->id)) {
|
||||
$options->min= $question->min[$key];
|
||||
$options->max= $question->max[$key];
|
||||
if (!update_record("quiz_numerical", $options)) {
|
||||
$result->error = "Could not update quiz numerical options! (id=$options->id)";
|
||||
return $result;
|
||||
}
|
||||
} else { // completely new answer
|
||||
unset($options);
|
||||
$options->question = $question->id;
|
||||
$options->answer = $answer->id;
|
||||
$options->min = $question->min[$key];
|
||||
$options->max = $question->max[$key];
|
||||
if (!insert_record("quiz_numerical", $options)) {
|
||||
$result->error = "Could not insert quiz numerical options!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$answerids[] = $answer->id;
|
||||
}
|
||||
}
|
||||
|
||||
/// Save units
|
||||
/// Make unit records
|
||||
$newunits = array();
|
||||
unset($tmpunit);
|
||||
$tmpunit->question = $question->id;
|
||||
foreach ($question->multiplier as $key => $multiplier) {
|
||||
if ($multiplier && is_numeric($multiplier)) {
|
||||
$tmpunit->multiplier = $multiplier;
|
||||
$tmpunit->unit = trim($question->unit[$key]);
|
||||
$newunits[] = $tmpunit;
|
||||
// Set up the options object
|
||||
$options = clone($oldoptions);
|
||||
$options->question = $question->id;
|
||||
$options->answers = implode(',', $answerids);
|
||||
$options->tolerance = $this->apply_unit($question->tolerance, $units);
|
||||
|
||||
// Save options
|
||||
if (isset($oldoptions->id)) { // options have changed
|
||||
$options->id = $oldoptions->id;
|
||||
if (! update_record('quiz_numerical', $options)) {
|
||||
$result->error = "Could not update quiz numerical options! (id=$options->id)";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
if (1 == count($newunits) && !$newunits[0]->unit) {
|
||||
/// Only default unit and it is empty, so drop it:
|
||||
$newunits = array();
|
||||
}
|
||||
if ($oldunits = get_records('quiz_numerical_units',
|
||||
'question', $question->id)) {
|
||||
foreach ($oldunits as $unit) {
|
||||
if ($newunit = array_shift($newunits)) {
|
||||
$unit->multiplier = $newunit->multiplier;
|
||||
$unit->unit = $newunit->unit;
|
||||
if (!update_record('quiz_numerical_units', $unit)) {
|
||||
$result->error = "Could not update quiz_numerical_unit $unit->unit";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
delete_records('quiz_numerical_units', 'id', $unit->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($newunits as $newunit) {
|
||||
// Create new records for the remaining units:
|
||||
if (!insert_record('quiz_numerical_units', $newunit)) {
|
||||
$result->error = "Unable to insert new unit $newunit->unit";
|
||||
} else { // new options
|
||||
if (! insert_record('quiz_numerical', $options)) {
|
||||
$result->error = "Could not insert quiz numerical options!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform sanity checks on fractional grades
|
||||
if ($maxfraction != 1) {
|
||||
$maxfraction = $maxfraction * 100;
|
||||
$result->noticeyesno = get_string("fractionsnomax", "quiz", $maxfraction);
|
||||
return $result;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix, $addedanswercondition='') {
|
||||
|
||||
$result->answers = array();
|
||||
$units = get_records('quiz_numerical_units',
|
||||
'question', $question->id);
|
||||
if (isset($question->response[$nameprefix])) {
|
||||
$response = trim(stripslashes($question->response[$nameprefix]));
|
||||
// Arrays with 'wild cards':
|
||||
$search = array(' ',',');
|
||||
$replace = array('','.');
|
||||
$responsenum = str_replace($search, $replace, $response);
|
||||
if (empty($units)) {
|
||||
if ('' !== $responsenum && is_numeric($responsenum)) {
|
||||
$responsenum = (float)$responsenum;
|
||||
} else {
|
||||
unset($responsenum); // Answer is not numeric
|
||||
}
|
||||
} else if (ereg(
|
||||
'^([+-]?([0-9]+(\\.[0-9]*)?|[.][0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$',
|
||||
$responsenum, $responseparts)) {
|
||||
$responsenum = (float)$responseparts[1];
|
||||
if ($responseparts[5]) {
|
||||
$responseunit = $responseparts[5];
|
||||
} else {
|
||||
$responseunit = '';
|
||||
}
|
||||
$unitidentified = false;
|
||||
foreach ($units as $unit) {
|
||||
if (str_replace($search, $replace, $unit->unit)
|
||||
== $responseunit) {
|
||||
$unitidentified = true;
|
||||
$responsenum /= $unit->multiplier;
|
||||
break;
|
||||
function save_numerical_units($question) {
|
||||
if (!$oldunits = get_records("quiz_numerical_units", "question", $question->id)) {
|
||||
$oldunits = array();
|
||||
}
|
||||
|
||||
// Set the units
|
||||
$units = array();
|
||||
$keys = array();
|
||||
$oldunits = array_values($oldunits);
|
||||
usort($oldunits, create_function('$a, $b', // make sure the default unit is at index 0
|
||||
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
|
||||
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
|
||||
foreach ($oldunits as $unit) {
|
||||
$units[] = clone($unit);
|
||||
}
|
||||
$n = isset($question->multiplier) ? count($question->multiplier) : 0;
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
// Discard any unit which doesn't specify the unit or the multiplier
|
||||
if (!empty($question->multiplier[$i]) && !empty($question->unit[$i])) {
|
||||
$units[$i]->question = $question->id;
|
||||
$units[$i]->multiplier =
|
||||
$this->apply_unit($question->multiplier[$i], array());
|
||||
$units[$i]->unit = $question->unit[$i];
|
||||
} else {
|
||||
unset($units[$i]);
|
||||
}
|
||||
}
|
||||
unset($question->multiplier, $question->unit);
|
||||
|
||||
/// Save units
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
if (!isset($units[$i]) && isset($oldunits[$i])) { // Delete if it hasn't been resubmitted
|
||||
delete_records('quiz_numerical_units', 'id', $oldunits[$i]->id);
|
||||
} else if ($oldunits != $units) { // answer has changed or is new
|
||||
if (isset($oldunits[$i]->id)) { // answer has changed
|
||||
$units[$i]->id = $oldunits[$i]->id;
|
||||
if (! update_record('quiz_numerical_units', $units[$i])) {
|
||||
$result->error = "Could not update quiz_numerical_unit $units[$i]->unit";
|
||||
return $result;
|
||||
}
|
||||
} else if (isset($units[$i])) { // answer is new
|
||||
if (! insert_record('quiz_numerical_units', $units[$i])) {
|
||||
$result->error = "Unable to insert new unit $units[$i]->unit";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
if (!$unitidentified) {
|
||||
unset($responsenum); // No unit OK
|
||||
}
|
||||
} else {
|
||||
unset($responsenum); // Answer is not numeric
|
||||
}
|
||||
} else {
|
||||
}
|
||||
$result->units = &$units;
|
||||
return $result;
|
||||
}
|
||||
|
||||
function compare_responses(&$question, &$state, &$teststate) {
|
||||
if (isset($state->responses[''])) {
|
||||
$response = $this->apply_unit(stripslashes($state->responses['']),
|
||||
$question->options->units);
|
||||
} else {
|
||||
$response = '';
|
||||
}
|
||||
$answers = $this->get_answers($question, $addedanswercondition);
|
||||
foreach ($answers as $answer) {
|
||||
|
||||
/// Check if response matches answer...
|
||||
if ('' !== $response and empty($result->answers)
|
||||
|| $answer->fraction
|
||||
> $result->answers[$nameprefix]->fraction
|
||||
and strtolower($response) == strtolower($answer->answer)
|
||||
|| '' != trim($answer->min) && isset($responsenum)
|
||||
&& $responsenum >= (float)$answer->min
|
||||
&& $responsenum <= (float)$answer->max) {
|
||||
$result->answers[$nameprefix] = $answer;
|
||||
}
|
||||
if (isset($teststate->responses[''])) {
|
||||
$testresponse =
|
||||
$this->apply_unit(stripslashes($teststate->responses['']),
|
||||
$question->options->units);
|
||||
} else {
|
||||
$testresponse = '';
|
||||
}
|
||||
|
||||
$result->grade = isset($result->answers[$nameprefix])
|
||||
? $result->answers[$nameprefix]->fraction
|
||||
: 0.0;
|
||||
$result->correctanswers = quiz_extract_correctanswers($answers,
|
||||
$nameprefix);
|
||||
$this->get_tolerance_interval($question, $teststate);
|
||||
// We need to add a tiny fraction (0.00000000000000001) to make the
|
||||
// comparison work correctly. Otherwise seemingly equal values can yield
|
||||
// false.
|
||||
$tolerance = (float)$question->options->tolerance + 0.00000000000000001;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// For numerical answer we have the policy to
|
||||
// set feedback for any response, even if the
|
||||
// response does not entitle the student to it.
|
||||
/////////////////////////////////////////////////
|
||||
if ('' !== $response and empty($result->answers)
|
||||
|| empty($result->answers[$nameprefix]->feedback)) {
|
||||
// Look for just any feedback:
|
||||
foreach ($result->correctanswers as $correctanswer) {
|
||||
if ($correctanswer->feedback) {
|
||||
$result->answers[$nameprefix]->feedback =
|
||||
$correctanswer->feedback;
|
||||
if (empty($result->answers[$nameprefix]->id)) {
|
||||
// Better fake an answer as well:
|
||||
$result->answers[$nameprefix]->id = 0;
|
||||
$result->answers[$nameprefix]->answer = $response;
|
||||
$result->answers[$nameprefix]->fraction = 0.0;
|
||||
$result->answers[$nameprefix]->question = $question->id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (is_numeric($response) && is_numeric($testresponse)) {
|
||||
if ($teststate->options->min <= $response
|
||||
&& $teststate->options->max >= $response) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
function get_tolerance_interval(&$question, &$state) {
|
||||
$answer = (float)$state->responses[''];
|
||||
if (empty($question->options->tolerance)) {
|
||||
$state->options->min = $state->options->max = $answer;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($question->options->tolerancetype)) {
|
||||
$question->options->tolerancetype = 2; // nominal
|
||||
}
|
||||
$tolerance = (float)$question->options->tolerance;
|
||||
switch ($question->options->tolerancetype) {
|
||||
case '1': case 'relative':
|
||||
/// Recalculate the tolerance and fall through
|
||||
/// to the nominal case:
|
||||
$tolerance = $answer * $tolerance;
|
||||
// Falls through to the nominal case -
|
||||
case '2': case 'nominal':
|
||||
$tolerance = abs($tolerance); // important - otherwise min and max are swapped
|
||||
$max = $answer + $tolerance;
|
||||
$min = $answer - $tolerance;
|
||||
break;
|
||||
case '3': case 'geometric':
|
||||
$quotient = 1 + abs($tolerance);
|
||||
$max = $answer * $quotient;
|
||||
$min = $answer / $quotient;
|
||||
break;
|
||||
default:
|
||||
error("Unknown tolerance type $question->options->tolerancetype");
|
||||
}
|
||||
|
||||
$state->options->min = $min;
|
||||
$state->options->max = $max;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the $rawresponse has a unit and applys it if appropriate.
|
||||
*
|
||||
* @param string $rawresponse The response string to be converted to a float.
|
||||
* @param array $units An array with the defined units, where the
|
||||
* unit is the key and the multiplier the value.
|
||||
* @return float The rawresponse with the unit taken into
|
||||
* account as a float.
|
||||
*/
|
||||
function apply_unit($rawresponse, $units) {
|
||||
if (!is_numeric($rawresponse)) {
|
||||
return $rawresponse;
|
||||
}
|
||||
|
||||
// Make units more useful
|
||||
$tmpunits = array();
|
||||
foreach ($units as $unit) {
|
||||
$tmpunits[$unit->unit] = $unit->multiplier;
|
||||
}
|
||||
|
||||
$search = array(' ', ',');
|
||||
$replace = array('', '.');
|
||||
$rawresponse = str_replace($search, $replace, $rawresponse); // remove spaces
|
||||
if (ereg(
|
||||
'^([+-]?([0-9]+(\\.[0-9]*)?|[.][0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$',
|
||||
$rawresponse, $responseparts)) {
|
||||
$responsenum = (float)$responseparts[1];
|
||||
if (isset($tmpunits[$responseparts[5]])) {
|
||||
return (float)$responseparts[1]*$tmpunits[$responseparts[5]];
|
||||
} else {
|
||||
return (float)$responseparts[1];
|
||||
}
|
||||
}
|
||||
return (float)$rawresponse;
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
@@ -23,31 +23,215 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
return 'random';
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
/// No options to be saved for this question type:
|
||||
function get_question_options(&$question) {
|
||||
// Don't do anything here, because the random question has no options.
|
||||
// Everything is handled by the create- or restore_session_and_responses
|
||||
// functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
function wrapped_questions($question) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
foreach ($question->response as $key => $response) {
|
||||
if (ereg('[^0-9][0-9]+random$', $key)) {
|
||||
$randomquestion = get_record('quiz_questions',
|
||||
'id', $response);
|
||||
$randomquestion->response = $question->response;
|
||||
unset($randomquestion->response[$key]);
|
||||
if ($subwrapped = $QUIZ_QTYPES[$randomquestion->qtype]
|
||||
->wrapped_questions($randomquestion)) {
|
||||
return "$response,$subwrapped";
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
function save_question_options($question) {
|
||||
// No options, but we use the parent field to hide random questions.
|
||||
// To avoid problems we set the parent field to the question id.
|
||||
return (set_field('quiz_questions', 'parent', $question->id, 'id',
|
||||
$question->id) ? true : false);
|
||||
}
|
||||
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
// Choose a random question from the category:
|
||||
// We need to make sure that no question is used more than once in the
|
||||
// quiz. Therfore the following need to be excluded:
|
||||
// 1. All questions that are explicitly assigned to the quiz
|
||||
// 2. All random questions
|
||||
// 3. All questions that are already chosen by an other random question
|
||||
if (!isset($quiz->questionsinuse)) {
|
||||
$quiz->questionsinuse = $quiz->questions;
|
||||
}
|
||||
|
||||
if (!isset($this->catrandoms[$question->category])) {
|
||||
// Need to fetch random questions from category $question->category"
|
||||
// (Note: $this refers to the questiontype, not the question.)
|
||||
global $CFG;
|
||||
$possiblerandomqtypes = "'"
|
||||
. implode("','", $this->possiblerandomqtypes) . "'";
|
||||
if ($question->questiontext == "1") {
|
||||
// recurse into subcategories
|
||||
$categorylist = quiz_categorylist($question->category);
|
||||
} else {
|
||||
$categorylist = $question->category;
|
||||
}
|
||||
$this->catrandoms[$question->category] = get_records_sql
|
||||
("SELECT * FROM {$CFG->prefix}quiz_questions
|
||||
WHERE category IN ($categorylist)
|
||||
AND parent = '0'
|
||||
AND id NOT IN ($quiz->questionsinuse)
|
||||
AND qtype IN ($possiblerandomqtypes)");
|
||||
$this->catrandoms[$question->category] =
|
||||
draw_rand_array($this->catrandoms[$question->category],
|
||||
count($this->catrandoms[$question->category])); // from bug 1889
|
||||
}
|
||||
|
||||
while ($wrappedquestion =
|
||||
array_pop($this->catrandoms[$question->category])) {
|
||||
if (!ereg("(^|,)$wrappedquestion->id(,|$)", $quiz->questionsinuse)) {
|
||||
/// $randomquestion is not in use and will therefore be used
|
||||
/// as the randomquestion here...
|
||||
|
||||
global $QUIZ_QTYPES;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion);
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->create_session_and_responses($wrappedquestion, $state, $quiz,
|
||||
$attempt);
|
||||
$wrappedquestion->name_prefix = $question->name_prefix;
|
||||
$wrappedquestion->maxgrade = $question->maxgrade;
|
||||
$quiz->questionsinuse .= ",$wrappedquestion->id";
|
||||
|
||||
$state->options->question = &$wrappedquestion;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
notify(get_string('toomanyrandom', 'quiz', $question->category));
|
||||
return false;
|
||||
}
|
||||
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
if (!ereg('^random([0-9]+)-(.*)$', $state->responses[''], $answerregs)) {
|
||||
notify("The answer value '{$state->responses['']}' for the state with "
|
||||
."id=$state->id to the random question "
|
||||
."$question->id is malformated."
|
||||
." - No response can be extracted!");
|
||||
return false;
|
||||
}
|
||||
$state->responses[''] = $answerregs[2];
|
||||
|
||||
if (!$wrappedquestion = get_record('quiz_questions', 'id', $answerregs[1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->restore_session_and_responses($wrappedquestion, $state)) {
|
||||
return false;
|
||||
}
|
||||
$wrappedquestion->name_prefix = $question->name_prefix;
|
||||
$wrappedquestion->maxgrade = $question->maxgrade;
|
||||
|
||||
$state->options->question = &$wrappedquestion;
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
|
||||
// Trick the wrapped question into pretending to be the random one.
|
||||
$realqid = $wrappedquestion->id;
|
||||
$wrappedquestion->id = $question->id;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->save_session_and_responses($wrappedquestion, $state);
|
||||
|
||||
// Read what the wrapped question has just set the answer field to
|
||||
// (if anything)
|
||||
$response = get_field('quiz_states', 'answer', 'id', $state->id);
|
||||
if(false === $response) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefix the answer field...
|
||||
$response = "random$realqid-$response";
|
||||
|
||||
// ... and save it again.
|
||||
if (!set_field('quiz_states', 'answer', $response, 'id', $state->id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Restore the real id
|
||||
$wrappedquestion->id = $realqid;
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_correct_responses($wrappedquestion, $state);
|
||||
}
|
||||
|
||||
function print_question(&$question, &$state, &$number, $quiz, $options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question($wrappedquestion, $state, $number, $quiz, $options);
|
||||
}
|
||||
/*
|
||||
function print_question_grading_details(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_grading_details($wrappedquestion, $state, $quiz,
|
||||
$options);
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_formulation_and_controls($wrappedquestion, $state,
|
||||
$quiz, $options);
|
||||
}
|
||||
|
||||
function print_question_submit_buttons(&$question, &$state, $quiz,
|
||||
$options) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_submit_buttons($wrappedquestion, $state, $quiz,
|
||||
$options);
|
||||
}
|
||||
*/
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->grade_responses($wrappedquestion, $state, $quiz);
|
||||
}
|
||||
|
||||
function get_texsource(&$question, &$state, $quiz, $type) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_texsource($wrappedquestion, $state, $quiz, $type);
|
||||
}
|
||||
|
||||
function compare_responses(&$question, $state, $teststate) {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->compare_responses($wrappedquestion, $state, $teststate);
|
||||
}
|
||||
|
||||
function print_replacement_options($question, $course, $quizid='0') {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_replacement_options($wrappedquestion, $state, $quizid);
|
||||
}
|
||||
|
||||
function print_question_form_end($question, $submitscript='') {
|
||||
global $QUIZ_QTYPES;
|
||||
$wrappedquestion = &$state->options->question;
|
||||
return $QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->print_question_form_end($wrappedquestion, $state, $quizid);
|
||||
}
|
||||
/*
|
||||
function convert_to_response_answer_field($questionresponse) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
@@ -64,52 +248,69 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function create_response($question, $nameprefix, $questionsinuse) {
|
||||
// It's for question types like RANDOMSAMATCH and RANDOM that
|
||||
// the true power of the pattern with this function comes to the surface.
|
||||
|
||||
global $CFG;
|
||||
|
||||
if (!isset($this->catrandoms[$question->category])) {
|
||||
//Need to fetch random questions from category $question->category"
|
||||
|
||||
$possiblerandomqtypes = "'"
|
||||
. implode("','", $this->possiblerandomqtypes) . "'";
|
||||
if ($question->questiontext == "1") {
|
||||
// recurse into subcategories
|
||||
$categorylist = quiz_categorylist($question->category);
|
||||
} else {
|
||||
$categorylist = $question->category;
|
||||
}
|
||||
$this->catrandoms[$question->category] = get_records_sql
|
||||
("SELECT * FROM {$CFG->prefix}quiz_questions
|
||||
WHERE category IN ($categorylist)
|
||||
AND id NOT IN ($questionsinuse)
|
||||
AND qtype IN ($possiblerandomqtypes)");
|
||||
$this->catrandoms[$question->category] =
|
||||
draw_rand_array($this->catrandoms[$question->category],
|
||||
count($this->catrandoms[$question->category])); // from bug 1889
|
||||
}
|
||||
|
||||
while ($randomquestion =
|
||||
array_pop($this->catrandoms[$question->category])) {
|
||||
if (!ereg("(^|,)$randomquestion->id(,|$)", $questionsinuse)) {
|
||||
/// $randomquestion is not in use and will therefore be used
|
||||
/// as the randomquestion here...
|
||||
|
||||
global $QUIZ_QTYPES;
|
||||
$response = $QUIZ_QTYPES[$randomquestion->qtype]
|
||||
->create_response($randomquestion,
|
||||
quiz_qtype_nameprefix($randomquestion, $nameprefix),
|
||||
"$questionsinuse,$randomquestion->id");
|
||||
$response[$nameprefix] = $randomquestion->id;
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
notify(get_string('toomanyrandom', 'quiz', $question->category));
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
echo '<input type="hidden" name="' . $nameprefix
|
||||
. '" value="' . $actualquestion->id . '" />';
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]
|
||||
->print_question_formulation_and_controls($actualquestion,
|
||||
$quiz, $readonly, $answers, $correctanswers,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
echo '<p>' . get_string('random', 'quiz') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function get_wrapped_question($question, $nameprefix) {
|
||||
if (!empty($question->response[$nameprefix])
|
||||
and $actualquestion = get_record('quiz_questions',
|
||||
'id', $question->response[$nameprefix])) {
|
||||
$actualquestion->response = $question->response;
|
||||
unset($actualquestion->response[$nameprefix]);
|
||||
$actualquestion->maxgrade = $question->maxgrade;
|
||||
return $actualquestion;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]->grade_response(
|
||||
$actualquestion,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
@@ -137,8 +338,8 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
/// function is now able to handle both types of response record.
|
||||
|
||||
|
||||
/*** Pick random question id from the answer field in a way that ***/
|
||||
/*** works for both formats: ***/
|
||||
// Pick random question id from the answer field in a way that
|
||||
/// works for both formats:
|
||||
if (!ereg('^(random)?([0-9]+)(-(.*))?$', $rawresponse->answer, $answerregs)) {
|
||||
error("The answer value '$rawresponse->answer' for the response with "
|
||||
."id=$rawresponse->id to the random question "
|
||||
@@ -151,8 +352,8 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
'id', $randomquestionid)) {
|
||||
|
||||
if ($answerregs[1] && $answerregs[3]) {
|
||||
/**** The raw response is formatted according to
|
||||
/**** Moodle version 1.5 or later ****/
|
||||
// The raw response is formatted according to
|
||||
// Moodle version 1.5 or later
|
||||
$randomresponse = $rawresponse;
|
||||
$randomresponse->question = $randomquestionid;
|
||||
$randomresponse->answer = $answerregs[4];
|
||||
@@ -160,22 +361,19 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
} else if ($randomresponse = get_record
|
||||
('quiz_responses', 'question', $rawresponse->answer,
|
||||
'attempt', $rawresponse->attempt)) {
|
||||
/*** The response was stored by an older version of Moodle */
|
||||
/*** :-) ***/
|
||||
|
||||
// The response was stored by an older version of Moodle
|
||||
// :-)
|
||||
|
||||
} else {
|
||||
notify("Error: Cannot find response to random question $randomquestionid");
|
||||
unset($randomresponse);
|
||||
}
|
||||
|
||||
|
||||
if (isset($randomresponse)) {
|
||||
/// The prefered case:
|
||||
/// There is a random question and a response field, from
|
||||
/// There is a random question and a response field, from
|
||||
/// which the response array can be extracted:
|
||||
|
||||
$response = $QUIZ_QTYPES[$randomquestion->qtype]
|
||||
->extract_response($randomresponse,
|
||||
quiz_qtype_nameprefix($randomquestion, $nameprefix));
|
||||
|
||||
} else {
|
||||
|
||||
@@ -191,7 +389,8 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
// good chances to execute properly anyway.)
|
||||
}
|
||||
$response[$nameprefix] = $randomquestionid;
|
||||
return $response;
|
||||
//return $response;
|
||||
return '';
|
||||
} else {
|
||||
notify("Error: Unable to find random question $rawresponse->question");
|
||||
/// No new random question is picked as this is probably
|
||||
@@ -199,54 +398,6 @@ class quiz_random_qtype extends quiz_default_questiontype {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
echo '<input type="hidden" name="' . $nameprefix
|
||||
. '" value="' . $actualquestion->id . '" />';
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]
|
||||
->print_question_formulation_and_controls($actualquestion,
|
||||
$quiz, $readonly, $answers, $correctanswers,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
echo '<p>' . get_string('random', 'quiz') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
function get_wrapped_question($question, $nameprefix) {
|
||||
if (!empty($question->response[$nameprefix])
|
||||
and $actualquestion = get_record('quiz_questions',
|
||||
'id', $question->response[$nameprefix])) {
|
||||
$actualquestion->response = $question->response;
|
||||
unset($actualquestion->response[$nameprefix]);
|
||||
$actualquestion->maxgrade = $question->maxgrade;
|
||||
return $actualquestion;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
// Get the wrapped question...
|
||||
if ($actualquestion = $this->get_wrapped_question($question,
|
||||
$nameprefix)) {
|
||||
return $QUIZ_QTYPES[$actualquestion->qtype]->grade_response(
|
||||
$actualquestion,
|
||||
quiz_qtype_nameprefix($actualquestion, $nameprefix));
|
||||
} else {
|
||||
$result->grade = 0.0;
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
} else {
|
||||
$options->choose = "";
|
||||
}
|
||||
$numberavailable = count_records("quiz_questions", "category", $category->id, "qtype", SHORTANSWER);
|
||||
$saquestions = $QUIZ_QTYPES[RANDOMSAMATCH]->get_sa_candidates($category->id);
|
||||
$numberavailable = count($saquestions);
|
||||
unset($saquestions);
|
||||
print_heading_with_help(get_string("editingrandomsamatch", "quiz"), "randomsamatch", "quiz");
|
||||
require("$CFG->dirroot/mod/quiz/questiontypes/randomsamatch/randomsamatch.html");
|
||||
|
||||
|
||||
@@ -13,18 +13,34 @@
|
||||
class quiz_randomsamatch_qtype extends quiz_match_qtype {
|
||||
/// Extends MATCH as there are quite a few simularities...
|
||||
|
||||
// $catrandoms carries question ids for shortanswer questions
|
||||
// available as random questios.
|
||||
// They are sorted by category.
|
||||
var $catrandoms = array();
|
||||
|
||||
function name() {
|
||||
return 'randomsamatch';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
if (!$question->options->choose = get_field('quiz_randomsamatch',
|
||||
'choose', 'question', $question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
|
||||
// This could be included as a flag in the database. It's already
|
||||
// supported by the code.
|
||||
// Recurse subcategories: 0 = no recursion, 1 = recursion
|
||||
$question->options->subcats = 1;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
$options->question = $question->id;
|
||||
$options->choose = $question->choose;
|
||||
|
||||
if (2 > $question->choose) {
|
||||
$result->error = "At least two shortanswer questions need to be chosen!";
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($existing = get_record("quiz_randomsamatch",
|
||||
"question", $options->question)) {
|
||||
$options->id = $existing->id;
|
||||
@@ -40,66 +56,117 @@ class quiz_randomsamatch_qtype extends quiz_match_qtype {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function wrapped_questions($question) {
|
||||
if (empty($question->response)) {
|
||||
return false;
|
||||
} else {
|
||||
$wrapped = '';
|
||||
$delimiter = '';
|
||||
foreach ($question->response as $rkey => $response) {
|
||||
$wrapped .= $delimiter.$this->extract_response_id($rkey);
|
||||
$delimiter = ',';
|
||||
}
|
||||
return $wrapped;
|
||||
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
// Choose a random shortanswer question from the category:
|
||||
// We need to make sure that no question is used more than once in the
|
||||
// quiz. Therfore the following need to be excluded:
|
||||
// 1. All questions that are explicitly assigned to the quiz
|
||||
// 2. All random questions
|
||||
// 3. All questions that are already chosen by an other random question
|
||||
global $QUIZ_QTYPES;
|
||||
if (!isset($quiz->questionsinuse)) {
|
||||
$quiz->questionsinuse = $quiz->questions;
|
||||
}
|
||||
|
||||
if ($question->options->subcats) {
|
||||
// recurse into subcategories
|
||||
$categorylist = quiz_categorylist($question->category);
|
||||
} else {
|
||||
$categorylist = $question->category;
|
||||
}
|
||||
|
||||
$saquestions = $this->get_sa_candidates($categorylist, $quiz->questionsinuse);
|
||||
|
||||
$count = count($saquestions);
|
||||
$wanted = $question->options->choose;
|
||||
if ($count < $wanted && isteacherinanycourse()) {
|
||||
notify("Error: could not get enough Short-Answer questions!
|
||||
Got $count Short-Answer questions, but wanted $wanted.
|
||||
Reducing number to choose from to $count!");
|
||||
$question->options->choose = $count;
|
||||
}
|
||||
|
||||
$saquestions =
|
||||
draw_rand_array($saquestions, $question->options->choose); // from bug 1889
|
||||
|
||||
foreach ($saquestions as $key => $wrappedquestion) {
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we overwrite the $question->options->answers field to only
|
||||
// *one* (the first) correct answer. This loop can be deleted to
|
||||
// take all answers into account (i.e. put them all into the
|
||||
// drop-down menu.
|
||||
$foundcorrect = false;
|
||||
foreach ($wrappedquestion->options->answers as $answer) {
|
||||
if ($foundcorrect || $answer->fraction != 1.0) {
|
||||
unset($wrappedquestion->options->answers[$answer->id]);
|
||||
} else if (!$foundcorrect) {
|
||||
$foundcorrect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->create_session_and_responses($wrappedquestion, $state, $quiz,
|
||||
$attempt)) {
|
||||
return false;
|
||||
}
|
||||
$wrappedquestion->name_prefix = $question->name_prefix;
|
||||
$wrappedquestion->maxgrade = $question->maxgrade;
|
||||
$quiz->questionsinuse .= ",$wrappedquestion->id";
|
||||
$state->options->subquestions[$key] = clone($wrappedquestion);
|
||||
}
|
||||
|
||||
// Shuffle the answers if required
|
||||
$subquestionids = array_values(array_map(create_function('$val',
|
||||
'return $val->id;'), $state->options->subquestions));
|
||||
if ($quiz->shuffleanswers) {
|
||||
$subquestionids = swapshuffle($subquestionids);
|
||||
}
|
||||
$state->options->order = $subquestionids;
|
||||
// Create empty responses
|
||||
foreach ($subquestionids as $val) {
|
||||
$state->responses[$val] = '';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_response($question, $nameprefix, $questionsinuse) {
|
||||
// It's for question types like RANDOMSAMATCH and RANDOM that
|
||||
// the true power of the pattern with this function comes to the surface.
|
||||
// This implementation will stand even after a possible exclusion of
|
||||
// the funtions extract_response and convert_to_response_answer_field
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
global $QUIZ_QTYPES;
|
||||
$responses = explode(',', $state->responses['']);
|
||||
$responses = array_map(create_function('$val',
|
||||
'return explode("-", $val);'), $responses);
|
||||
|
||||
if (!isset($this->catrandoms[$question->category])) {
|
||||
/// Need to fetch the shortanswer question ids for the category:
|
||||
|
||||
$saquestions = get_records_select('quiz_questions',
|
||||
" category='$question->category'
|
||||
AND qtype='".SHORTANSWER."'
|
||||
AND id NOT IN ($questionsinuse) ");
|
||||
$this->catrandoms[$question->category] = array_keys($saquestions);
|
||||
shuffle($this->catrandoms[$question->category]);
|
||||
}
|
||||
|
||||
/// Access question options to find out how many short-answer
|
||||
/// questions we are supposed to pick...
|
||||
if ($options = get_record('quiz_randomsamatch',
|
||||
'question', $question->id)) {
|
||||
$questionstopick = $options->choose;
|
||||
} else {
|
||||
notify("Error: Missing question options! - Try to pick two shortanswer questions anyway");
|
||||
$questionstopick = 2;
|
||||
}
|
||||
|
||||
/// Pick the short-answer question ids and create the $response array
|
||||
$response = array();
|
||||
while ($questionstopick) {
|
||||
$said = array_pop($this->catrandoms[$question->category]);
|
||||
if (!ereg("(^|,)$said(,|$)", $questionsinuse)) {
|
||||
$response[$nameprefix.$said] = '0';
|
||||
--$questionstopick;
|
||||
// Restore the previous responses
|
||||
$state->responses = array();
|
||||
foreach ($responses as $response) {
|
||||
$state->responses[$response[0]] = $response[1];
|
||||
$state->options->order[] = $response[0];
|
||||
if (!$wrappedquestion = get_record('quiz_questions', 'id',
|
||||
$response[0])) {
|
||||
notify("Couldn't get question (id=$response[0])!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->get_question_options($wrappedquestion)) {
|
||||
notify("Couldn't get question options (id=$response[0])!");
|
||||
return false;
|
||||
}
|
||||
if (!$QUIZ_QTYPES[$wrappedquestion->qtype]
|
||||
->restore_session_and_responses($wrappedquestion, $state)) {
|
||||
notify("Couldn't restore session of question (id=$response[0])!");
|
||||
return false;
|
||||
}
|
||||
$wrappedquestion->name_prefix = $question->name_prefix;
|
||||
$wrappedquestion->maxgrade = $question->maxgrade;
|
||||
|
||||
if ($questionstopick) {
|
||||
notify("Error: could not get enough Short-Answer questions!");
|
||||
$count = count($response);
|
||||
$wanted = $count + $questionstopick;
|
||||
notify("Got $count Short-Answer questions, but wanted $wanted.");
|
||||
$state->options->subquestions[$wrappedquestion->id] =
|
||||
clone($wrappedquestion);
|
||||
}
|
||||
|
||||
return $response;
|
||||
return true;
|
||||
}
|
||||
|
||||
function extract_response($rawresponse, $nameprefix) {
|
||||
@@ -115,112 +182,16 @@ class quiz_randomsamatch_qtype extends quiz_match_qtype {
|
||||
return $response;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
|
||||
// Print question formulation
|
||||
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat, NULL, $quiz->course);
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
// Summarize shortanswer questions answer alternatives:
|
||||
if (empty($correctanswers)) {
|
||||
// Get them using the grade_response method
|
||||
$tempresult = $this->grade_response($question, $nameprefix);
|
||||
$saanswers = $tempresult->correctanswers;
|
||||
} else {
|
||||
$saanswers = $correctanswers;
|
||||
}
|
||||
foreach ($saanswers as $key => $saanswer) {
|
||||
unset($saanswers[$key]); // Unsets the nameprefix occurence
|
||||
$saanswers[$saanswer->id] = trim($saanswer->answer);
|
||||
}
|
||||
$saanswers = draw_rand_array($saanswers, count($saanswers));
|
||||
|
||||
// Print the shortanswer questions and input controls:
|
||||
echo '<table border="0" cellpadding="10">';
|
||||
foreach ($question->response as $inputname => $response) {
|
||||
if (!($saquestion = get_record('quiz_questions', 'id',
|
||||
quiz_extract_posted_id($inputname, $nameprefix)))) {
|
||||
notify("Error: cannot find shortanswer question for $inputname ");
|
||||
continue;
|
||||
}
|
||||
|
||||
echo '<tr><td align="left" valign="top">';
|
||||
echo $saquestion->questiontext;
|
||||
echo '</td>';
|
||||
echo '<td align="right" valign="top">';
|
||||
if (!empty($correctanswers)
|
||||
&& $correctanswers[$inputname]->id == $response) {
|
||||
echo '<span="highlight">';
|
||||
choose_from_menu($saanswers, $inputname, $response);
|
||||
echo '</span><br />';
|
||||
} else {
|
||||
choose_from_menu($saanswers, $inputname, $response);
|
||||
if ($readonly && $quiz->correctanswers
|
||||
&& isset($correctanswer[$inputname])) {
|
||||
quiz_print_correctanswer($correctanswer[$inputname]->answer);
|
||||
}
|
||||
}
|
||||
if ($quiz->feedback && isset($answers[$inputname])
|
||||
&& $answers[$inputname]->feedback) {
|
||||
quiz_print_comment($answers[$inputname]->feedback);
|
||||
}
|
||||
echo '</td></tr>';
|
||||
}
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
global $QUIZ_QTYPES;
|
||||
|
||||
$result->answers = array();
|
||||
$result->correctanswers = array();
|
||||
$result->grade = 0.0;
|
||||
|
||||
foreach ($question->response as $inputname => $subresponse) {
|
||||
if ($subquestion = get_record('quiz_questions',
|
||||
'id', quiz_extract_posted_id($inputname, $nameprefix),
|
||||
// These two query conditions are security checks that prevents cheating...
|
||||
'qtype', SHORTANSWER,
|
||||
'category', $question->category)) {
|
||||
|
||||
if ($subresponse = get_record('quiz_answers',
|
||||
'id', $subresponse)) {
|
||||
$subquestion->response[$inputname] = $subresponse->answer;
|
||||
} else {
|
||||
$subquestion->response[$inputname] = '';
|
||||
}
|
||||
|
||||
// Use the shortanswer framework to for grading...
|
||||
$subresult = $QUIZ_QTYPES[SHORTANSWER]
|
||||
->grade_response($subquestion, $inputname);
|
||||
|
||||
// Summarize shortanswer results
|
||||
if (isset($subresult->answers[$inputname])) {
|
||||
$result->answers[$inputname] =
|
||||
$subresult->answers[$inputname];
|
||||
$result->grade += $result->answers[$inputname]->fraction;
|
||||
if ($result->answers[$inputname]->fraction >= 1.0) {
|
||||
$result->correctanswers[$inputname] =
|
||||
$result->answers[$inputname];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Pick the first correctanswer:
|
||||
foreach ($subresult->correctanswers as $correct) {
|
||||
$result->correctanswers[$inputname] = $correct;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($result->grade) {
|
||||
$result->grade /= count($question->response);
|
||||
}
|
||||
return $result;
|
||||
function get_sa_candidates($categorylist, $questionsinuse=0) {
|
||||
return get_records_select('quiz_questions',
|
||||
"qtype = '".SHORTANSWER."' " .
|
||||
"AND category IN ($categorylist) " .
|
||||
"AND parent = '0' " .
|
||||
"AND hidden = '0'" .
|
||||
"AND id NOT IN ($questionsinuse)");
|
||||
}
|
||||
}
|
||||
|
||||
//// END OF CLASS ////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php // $Id$
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/questiontypes/rqp/lib.php');
|
||||
|
||||
$types = quiz_rqp_get_types();
|
||||
print_heading_with_help(get_string('editingrqp', 'quiz'), 'rqp', 'quiz');
|
||||
if (empty($question->id)) {
|
||||
$question->options->type = '';
|
||||
$question->options->source = '';
|
||||
$question->options->format = '';
|
||||
}
|
||||
else if (!$QUIZ_QTYPES[$question->qtype]->get_question_options($question)) {
|
||||
$question->options->type = '';
|
||||
$question->options->source = '';
|
||||
$question->options->format = '';
|
||||
echo "<p align=\"center\">Error! Could not load the options for this question!</p>\n";
|
||||
}
|
||||
require('rqp.html');
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
require_once('../../../../config.php');
|
||||
require_once('lib.php');
|
||||
require_once('remote.php');
|
||||
|
||||
// Check user admin
|
||||
require_login();
|
||||
if (!isadmin()) {
|
||||
error('You need to be an admin user to use this page.', $CFG->wwwroot . '/login/index.php');
|
||||
}
|
||||
|
||||
if (!$site = get_site()) {
|
||||
error('Site isn\'t defined!');
|
||||
}
|
||||
|
||||
$sesskey = required_param('sesskey', PARAM_RAW);
|
||||
if (!confirm_sesskey($sesskey)) {
|
||||
error(get_string('confirmsesskeybad', 'error'));
|
||||
}
|
||||
$type->id = optional_param('id', NULL, PARAM_INT);
|
||||
$type->name = optional_param('name', NULL, PARAM_RAW);
|
||||
if (is_null($type->name)) {
|
||||
$delete = optional_param('delete', NULL, PARAM_INT);
|
||||
if (!is_null($delete)) {
|
||||
$type->id = $delete;
|
||||
$confirm = optional_param('confirm', NULL, PARAM_RAW);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$delete = NULL;
|
||||
$type->rendering_server = required_param('rendering_server', PARAM_RAW);
|
||||
$type->cloning_server = required_param('cloning_server', PARAM_RAW);
|
||||
}
|
||||
|
||||
// Print the header
|
||||
$stradmin = get_string('admin');
|
||||
$strconfiguration = get_string('configuration');
|
||||
$strmanagemodules = get_string('managemodules');
|
||||
$strmodulename = get_string('modulename', 'quiz');
|
||||
$stritemtypes = get_string('itemtypes', 'quiz');
|
||||
$streditingitemtype = get_string(is_null($type->id) ? 'addingitemtype' : 'editingitemtype', 'quiz');
|
||||
$navigation = '<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/index.php">' . $stradmin . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/configure.php">' . $strconfiguration . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/modules.php">' . $strmanagemodules . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/module.php?module=quiz&sesskey=' . s(rawurlencode($sesskey)) . '">' . $strmodulename . '</a> -> ' .
|
||||
'<a href="types.php?sesskey=' . s(rawurlencode($sesskey)) . '">' . $stritemtypes . '</a> -> ' .
|
||||
$streditingitemtype;
|
||||
print_header($site->shortname . ': ' . $strmodulename . ': ' . $stritemtypes . ': ' . $streditingitemtype, $site->fullname, $navigation, '', '', true, '', '');
|
||||
|
||||
print_heading($streditingitemtype);
|
||||
|
||||
$err = array();
|
||||
|
||||
// Save the type
|
||||
while (!is_null($type->name)) { // using like if but with support for break
|
||||
// Check basic settings
|
||||
if (empty($type->name)) {
|
||||
$err['name'] = get_string('missingitemtypename', 'quiz');
|
||||
break;
|
||||
}
|
||||
if (empty($type->rendering_server)) {
|
||||
$err['rendering_server'] = get_string('missingrenderingserver', 'quiz');
|
||||
break;
|
||||
}
|
||||
// Check servers exist and work
|
||||
$servers = remote_server_info($type, true, !empty($type->cloning_server));
|
||||
if (false === $servers['rendering']) {
|
||||
$err['rendering_server'] = get_string('renderingserverconnectfailed', 'quiz', $type->rendering_server);
|
||||
break;
|
||||
}
|
||||
if (!empty($type->cloning_server)) {
|
||||
if (false === $servers['cloning']) {
|
||||
$err['cloning_server'] = get_string('cloningserverconnectfailed', 'quiz', $type->cloning_server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$servers['rendering']->rendering) {
|
||||
$err['rendering_server'] = get_string('renderingserverdoesnt', 'quiz', $type->rendering_server);
|
||||
break;
|
||||
}
|
||||
if (!empty($type->cloning_server)) {
|
||||
if (!$servers['cloning']->cloning) {
|
||||
if ($type->cloning_server !== $type->rendering_server) {
|
||||
$err['cloning_server'] = get_string('cloningserverdoesnt', 'quiz', $type->cloning_server);
|
||||
break;
|
||||
}
|
||||
if (!$servers['rendering']->implicitCloning) {
|
||||
$err['cloning_server'] = get_string('cloningrenderingserverdoesnt', 'quiz', $type->cloning_server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the server specific flags
|
||||
$type->flags = 0;
|
||||
$type->flags |= $servers['rendering']->itemCaching ? REMOTE_ITEM_CACHE : 0;
|
||||
if (!empty($type->cloning_server)) {
|
||||
$type->flags |= $servers['cloning']->cloning ? REMOTE_CLONING : 0;
|
||||
if ($type->cloning_server === $type->rendering_server) {
|
||||
$type->flags |= $servers['rendering']->implicitCloning ? REMOTE_IMPLICIT_CLONING : 0;
|
||||
}
|
||||
$type->flags |= $servers['cloning']->templateCaching ? REMOTE_TEMPLATE_CACHE : 0;
|
||||
}
|
||||
// Save the type in the database
|
||||
if (!quiz_rqp_save_type($type)) {
|
||||
error('Could not save the item type', 'types.php?sesskey=' . s(rawurlencode($sesskey)));
|
||||
}
|
||||
redirect('types.php?sesskey=' . rawurlencode($sesskey), get_string('success'));
|
||||
// error and redirect both call exit so we never go round the loop
|
||||
}
|
||||
|
||||
if (is_null($type->name)) {
|
||||
// Load the type
|
||||
if (is_null($type->id)) {
|
||||
$type->name = '';
|
||||
$type->rendering_server = '';
|
||||
$type->cloning_server = '';
|
||||
} else {
|
||||
if (! ($type = get_record('quiz_rqp_type', 'id', $type->id))) {
|
||||
error('Could not load the item type', 'types.php?sesskey=' . s(rawurlencode($sesskey)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display the delete form
|
||||
if (!is_null($delete)) {
|
||||
if (md5($delete) === $confirm) {
|
||||
if (!quiz_rqp_delete_type($delete)) {
|
||||
error('Could not delete the item type', 'types.php?sesskey=' . s(rawurlencode($sesskey)));
|
||||
}
|
||||
redirect('types.php?sesskey=' . rawurlencode($sesskey), get_string('success'));
|
||||
}
|
||||
notice_yesno(get_string('deleteitemtypecheck', 'quiz', $type->name), 'edittype.php?delete=' . s(rawurlencode($delete)) . '&confirm=' . s(rawurlencode(md5($delete))) . '&sesskey=' . s(rawurlencode($sesskey)), 'types.php?sesskey=' . s(rawurlencode($sesskey)));
|
||||
print_footer();
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<form name="theform" method="post" action="edittype.php">
|
||||
<center>
|
||||
<table cellpadding="5">
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('name'); ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="name" size="50" value="<?php p($type->name); ?>" />
|
||||
<?php
|
||||
if (isset($err['name'])) {
|
||||
formerr($err['name']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('renderingserver', 'quiz'); ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="rendering_server" size="50" value="<?php p($type->rendering_server); ?>" />
|
||||
<?php
|
||||
if (isset($err['rendering_server'])) {
|
||||
formerr($err['rendering_server']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('cloningserver', 'quiz'); ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="cloning_server" size="50" value="<?php p($type->cloning_server); ?>" />
|
||||
<?php
|
||||
if (isset($err['cloning_server'])) {
|
||||
formerr($err['cloning_server']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="id" value="<?php p($type->id); ?>" />
|
||||
<input type="hidden" name="sesskey" value="<?php p($sesskey); ?>" />
|
||||
<input type="submit" value="<?php print_string('savechanges'); ?>" />
|
||||
|
||||
</center>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
print_footer();
|
||||
?>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 125 B |
@@ -0,0 +1,113 @@
|
||||
<?php // $Id$
|
||||
|
||||
/**
|
||||
* Library of functions used by the RQP question type
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Alex Smith and other members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
function quiz_rqp_get_types() {
|
||||
return get_records('quiz_rqp_type', '', '', 'name ASC');
|
||||
}
|
||||
|
||||
function quiz_rqp_save_type($type) {
|
||||
if (empty($type->id)) {
|
||||
return insert_record('quiz_rqp_type', $type, false);
|
||||
}
|
||||
return update_record('quiz_rqp_type', $type);
|
||||
}
|
||||
|
||||
function quiz_rqp_delete_type($id) {
|
||||
return delete_records('quiz_rqp_type', 'id', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a colon separated list of space separated values from an associative
|
||||
* array of arrays
|
||||
*
|
||||
* An associative array of values or an associative array of arrays is imploded
|
||||
* to a string by creating a colon separated list of space separated values. The
|
||||
* key is treated as the first value. The {@link quiz_rqp_explode} function can
|
||||
* restore the array from this string representation.
|
||||
* @return string The string representation of the array. This is a colon
|
||||
* separated list of space separated values.
|
||||
* @param array $array An associative array of single values or an associative
|
||||
* array of arrays to be imploded.
|
||||
*/
|
||||
function quiz_rqp_implode($array) {
|
||||
if (count($array) < 1) {
|
||||
return '';
|
||||
}
|
||||
$str = '';
|
||||
foreach ($array as $key => $val) {
|
||||
$str .= $key . ' ';
|
||||
if (is_array($val)) {
|
||||
if (count($val) > 0) {
|
||||
foreach ($val as $subval) {
|
||||
$str .= $subval . ' ';
|
||||
}
|
||||
// Remove the trailing space
|
||||
$str = substr($str, 0, -1);
|
||||
}
|
||||
} else {
|
||||
$str .= $val;
|
||||
}
|
||||
$str .= ':';
|
||||
}
|
||||
// Remove the trailing semi-colon
|
||||
return substr($str, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreates an associative array or an associative array of arrays from the
|
||||
* string representation
|
||||
*
|
||||
* Takes a colon separated list of space separated values as produced by
|
||||
* {@link quiz_rqp_implode} and recreates the array. If an array of single values
|
||||
* is expected then an error results if an element has more than one value.
|
||||
* Otherwise every value is an array.
|
||||
* @return array The associative array restored from the string. Every
|
||||
* element is a single value if $multi is false or an array
|
||||
* if $multi is true.
|
||||
* @param string $str The string to explode. This is a colon separated list of
|
||||
* space separated values.
|
||||
* @param boolean $multi Flag indicating if the values in the array are expected
|
||||
* to be of multiple cardinality (i.e. an array of arrays
|
||||
* is expected) or single values (i.e. an array of values).
|
||||
* The default is false indicating an array of single
|
||||
* values is expected.
|
||||
*/
|
||||
function quiz_rqp_explode($str, $multi=false) {
|
||||
// Explode by colon
|
||||
if ($str === '') {
|
||||
return array();
|
||||
}
|
||||
$array = explode(':', $str);
|
||||
$n = count($array);
|
||||
$return = array();
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
// Explode by space
|
||||
$array[$i] = explode(' ', $array[$i]);
|
||||
// Get the key
|
||||
$key = array_shift($array[$i]);
|
||||
if (array_key_exists($key, $return)) {
|
||||
// Element appears twice!
|
||||
return false;
|
||||
}
|
||||
// Save the element
|
||||
if ($multi) {
|
||||
$return[$key] = $array[$i];
|
||||
} else if (count($array[$i]) > 1) {
|
||||
return false;
|
||||
} else {
|
||||
$return[$key] = $array[$i][0];
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
?>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,460 @@
|
||||
<?php // $Id$
|
||||
|
||||
/**
|
||||
* This file defines the RQP question type class
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Alex Smith and other members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once($CFG->dirroot . '/mod/quiz/questiontypes/rqp/lib.php');
|
||||
require_once('remote.php');
|
||||
|
||||
/**
|
||||
* RQP question type class
|
||||
*/
|
||||
class quiz_rqp_qtype extends quiz_default_questiontype {
|
||||
|
||||
/**
|
||||
* Name of the rqp question type
|
||||
*
|
||||
* @ return string 'rqp'
|
||||
*/
|
||||
function name() {
|
||||
return 'rqp';
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the type-specific options
|
||||
*
|
||||
* This also saves additional information that it receives from
|
||||
* an RQP_itemInformation call to the RQP server
|
||||
*/
|
||||
function save_question_options($form) {
|
||||
global $CFG;
|
||||
|
||||
// Check source type
|
||||
if (!$type = get_record('quiz_rqp_type', 'id', $form->type)) {
|
||||
$result->notice = get_string('invalidsourcetype', 'quiz');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Create the object to be stored in quiz_rqp table
|
||||
$options = new object;
|
||||
$options->question = $form->id;
|
||||
$options->type = $form->type;
|
||||
$options->type_name = $type->name;
|
||||
$options->source = $form->source;
|
||||
$options->format = $form->format;
|
||||
$options->rendering_server = $type->rendering_server;
|
||||
$options->cloning_server = $type->cloning_server;
|
||||
|
||||
// Check source file
|
||||
$item = remote_item_info($options);
|
||||
if (false === $item || is_soap_fault($item)) {
|
||||
$result->notice = get_string('invalidsource', 'quiz', $options);
|
||||
return $result;
|
||||
}
|
||||
if ($item->sourceErrors) {
|
||||
array_walk($item->sourceErrors,
|
||||
create_function('&$val', '$val = $val->message;'));
|
||||
$result->notice = get_string('invalidsource', 'quiz', $options) .
|
||||
'<br />' . implode('<br />', array_values($item->sourceErrors));
|
||||
return $result;
|
||||
}
|
||||
// Time dependent items are not supported by the quiz module yet
|
||||
if ($item->timeDependent) {
|
||||
$result->noticeyesno = get_string('notimedependentitems', 'quiz');
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Set the format and item specific flags
|
||||
$options->format = $item->format;
|
||||
$options->maxscore = $item->maxScore;
|
||||
$options->flags = 0;
|
||||
$options->flags |= $item->template ? REMOTE_TEMPLATE : 0;
|
||||
$options->flags |= $item->adaptive ? REMOTE_ADAPTIVE : 0;
|
||||
|
||||
// Check for cloning support if item is a template
|
||||
if ($item->template && !($type->flags & REMOTE_TEMPLATES_SUPPORTED)) {
|
||||
$result->notice = get_string('itemrequirescloning', 'quiz');
|
||||
return $result;
|
||||
}
|
||||
// Save the options
|
||||
if ($old = get_record('quiz_rqp', 'question', $form->id)) {
|
||||
$old->type = $options->type;
|
||||
$old->source = $options->source;
|
||||
$old->format = $options->format;
|
||||
$old->flags = $options->flags;
|
||||
$old->maxscore = $options->maxscore;
|
||||
if (!update_record('quiz_rqp', $old)) {
|
||||
$result->error = "Could not update quiz rqp options! (id=$old->id)";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
if (!insert_record('quiz_rqp', $options)) {
|
||||
$result->error = 'Could not insert quiz rqp options!';
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the question type specific options for the question.
|
||||
*
|
||||
* This function loads all question type specific options for the
|
||||
* question from the database into the $question->options field.
|
||||
* @return bool Indicates success or failure.
|
||||
* @param object $question The question object for the question.
|
||||
*/
|
||||
function get_question_options(&$question) {
|
||||
|
||||
$options =& $question->options;
|
||||
if (! ($options = get_record('quiz_rqp', 'question', $question->id))) {
|
||||
return false;
|
||||
}
|
||||
if (!$type = get_record('quiz_rqp_type', 'id', $options->type)) {
|
||||
return false;
|
||||
}
|
||||
$options->type_name = $type->name;
|
||||
$options->rendering_server = $type->rendering_server;
|
||||
$options->cloning_server = $type->cloning_server;
|
||||
$options->flags &= $type->flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value or array of values which will give full marks if graded as
|
||||
* the $state->responses field
|
||||
*
|
||||
* The correct answers are obtained from the RQP server via the
|
||||
* RQP_SessionInformation operation
|
||||
* @return mixed An array of values giving the responses corresponding
|
||||
* to the (or a) correct answer to the question.
|
||||
* @param object $question The question for which the correct answer is to
|
||||
* be retrieved.
|
||||
* @param object $state The state object that corresponds to the question,
|
||||
* for which a correct answer is needed.
|
||||
*/
|
||||
function get_correct_responses(&$question, &$state) {
|
||||
$info = remote_session_info($question, $state);
|
||||
if (false === $info || is_soap_fault($info)) {
|
||||
return null;
|
||||
}
|
||||
return $info->correctResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates empty session and response information for the question
|
||||
*
|
||||
* This function is called to start a question session. Empty question type
|
||||
* specific session data and empty response data is added to the state object.
|
||||
* @return bool Indicates success or failure.
|
||||
* @param object $question The question for which the session is to be created.
|
||||
* @param object $state The state to create the session for. This is passed by
|
||||
* reference and will be updated.
|
||||
* @param object $quiz The quiz for which the session is to be started.
|
||||
* (not used)
|
||||
* @param object $attempt The quiz attempt for which the session is to be
|
||||
* started. (not used)
|
||||
*/
|
||||
function create_session_and_responses(&$question, &$state, $quiz, $attempt) {
|
||||
$state->responses = array('' => '');
|
||||
$state->options->persistent_data = '';
|
||||
$state->options->template_vars = array();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the session data and most recent responses for the given state
|
||||
*
|
||||
* This function loads any session data associated with the question session
|
||||
* in the given state from the quiz_rqp_states table into the state object.
|
||||
* @return bool Indicates success or failure.
|
||||
* @param object $question The question object for the question including any
|
||||
* question type specific information.
|
||||
* @param object $state The saved state to load the session for. This
|
||||
* object is updated to include the question
|
||||
* type specific session information and responses
|
||||
* (it is passed by reference).
|
||||
*/
|
||||
function restore_session_and_responses(&$question, &$state) {
|
||||
if (!$options = get_record('quiz_rqp_states', 'stateid', $state->id)) {
|
||||
return false;
|
||||
}
|
||||
$state->responses = quiz_rqp_explode($options->responses);
|
||||
$state->options->persistent_data = $options->persistent_data;
|
||||
$state->options->template_vars =
|
||||
quiz_rqp_explode($options->template_vars, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the session data and responses for the question in a new state
|
||||
*
|
||||
* This function saves all session data from the state object into the
|
||||
* quiz_rqp_states table
|
||||
* @return bool Indicates success or failure.
|
||||
* @param object $question The question object for the question including
|
||||
* the question type specific information.
|
||||
* @param object $state The state for which the question type specific
|
||||
* data and responses should be saved.
|
||||
*/
|
||||
function save_session_and_responses(&$question, &$state) {
|
||||
$options->stateid = $state->id;
|
||||
$options->responses = quiz_rqp_implode($state->responses);
|
||||
$options->persistent_data = $state->options->persistent_data;
|
||||
$options->template_vars =
|
||||
quiz_rqp_implode($state->options->template_vars);
|
||||
if (!insert_record('quiz_rqp_states', $options)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the main content of the question including any interactions
|
||||
*
|
||||
* This function prints the main content of the question which it obtains
|
||||
* from the RQP server via the Render operation. It also updates
|
||||
* $state->options->persistent_data and $state->options->template_vars
|
||||
* with the values returned by the RQP server.
|
||||
* @param object $question The question to be rendered.
|
||||
* @param object $state The state to render the question in. The grading
|
||||
* information is in ->grade, ->raw_grade and
|
||||
* ->penalty. The current responses are in
|
||||
* ->responses. The last graded state is in
|
||||
* ->last_graded (hence the most recently graded
|
||||
* responses are in ->last_graded->responses).
|
||||
* @param object $quiz The quiz to which the question belongs.
|
||||
* @param object $options An object describing the rendering options.
|
||||
* The fields are:
|
||||
* ->readonly Review / interactive mode
|
||||
* ->feedback Show feedback for the graded
|
||||
* responses
|
||||
* ->validation Show how the current responses
|
||||
* responses were interpreted
|
||||
* ->correct_responses Show solutions
|
||||
* These are all boolean values.
|
||||
*/
|
||||
function print_question_formulation_and_controls(&$question, &$state,
|
||||
$quiz, $options) {
|
||||
|
||||
// Use the render output created during grading if it exists
|
||||
if (isset($state->options->renderoutput)) {
|
||||
$output =& $state->options->renderoutput;
|
||||
} else {
|
||||
// Otherwise perform a render operation
|
||||
$output = remote_render($question, $state, false,
|
||||
$options->readonly ? 'readonly' : 'normal');
|
||||
if (false === $output || is_soap_fault($output)) {
|
||||
unset($output);
|
||||
notify('Error: Rendering failed!');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$state->options->persistent_data = $output->persistentData;
|
||||
$state->options->template_vars = $output->templateVars;
|
||||
// Print the head (this may not work, really it should be in the head
|
||||
// section of the html document but moodle doesn't allow question types
|
||||
// to put things there)
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'head'])) {
|
||||
echo $output->output[RQP_URI_COMPONENT . 'head']->output;
|
||||
}
|
||||
// Print the title
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'title'])) {
|
||||
echo '<h2>' . $output->output[RQP_URI_COMPONENT . 'title']->output
|
||||
. "</h2>\n";
|
||||
}
|
||||
// Print the stem
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'stem'])) {
|
||||
echo '<div class="RQPstem">';
|
||||
echo $output->output[RQP_URI_COMPONENT . 'stem']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
// Print the interactions
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'interactions'])) {
|
||||
echo '<div class="RQPinteractions" align="right">';
|
||||
echo $output->output[RQP_URI_COMPONENT . 'interactions']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
// Print the last answer
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'lastAnswer'])) {
|
||||
echo '<div class="RQPlastAnswer">';
|
||||
echo $output->output[RQP_URI_COMPONENT . 'lastAnswer']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
// Print the validation when required
|
||||
if ($options->validation) {
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'validation'])) {
|
||||
echo '<div class="RQPvalidation">';
|
||||
echo $output->output[RQP_URI_COMPONENT . 'validation']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
// Print the feedback when required
|
||||
if ($options->feedback) {
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'feedback'])) {
|
||||
echo '<div class="RQPfeedback">';
|
||||
echo $output->output[RQP_URI_COMPONENT . 'feedback']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
// Print the solution when required
|
||||
if ($options->correct_responses) {
|
||||
if (isset($output->output[RQP_URI_COMPONENT . 'solution'])) {
|
||||
echo $output->output[RQP_URI_COMPONENT . 'solution']->output;
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
// Note: hint(s) and modal feedback are ignored; moodle does not support
|
||||
// them yet.
|
||||
// Remove the render output created during grading (if any)
|
||||
unset($state->options->renderoutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the submit and validate buttons
|
||||
* @param object $question The question for which the buttons are to be printed
|
||||
* @param object $state The state the question is in (not used)
|
||||
* @param object $quiz The quiz to which the question belongs.
|
||||
* @param object $options An object describing the rendering options.
|
||||
* (not used. This function should only have been called
|
||||
* if the options were such that the buttons are required)
|
||||
*/
|
||||
function print_question_submit_buttons(&$question, &$state, $quiz, $options) {
|
||||
echo '<input type="submit" name="';
|
||||
echo $question->name_prefix;
|
||||
echo 'validate" value="';
|
||||
print_string('validate', 'quiz');
|
||||
echo '" /> ';
|
||||
if ($quiz->optionflags & QUIZ_ADAPTIVE) {
|
||||
echo '<input type="submit" name="';
|
||||
echo $question->name_prefix;
|
||||
echo 'mark" value="';
|
||||
print_string('mark', 'quiz');
|
||||
echo '" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the question for printing and returns the LaTeX source produced
|
||||
*
|
||||
* This function calls the RQP_Render operation to render the question
|
||||
* suitable for a printed problem or solution sheet in LaTeX and returns
|
||||
* the rendered output. It does not advance the state of the question session.
|
||||
* @return string The LaTeX output.
|
||||
* @param object $question The question to be rendered.
|
||||
* @param object $state The state to render the question in.
|
||||
* @param object $quiz The quiz to which the question belongs.
|
||||
* @param string $type Indicates if the question or the solution is to be
|
||||
* rendered. Possible values are 'question' and
|
||||
* 'solution'.
|
||||
*/
|
||||
/*function get_texsource(&$question, &$state, $quiz, $type) {
|
||||
// Perform the RQP render operation requesting tex output
|
||||
$output = remote_render($question, $state, 'print', RQP_URI_STATE .
|
||||
('solution' === $type ? 'solution' : 'normal'));
|
||||
if (false === $output || is_soap_fault($output)) {
|
||||
notify('Error: Rendering failed!');
|
||||
exit;
|
||||
}
|
||||
if (isset($output->bodyParts[RQP_URI_FORMAT . 'text/latex2e-parts/body'])) {
|
||||
$output = $output->bodyParts[RQP_URI_FORMAT . 'text/latex2e-parts/body']->content;
|
||||
}
|
||||
else {
|
||||
$output = '';
|
||||
}
|
||||
return $output;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Performs response processing and grading
|
||||
*
|
||||
* This function calls RQP_Render to perform response processing and grading
|
||||
* and updates the state accordingly. It also caches the rendering output in case
|
||||
* it is needed later.
|
||||
* @return boolean Indicates success or failure.
|
||||
* @param object $question The question to be graded.
|
||||
* @param object $state The state to render the question in. The grading
|
||||
* information is in ->grade, ->raw_grade and
|
||||
* ->penalty. The currently responses are in
|
||||
* ->responses. The last graded state is in
|
||||
* ->last_graded (hence the most recently graded
|
||||
* responses are in ->last_graded->responses). The
|
||||
* question type specific information is also
|
||||
* included. The ->raw_grade and ->penalty fields
|
||||
* are updated. The ->grade field is computed
|
||||
* automatically. The cumulative penalty is set
|
||||
* in ->penalty by adding to the penalty from the
|
||||
* most recently graded state. The method is able to
|
||||
* close the question session (preventing any further
|
||||
* attempts at this question) by setting
|
||||
* $state->event to QUIZ_EVENTCLOSE.
|
||||
* @param object $quiz The quiz to which the question belongs.
|
||||
*/
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
// Perform the grading and rendering
|
||||
$output = remote_render($question, $state, QUIZ_EVENTGRADE == $state->event
|
||||
|| QUIZ_EVENTCLOSE == $state->event, 'normal');
|
||||
if (false === $output || is_soap_fault($output)) {
|
||||
unset($output);
|
||||
return false;
|
||||
}
|
||||
$state->options->persistent_data = $output->persistentData;
|
||||
$state->options->template_vars = $output->templateVars;
|
||||
// Save the rendering results for later
|
||||
$state->options->renderoutput = $output;
|
||||
if (isset($output->outcomeVars[RQP_URI_OUTCOME . 'rawScore'])) {
|
||||
$state->raw_grade = (float) $output->outcomeVars[RQP_URI_OUTCOME .
|
||||
'rawScore'][0];
|
||||
if (isset($output->outcomeVars[RQP_URI_OUTCOME . 'penalty'])) {
|
||||
$state->penalty = (float) $output->outcomeVars[RQP_URI_OUTCOME .
|
||||
'penalty'][0];
|
||||
} else {
|
||||
$state->penalty = 0;
|
||||
}
|
||||
} else if (isset($output->outcomeVars[RQP_URI_OUTCOME . 'grade'])) {
|
||||
// This won't work quite as we would like but it is the best we can
|
||||
// do given that the server won't tell us the information we need
|
||||
$state->raw_grade = (float) $output->outcomeVars[RQP_URI_OUTCOME .
|
||||
'grade'][0];
|
||||
$state->penalty = 0;
|
||||
} else {
|
||||
$state->raw_grade = 0;
|
||||
$state->penalty = 0;
|
||||
}
|
||||
$state->raw_grade = ($state->raw_grade * ((float) $question->maxgrade))
|
||||
/ ((float) $question->options->maxscore);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes configuration settings for the question type on the quiz admin
|
||||
* page
|
||||
*
|
||||
* Returns an array of objects describing the options for the question type
|
||||
* to be included on the quiz module admin page.
|
||||
* This is currently only a link to the server setup page types.php
|
||||
* @return array Array of objects describing the configuration options to
|
||||
* be included on the quiz module admin page.
|
||||
*/
|
||||
function get_config_options() {
|
||||
global $CFG;
|
||||
|
||||
$link->name = 'managetypes';
|
||||
$link->link = 'types.php';
|
||||
return array($link);
|
||||
}
|
||||
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//// INITIATION - Without this line the question type is not in use... ///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
$QUIZ_QTYPES[RQP]= new quiz_rqp_qtype();
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,205 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* Remote question processing interface (using RQP)
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Alex Smith and others members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
// Load functions for the RQP-SOAP binding
|
||||
require_once('rqp.php');
|
||||
|
||||
// Remote item processing flags (cached from server)
|
||||
define('REMOTE_CLONING', 1);
|
||||
define('REMOTE_IMPLICIT_CLONING', 2);
|
||||
define('REMOTE_TEMPLATES_SUPPORTED', 3);
|
||||
define('REMOTE_TEMPLATE', 4);
|
||||
define('REMOTE_ADAPTIVE', 8);
|
||||
|
||||
// Global connection variable
|
||||
global $remote_connections;
|
||||
$remote_connections = array();
|
||||
|
||||
|
||||
/**
|
||||
* Create connection to rendering server if it does not already exist
|
||||
*
|
||||
* @param object $options This holds in particular the URL of the rendering
|
||||
* server in $options->rendering_server
|
||||
* @return boolean Indicates success or failure
|
||||
*/
|
||||
function remote_rendering_connect($options) {
|
||||
global $remote_connections;
|
||||
|
||||
if (!array_key_exists($options->rendering_server, $remote_connections)) {
|
||||
$remote_connections[$options->rendering_server] =
|
||||
rqp_connect($options->rendering_server);
|
||||
}
|
||||
if (!$remote_connections[$options->rendering_server]) {
|
||||
unset($remote_connections[$options->rendering_server]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create connection to cloning server if it does not already exist
|
||||
*
|
||||
* @param object $options This holds in particular the URL of the rendering
|
||||
* server in $options->cloning_server
|
||||
* @return boolean Indicates success or failure
|
||||
*/
|
||||
function remote_cloning_connect($options) {
|
||||
global $remote_connections;
|
||||
|
||||
if (!array_key_exists($options->cloning_server, $remote_connections)) {
|
||||
$remote_connections[$options->cloning_server] =
|
||||
rqp_connect($options->cloning_server);
|
||||
}
|
||||
if (!$remote_connections[$options->cloning_server]) {
|
||||
unset($remote_connections[$options->cloning_server]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function remote_server_info($options, $rendering_server=true,
|
||||
$cloning_server=true) {
|
||||
global $remote_connections;
|
||||
|
||||
$return = array();
|
||||
if ($rendering_server) {
|
||||
if (remote_rendering_connect($options)) {
|
||||
$return['rendering'] =
|
||||
rqp_server_info($remote_connections[$options->rendering_server]);
|
||||
if (is_soap_fault($return['rendering'])) {
|
||||
$return['rendering'] = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$return['rendering'] = false;
|
||||
}
|
||||
}
|
||||
if ($cloning_server) {
|
||||
if (remote_cloning_connect($options)) {
|
||||
$return['cloning'] =
|
||||
rqp_server_info($remote_connections[$options->cloning_server]);
|
||||
if (is_soap_fault($return['cloning'])) {
|
||||
$return['cloning'] = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$return['cloning'] = false;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function remote_item_info(&$options, $allinpackage=false) {
|
||||
global $remote_connections;
|
||||
|
||||
if (!remote_rendering_connect($options)) {
|
||||
return false;
|
||||
}
|
||||
if ($allinpackage) {
|
||||
$items = rqp_item_info($remote_connections[$options->rendering_server],
|
||||
$options->source, $options->format, 0);
|
||||
if ($items->length > 0) {
|
||||
$items = array($items);
|
||||
for ($i = 1; $i < $items[0]->length; $i++) {
|
||||
$items[$i] =
|
||||
rqp_item_info($remote_connections[$options->rendering_server],
|
||||
$options->source, $options->format, $i);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
return rqp_item_info($remote_connections[$options->rendering_server],
|
||||
$options->source, $options->format, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a remote rendering operation on the RQP question
|
||||
*
|
||||
* @param string $output One of 'normal', 'readonly' or 'print'.
|
||||
*/
|
||||
function remote_render(&$question, &$state, $advanceState=false,
|
||||
$output='normal', $from=0, $to=0) {
|
||||
global $remote_connections;
|
||||
|
||||
// Make the code more readable
|
||||
$options =& $question->options;
|
||||
|
||||
// Add prefix to response variable names
|
||||
$responses = array();
|
||||
foreach ($state->responses as $key => $resp) {
|
||||
$responses[$question->name_prefix . $key] = $resp;
|
||||
}
|
||||
|
||||
// Prepare the render format
|
||||
if ('print' === $output) {
|
||||
$renderFormat = RQP_URI_FORMAT . 'latex-2e';
|
||||
} else if ('readonly' === $output) {
|
||||
$renderFormat = RQP_URI_FORMAT . 'xhtml-1.0-print';
|
||||
} else {
|
||||
$renderFormat = RQP_URI_FORMAT . 'xhtml-1.0-web';
|
||||
}
|
||||
// Perform the RQP operation
|
||||
if (!remote_rendering_connect($options)) {
|
||||
return false;
|
||||
}
|
||||
$output = array();
|
||||
for ($i = $from; $i <= $to; $i++) {
|
||||
$output[$i] = rqp_render($remote_connections[$options->rendering_server],
|
||||
$options->source, $options->format, $state->options->persistent_data,
|
||||
$question->name_prefix, $responses, $advanceState,
|
||||
$renderFormat, $state->options->template_vars, 0, '', '', '', $i);
|
||||
if (false === $output[$i] || is_soap_fault($output[$i])) {
|
||||
return $output[$i];
|
||||
}
|
||||
}
|
||||
if ($from == $to) {
|
||||
return $output[$from];
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
function remote_session_info(&$question, &$state, $from=0, $to=0) {
|
||||
global $remote_connections;
|
||||
|
||||
// Make the code more readable
|
||||
$options =& $question->options;
|
||||
|
||||
// Perform the RQP operation
|
||||
if (!remote_rendering_connect($options)) {
|
||||
return false;
|
||||
}
|
||||
$info = array();
|
||||
for ($i = $from; $i <= $to; $i++) {
|
||||
$info[$i] = rqp_session_info($remote_connections[$options->rendering_server],
|
||||
$options->source, $options->format, $state->options->persistent_data,
|
||||
$state->options->template_vars, 0, '', $i);
|
||||
if (false === $info[$i] || is_soap_fault($info[$i])) {
|
||||
return $info[$i];
|
||||
}
|
||||
}
|
||||
if ($from == $to) {
|
||||
return $info[$from];
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
function remote_clone($options, $vars=array()) {
|
||||
global $remote_connections;
|
||||
|
||||
if (!remote_cloning_connect($options)) {
|
||||
return false;
|
||||
}
|
||||
return rqp_clone($remote_connections[$options->cloning_server],
|
||||
$options->source, $options->format, $vars);
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
if (empty($types)) { // display an error if no types have been defined
|
||||
echo '<p><b>' . get_string('notypes', 'quiz') . "</b></p>\n";
|
||||
if (isadmin()) {
|
||||
echo '<p align="center"><a href="questiontypes/rqp/edittype.php?sesskey='.sesskey().'">'.get_string('additemtype', 'quiz')."</a></p>\n";
|
||||
}
|
||||
}
|
||||
else { // otherwise display the page as normal
|
||||
?>
|
||||
<form name="theform" method="post" action="question.php">
|
||||
<table cellpadding="5">
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('category', 'quiz'); ?>:</b></td>
|
||||
<td>
|
||||
<?php quiz_category_select_menu($course->id, true, true, $question->category); ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('questionname', 'quiz'); ?>:</b></td>
|
||||
<td>
|
||||
<input type="text" name="name" size="50" value="<?php p($question->name); ?>" />
|
||||
<input type="hidden" name="questiontext" value="" />
|
||||
<input type="hidden" name="questiontextformat" value="0" />
|
||||
<input type="hidden" name="image" value="" />
|
||||
<?php
|
||||
if (isset($err['name'])) {
|
||||
formerr($err['name']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('itemtype', 'quiz'); ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
echo "<select name=\"type\">\n";
|
||||
foreach ($types as $type) {
|
||||
echo '<option value="' . s($type->id) . '"' . ($type->id === $question->options->type ? ' selected="selected"' : '') . '>' . s($type->name) . "</option>\n";
|
||||
}
|
||||
echo "</select>\n";
|
||||
if (isset($err['type'])) {
|
||||
formerr($err['type']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('itemsource', 'quiz'); ?>:</b>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
print_textarea(false, 20, 60, 630, 300, "source", $question->options->source);
|
||||
echo "<br />\n";
|
||||
if (isset($err['source'])) {
|
||||
formerr($err['source']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('itemsourceformat', 'quiz'); ?></b>
|
||||
</td>
|
||||
<td><input name="format" type="text" size="50" maxLength="255" value="<?php p($question->options->format); ?>" />
|
||||
<?php
|
||||
if (isset($err['format'])) {
|
||||
formerr($err['format']);
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
$QUIZ_QTYPES[$question->qtype]->print_replacement_options($question, $course, $contextquiz);
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question_form_end($question);
|
||||
} // end of else block for when some types have been defined
|
||||
?>
|
||||
</table>
|
||||
</form>
|
||||
@@ -0,0 +1,277 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* Library of functions binding RQP to SOAP
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Alex Smith and others members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
// Load the SOAP library that gives a unified wrapping to either the native
|
||||
// PHP5 SOAP extension if available or to nuSOAP otherwise.
|
||||
require_once('uni_soap.php');
|
||||
|
||||
/**
|
||||
* Base RQP URI for RQP-defined identifiers
|
||||
*
|
||||
* RQP defines standard URIs for common values of the parameters. Currently
|
||||
* there is no RQP domain so we define a base URI here so that it can be
|
||||
* changed later.
|
||||
*/
|
||||
define('RQP_URI_BASE', 'http://rqp.org/');
|
||||
|
||||
/**
|
||||
* RQP parameter URIs
|
||||
*
|
||||
* RQP defines standard URIs for common values of the parameters. These are
|
||||
* defined in several categories under different directories under the base
|
||||
* URI.
|
||||
*/
|
||||
define('RQP_URI_ERROR', RQP_URI_BASE . 'errors/');
|
||||
define('RQP_URI_FORMAT', RQP_URI_BASE . 'formats/');
|
||||
define('RQP_URI_OUTCOME', RQP_URI_BASE . 'outcomes/');
|
||||
define('RQP_URI_COMPONENT', RQP_URI_BASE . 'components/');
|
||||
|
||||
|
||||
/**
|
||||
* Start a SOAP connection
|
||||
*
|
||||
* @param string $server The URL of the RQP server that we want to connect to
|
||||
* @return mixed Returns a SoapClient object if connection is successful
|
||||
* or false in the case of a soap fault.
|
||||
*/
|
||||
function rqp_connect($server) {
|
||||
$connection = soap_connect($server . '?wsdl');
|
||||
if (is_soap_fault($connection)) {
|
||||
return false;
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server information using the RQP_ServerInformation operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_server_info($connection) {
|
||||
return soap_call($connection, 'RQP_ServerInformation', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item information using the RQP_ItemInformation operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_item_info($connection, $source, $format='', $index=0) {
|
||||
$itemInfo = soap_call($connection, 'RQP_ItemInformation',
|
||||
array('source'=>$source, 'format'=>$format, 'index'=>$index));
|
||||
if (is_soap_fault($itemInfo)) {
|
||||
return $itemInfo;
|
||||
}
|
||||
// put the sourceErrors into an associative array indexed by the
|
||||
// error identifier
|
||||
$sourceErrors = array();
|
||||
if (!empty($itemInfo->sourceErrors)) {
|
||||
foreach ($itemInfo->sourceErrors as $error) {
|
||||
$id = $error->identifier;
|
||||
unset($error->identifier);
|
||||
$sourceErrors[$id] = (object) $error;
|
||||
}
|
||||
}
|
||||
$itemInfo->sourceErrors = $sourceErrors;
|
||||
return $itemInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an item template to produce template variables using the RQP_ProcessTemplate operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_process_template($connection, $source, $format='') {
|
||||
$return = soap_call($connection, 'RQP_ProcessTemplate',
|
||||
array('source'=>$source, 'format'=>$format));
|
||||
if (is_soap_fault($return)) {
|
||||
return $return;
|
||||
}
|
||||
$templateVars = array();
|
||||
if (!empty($return->templateVars)) {
|
||||
foreach ($return->templateVars as $var) {
|
||||
$templateVars[$var->identifier] = $var->values;
|
||||
}
|
||||
}
|
||||
$return->templateVars = $templateVars;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone an item template using the RQP_ProcessTemplate operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @param array $templateVars Associative array of template variables
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_clone($connection, $source, $format='', $templateVars=array()) {
|
||||
// make an array of key-value pairs from the template variables array
|
||||
array_walk($templateVars, create_function('&$val, $key',
|
||||
'$val = (object) array(\'identifier\'=>$key, \'values\'=>$val);'));
|
||||
|
||||
$return = soap_call($connection, 'RQP_Clone', array('source'=>$source,
|
||||
'format'=>$format, 'templateVars'=>array_values($templateVars)));
|
||||
if (is_soap_fault($return)) {
|
||||
return $return;
|
||||
}
|
||||
$templateVars = array();
|
||||
if (!empty($return->templateVars)) {
|
||||
foreach ($return->templateVars as $var) {
|
||||
$templateVars[$var->identifier] = $var->values;
|
||||
}
|
||||
}
|
||||
$return->templateVars = $templateVars;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get runtime information about the item in the given state using the
|
||||
* RQP_SessionInformation operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @param string $persistentData String giving the state of the item session
|
||||
* @param array $templateVars Associative array of template variables
|
||||
* @param string $embedPrefix
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_session_info($connection, $source, $format='', $persistentData='',
|
||||
$templateVars=array(), $embedPrefix='', $index=0) {
|
||||
// make an array of key-value pairs from the template variables array
|
||||
array_walk($templateVars, create_function('&$val, $key',
|
||||
'$val = (object) array(\'identifier\'=>$key, \'values\'=>$val);'));
|
||||
|
||||
$return = soap_call($connection, 'RQP_SessionInformation',
|
||||
array('source'=>$source, 'format'=>$format, 'index'=>$index,
|
||||
'templateVars'=>array_values($templateVars),
|
||||
'persistentData'=>$persistentData, 'embedPrefix'=>$embedPrefix));
|
||||
if (is_soap_fault($return)) {
|
||||
return $return;
|
||||
}
|
||||
$templateVars = array();
|
||||
if (!empty($return->templateVars)) {
|
||||
foreach ($return->templateVars as $var) {
|
||||
$templateVars[$var->identifier] = $var->values;
|
||||
}
|
||||
}
|
||||
$return->templateVars = $templateVars;
|
||||
$responses = array();
|
||||
if (!empty($return->correctResponses)) {
|
||||
foreach ($return->correctResponses as $var) {
|
||||
$responses[$var->name] = $var->value;
|
||||
}
|
||||
}
|
||||
$return->correctResponses = $responses;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and render the item in the given state using the RQP_Render operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server that we want to connect to
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @param string $persistentData String giving the state of the item session
|
||||
* @param string $embedPrefix
|
||||
* @param array $responses Student responses from the form elements
|
||||
* @param boolean $advanceState Should the item be advanced to the next state?
|
||||
* @param anyURI $renderFormat
|
||||
* @param array $templateVars Associative array of template variables
|
||||
* @param anyURI $mediaBase
|
||||
* @param anyURI $appletBase
|
||||
* @param anyURI $modalFormat
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_render($connection, $source, $format='', $persistentData='',
|
||||
$embedPrefix='', $responses=array(), $advanceState=true, $renderFormat='',
|
||||
$templateVars=array(), $mediaBase='', $appletBase='',
|
||||
$modalFormat='', $index=0) {
|
||||
// make an array of key-value pairs from the template vars array
|
||||
array_walk($templateVars, create_function('&$val, $key',
|
||||
'$val = (object) array(\'identifier\'=>$key, \'values\'=>$val);'));
|
||||
|
||||
// make an array of name-value pairs from the responses array
|
||||
array_walk($responses, create_function('&$val, $key',
|
||||
'$val = (object) array(\'name\'=>$key, \'value\'=>$val);'));
|
||||
|
||||
$return = soap_call($connection, 'RQP_Render', array('source'=>$source,
|
||||
'format'=>$format, 'index'=>$index, 'templateVars'=>array_values($templateVars),
|
||||
'persistentData'=>$persistentData, 'responses'=>$responses,
|
||||
'advanceState'=>$advanceState, 'embedPrefix'=>$embedPrefix,
|
||||
'appletBase'=>$appletBase, 'mediaBase'=>$mediaBase,
|
||||
'renderFormat'=>$renderFormat, 'modalFormat'=>$modalFormat));
|
||||
if (is_soap_fault($return)) {
|
||||
return $return;
|
||||
}
|
||||
$outcomeVars = array();
|
||||
if (!empty($return->outcomeVars)) {
|
||||
foreach ($return->outcomeVars as $var) {
|
||||
$outcomeVars[$var->identifier] = $var->values;
|
||||
}
|
||||
}
|
||||
$return->outcomeVars = $outcomeVars;
|
||||
|
||||
$templateVars = array();
|
||||
if (!empty($return->templateVars)) {
|
||||
foreach ($return->templateVars as $var) {
|
||||
$templateVars[$var->identifier] = $var->values;
|
||||
}
|
||||
}
|
||||
$return->templateVars = $templateVars;
|
||||
|
||||
$output = array();
|
||||
if (!empty($return->output)) {
|
||||
foreach ($return->output as $out) {
|
||||
$id = $out->identifier;
|
||||
unset($out->identifier);
|
||||
$output[$id] = $out;
|
||||
}
|
||||
}
|
||||
$return->output = $output;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to the RQP_Author operation
|
||||
*
|
||||
* @param SoapClient $connection The URL of the RQP server
|
||||
* @param string $source Item source
|
||||
* @param anyURI $format Item format
|
||||
* @param string $persistentData String giving the state of the authoring session
|
||||
* @param string $embedPrefix
|
||||
* @param array $responses Teacher responses from the form elements
|
||||
* @param anyURI $renderFormat
|
||||
* @return object Object holding the return parameters or a SoapFault.
|
||||
*/
|
||||
function rqp_author($connection, $source, $format='', $persistentData='',
|
||||
$embedPrefix='', $responses=array(), $renderFormat='') {
|
||||
|
||||
// make an array of name-value pairs from the responses array
|
||||
array_walk($responses, create_function('&$val, $key',
|
||||
'$val = (object) array(\'name\'=>$key, \'value\'=>$val);'));
|
||||
|
||||
return soap_call($connection, 'RQP_Author', array('source'=>$source,
|
||||
'format'=>$format, 'persistentData'=>$persistentData, 'responses'=>$responses,
|
||||
'embedPrefix'=>$embedPrefix, 'renderFormat'=>$renderFormat));
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
// This page lists all the available RQP question types
|
||||
|
||||
require_once('../../../../config.php');
|
||||
require_once('lib.php');
|
||||
|
||||
// Check user admin
|
||||
require_login();
|
||||
if (!isadmin()) {
|
||||
error('You need to be an admin user to use this page.', $CFG->wwwroot . '/login/index.php');
|
||||
}
|
||||
|
||||
if (!$site = get_site()) {
|
||||
error('Site isn\'t defined!');
|
||||
}
|
||||
|
||||
$sesskey = required_param('sesskey', PARAM_RAW);
|
||||
if (!confirm_sesskey($sesskey)) {
|
||||
error(get_string('confirmsesskeybad', 'error'));
|
||||
}
|
||||
|
||||
// Print the header
|
||||
$stradmin = get_string('admin');
|
||||
$strconfiguration = get_string('configuration');
|
||||
$strmanagemodules = get_string('managemodules');
|
||||
$strmodulename = get_string('modulename', 'quiz');
|
||||
$stritemtypes = get_string('itemtypes', 'quiz');
|
||||
$navigation = '<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/index.php">' . $stradmin . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/configure.php">' . $strconfiguration . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/modules.php">' . $strmanagemodules . '</a> -> ' .
|
||||
'<a href="' . s($CFG->wwwroot) . '/' . s($CFG->admin) . '/module.php?module=quiz&sesskey=' . s(rawurlencode($sesskey)) . '">' . $strmodulename . '</a> -> ' .
|
||||
$stritemtypes;
|
||||
print_header($site->shortname . ': ' . $strmodulename . ': ' . $stritemtypes, $site->fullname, $navigation, '', '', true, '', '');
|
||||
|
||||
// Get list of types
|
||||
$types = quiz_rqp_get_types();
|
||||
|
||||
// Setup the table
|
||||
$strname = get_string('name');
|
||||
$strservers = get_string('servers', 'quiz');
|
||||
$strcapabilities = get_string('capabilities', 'quiz');
|
||||
$straction = get_string('action');
|
||||
$stredit = get_string('edit');
|
||||
$strdelete = get_string('delete');
|
||||
$table->head = array($strname, $strservers, $strcapabilities, $straction);
|
||||
$table->align = array('left', 'left', 'left', 'left');
|
||||
$table->size = array('*', '*', '*', '*');
|
||||
|
||||
if ($types) {
|
||||
foreach ($types as $type) {
|
||||
$link = '<a href="edittype.php?id=' . s(rawurlencode($type->id)) . '&sesskey=' . s(rawurlencode($sesskey)) . '">' . s($type->name) . '</a>';
|
||||
$servers = get_string('rendering', 'quiz') . ': ' . s($type->rendering_server);
|
||||
if ($type->cloning_server) {
|
||||
$servers .= "<br />\n" . get_string('cloning', 'quiz') . ': ' . s($type->cloning_server);
|
||||
}
|
||||
$capabilities = '';
|
||||
$actions = '<a title="' . $stredit . '" href="edittype.php?id=' . s(rawurlencode($type->id)) . '&sesskey=' . s(rawurlencode($sesskey)) . '"><img src="../../../../pix/t/edit.gif" border="0" alt="' . $stredit . '" /></a> <a title="' . $strdelete . '" href="edittype.php?delete=' . s(rawurlencode($type->id)) . '&sesskey=' . s(rawurlencode($sesskey)) . '"><img src="../../../../pix/t/delete.gif" border="0" alt="' . $strdelete . '" /></a>';
|
||||
$table->data[] = array($link, $servers, $capabilities, $actions);
|
||||
}
|
||||
}
|
||||
|
||||
// Print the table
|
||||
print_heading($stritemtypes);
|
||||
print_table($table);
|
||||
|
||||
// Print add link
|
||||
echo '<p align="center"><a href="edittype.php?sesskey=' . s(rawurlencode($sesskey)) . '">' . get_string('additemtype', 'quiz') . "</a></p>\n";
|
||||
|
||||
// Finish the page
|
||||
print_footer();
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
// Web services wrapper library script
|
||||
|
||||
if (class_exists('SoapClient')) {
|
||||
// Use the native PHP5 support
|
||||
require_once('uni_soap.php5');
|
||||
}
|
||||
else{
|
||||
// Use nuSOAP instead
|
||||
require_once('nusoap.php');
|
||||
|
||||
function make_soap_fault($faultcode, $faultstring, $faultactor='', $detail='', $faultname='', $headerfault='') {
|
||||
return new soap_fault($faultcode, $faultactor, $faultstring, $detail);
|
||||
}
|
||||
|
||||
function is_soap_fault($obj) {
|
||||
if (!is_object($obj))
|
||||
return false;
|
||||
return (strcasecmp(get_class($obj), 'soap_fault') === 0);
|
||||
}
|
||||
|
||||
if (class_exists('soap_client')) {
|
||||
function soap_connect($wsdl, $trace=false) {
|
||||
return new soap_client($wsdl, 'wsdl');
|
||||
}
|
||||
}
|
||||
else {
|
||||
function soap_connect($wsdl, $trace=false) {
|
||||
return new soapclient($wsdl, 'wsdl');
|
||||
}
|
||||
}
|
||||
|
||||
function soap_call($connection, $call, $params) {
|
||||
$result = $connection->call($call, $params);
|
||||
if ($connection->fault) {
|
||||
return @make_soap_fault($result['faultcode'], $result['faultstring'], '', $result['detail']);
|
||||
}
|
||||
if ($connection->error_str) {
|
||||
return @make_soap_fault('server', $connection->error_str, '', $connection->response);
|
||||
}
|
||||
/* Fix objects being returned as associative arrays (to fit with PHP5
|
||||
SOAP support */
|
||||
return fix_object($result);
|
||||
}
|
||||
|
||||
function soap_serve($wsdl, $functions) {
|
||||
global $HTTP_RAW_POST_DATA;
|
||||
|
||||
$s = new soap_server($wsdl);
|
||||
$s->service(isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '');
|
||||
}
|
||||
|
||||
function get_last_soap_messages($connection) {
|
||||
return array('request'=>$connection->request, 'response'=>$connection->response);
|
||||
}
|
||||
|
||||
/* Fix objects being returned as associative arrays (to fit with PHP5
|
||||
SOAP support */
|
||||
function fix_object($value) {
|
||||
if (is_array($value)) {
|
||||
$value = array_map('fix_object', $value);
|
||||
$keys = array_keys($value);
|
||||
/* check for arrays of length 1 (they get given the key "item"
|
||||
rather than 0 by nusoap) */
|
||||
if (1 === count($value) && 'item' === $keys[0]) {
|
||||
$value = array_values($value);
|
||||
}
|
||||
else {
|
||||
/* cast to object if it is an associative array with at least
|
||||
one string key */
|
||||
foreach ($keys as $key) {
|
||||
if (is_string($key)) {
|
||||
$value = (object) $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Fix simple type encoding - not needed for nuSOAP
|
||||
function soap_encode($value, $name, $type, $namespace, $encode=0) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Fix complex type encoding - not needed for nuSOAP
|
||||
function soap_encode_object($value, $name, $type, $namespace) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Fix array encoding - not needed for nuSOAP
|
||||
function soap_encode_array($value, $name, $type, $namespace) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
// In both cases...
|
||||
function handle_soap_wsdl_request($wsdlfile, $address=false) {
|
||||
header('Content-type: application/wsdl+xml');
|
||||
$wsdl = file_get_contents($wsdlfile);
|
||||
if (false !== $address) {
|
||||
if (true === $address) {
|
||||
$address = (($_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'];
|
||||
}
|
||||
$wsdl = str_replace('###SERVER_ADDRESS###', $address, $wsdl);
|
||||
}
|
||||
echo $wsdl;
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* Library of SOAP functions wrapping the PHP5 SOAP functions
|
||||
*
|
||||
* The purpose of this wrapping is to make it easier to use alternative SOAP
|
||||
* implementations like nuSOAP in case the PHP5 SOAP extension is not available
|
||||
* @version $Id$
|
||||
* @author Alex Smith and others members of the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new SoapClient object
|
||||
*
|
||||
* @param string $wsdl URI of the WSDL file
|
||||
* @param boolean $trace indicates if the soap messages should be saved (i.e. if
|
||||
* get_soap_messages is used) and should be used only for debugging
|
||||
* @return mixed Returns either a SoapClient object or, if the connection failed,
|
||||
* a SoapFault object.
|
||||
*/
|
||||
function soap_connect($wsdl, $trace=false) {
|
||||
try {
|
||||
$connection = new SoapClient($wsdl, array('soap_version'=>SOAP_1_1, 'exceptions'=>true, 'trace'=>$trace));
|
||||
}
|
||||
catch (SoapFault $f) {
|
||||
$connection = $f;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$connection = new SoapFault('client', 'Could not connect to the service');
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a call to a SoapClient
|
||||
*
|
||||
* @param SoapClient $connection The SoapClient to call
|
||||
* @param string $call Operation to be performed by client
|
||||
* @param array $params Parameters for the call
|
||||
* @return mixed The return parameters of the operation or a SoapFault
|
||||
* If the operation returned several parameters then these
|
||||
* are returned as an object rather than an array
|
||||
*/
|
||||
function soap_call($connection, $call, $params) {
|
||||
try {
|
||||
$return = $connection->__soapCall($call, $params);
|
||||
}
|
||||
catch (SoapFault $f) {
|
||||
$return = $f;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$return = new SoapFault('client', 'Could call the method');
|
||||
}
|
||||
// return multiple parameters using an object rather than an array
|
||||
if (is_array($return)) {
|
||||
$keys = array_keys($return);
|
||||
$assoc = true;
|
||||
foreach ($keys as $key) {
|
||||
if (!is_string($key)) {
|
||||
$assoc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($assoc)
|
||||
$return = (object) $return;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
function soap_serve($wsdl, $functions) {
|
||||
// create server object
|
||||
$s = new SoapServer($wsdl);
|
||||
// export functions
|
||||
foreach ($functions as $func)
|
||||
$s->addFunction($func);
|
||||
// handle the request
|
||||
$s->handle();
|
||||
}
|
||||
|
||||
function make_soap_fault($faultcode, $faultstring, $faultactor='', $detail='', $faultname='', $headerfault='') {
|
||||
return new SoapFault($faultcode, $faultstring, $faultactor, $detail, $faultname, $headerfault);
|
||||
}
|
||||
|
||||
function get_last_soap_messages($connection) {
|
||||
return array('request'=>$connection->__getLastRequest(), 'response'=>$connection->__getLastResponse());
|
||||
}
|
||||
|
||||
// Fix simple type encoding - work around a bug in PHP
|
||||
function soap_encode($value, $name, $type, $namespace, $encode=XSD_STRING) {
|
||||
$value = new SoapVar($value, $encode, $type, $namespace);
|
||||
if ('' === $name)
|
||||
return $value;
|
||||
return new SoapParam($value, $name);
|
||||
}
|
||||
|
||||
// Fix complex type encoding - work around a bug in PHP
|
||||
function soap_encode_object($value, $name, $type, $namespace) {
|
||||
if (!is_object($value))
|
||||
return $value;
|
||||
$value = new SoapVar($value, SOAP_ENC_OBJECT, $type, $namespace);
|
||||
if ('' === $name)
|
||||
return $value;
|
||||
return new SoapParam($value, $name);
|
||||
}
|
||||
|
||||
// Fix array encoding - work around a bug in PHP
|
||||
function soap_encode_array($value, $name, $type, $namespace) {
|
||||
if (!is_array($value))
|
||||
return $value;
|
||||
$value = new SoapVar($value, SOAP_ENC_ARRAY, 'ArrayOf' . $type, $namespace);
|
||||
if ('' === $name)
|
||||
return $value;
|
||||
return new SoapParam($value, $name);
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,204 +1,229 @@
|
||||
<?php // $Id$
|
||||
|
||||
///////////////////
|
||||
/// SHORTANSWER ///
|
||||
///////////////////
|
||||
|
||||
/// QUESTION TYPE CLASS //////////////////
|
||||
|
||||
///
|
||||
/// This class contains some special features in order to make the
|
||||
/// question type embeddable within a multianswer (cloze) question
|
||||
///
|
||||
|
||||
class quiz_shortanswer_qtype extends quiz_default_questiontype {
|
||||
|
||||
function get_answers($question, $addedcondition='') {
|
||||
// The added condition is one addition that has been added
|
||||
// to the behaviour of this question type in order to make
|
||||
// it embeddable within a multianswer (embedded cloze) question
|
||||
|
||||
global $CFG;
|
||||
|
||||
// There can be multiple answers
|
||||
return get_records_sql("SELECT a.*, sa.usecase
|
||||
FROM {$CFG->prefix}quiz_shortanswer sa,
|
||||
{$CFG->prefix}quiz_answers a
|
||||
WHERE sa.question = '$question->id'
|
||||
AND sa.question = a.question "
|
||||
. $addedcondition);
|
||||
|
||||
}
|
||||
|
||||
function name() {
|
||||
return 'shortanswer';
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
|
||||
$oldanswers = array();
|
||||
}
|
||||
|
||||
$answers = array();
|
||||
$maxfraction = -1;
|
||||
|
||||
// Insert all the new answers
|
||||
foreach ($question->answer as $key => $dataanswer) {
|
||||
if ($dataanswer != "") {
|
||||
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
|
||||
$answer = $oldanswer;
|
||||
$answer->answer = trim($dataanswer);
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!update_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not update quiz answer! (id=$answer->id)";
|
||||
return $result;
|
||||
}
|
||||
} else { // This is a completely new answer
|
||||
unset($answer);
|
||||
$answer->answer = trim($dataanswer);
|
||||
$answer->question = $question->id;
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!$answer->id = insert_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not insert quiz answer!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$answers[] = $answer->id;
|
||||
if ($question->fraction[$key] > $maxfraction) {
|
||||
$maxfraction = $question->fraction[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options = get_record("quiz_shortanswer", "question", $question->id)) {
|
||||
$options->answers = implode(",",$answers);
|
||||
$options->usecase = $question->usecase;
|
||||
if (!update_record("quiz_shortanswer", $options)) {
|
||||
$result->error = "Could not update quiz shortanswer options! (id=$options->id)";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
unset($options);
|
||||
$options->question = $question->id;
|
||||
$options->answers = implode(",",$answers);
|
||||
$options->usecase = $question->usecase;
|
||||
if (!insert_record("quiz_shortanswer", $options)) {
|
||||
$result->error = "Could not insert quiz shortanswer options!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform sanity checks on fractional grades
|
||||
if ($maxfraction != 1) {
|
||||
$maxfraction = $maxfraction * 100;
|
||||
$result->noticeyesno = get_string("fractionsnomax", "quiz", $maxfraction);
|
||||
return $result;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
/// This implementation is also used by question type NUMERICAL
|
||||
|
||||
/// Print question text and media
|
||||
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat,
|
||||
NULL, $quiz->course);
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
/// Print input controls
|
||||
|
||||
$stranswer = get_string("answer", "quiz");
|
||||
if (isset($question->response[$nameprefix])) {
|
||||
$value = ' value="'.htmlSpecialChars($question->response[$nameprefix]).'" ';
|
||||
} else {
|
||||
$value = ' value="" ';
|
||||
}
|
||||
$inputname = ' name="'.$nameprefix.'" ';
|
||||
echo "<p align=\"right\">$stranswer: <input type=\"text\" $readonly $inputname size=\"80\" $value /></p>";
|
||||
|
||||
if ($quiz->feedback && isset($answers[$nameprefix])
|
||||
&& $feedback = $answers[$nameprefix]->feedback) {
|
||||
quiz_print_comment($feedback);
|
||||
}
|
||||
if ($readonly && $quiz->correctanswers) {
|
||||
$delimiter = '';
|
||||
$correct = '';
|
||||
foreach ($correctanswers as $correctanswer) {
|
||||
$correct .= $delimiter.$correctanswer->answer;
|
||||
$delimiter = ', ';
|
||||
}
|
||||
quiz_print_correctanswer($correct);
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix, $addedanswercondition='') {
|
||||
|
||||
if (isset($question->response[$nameprefix])) {
|
||||
$response0 = trim(stripslashes($question->response[$nameprefix]));
|
||||
} else {
|
||||
$response0 = '';
|
||||
}
|
||||
$answers = $this->get_answers($question, $addedanswercondition);
|
||||
|
||||
/// Determine ->answers[]
|
||||
$result->answers = array();
|
||||
if ('' !== $response0) {
|
||||
|
||||
/// These are things to protect in the strings when wildcards are used
|
||||
$search = array('\\', '+', '(', ')', '[', ']', '-');
|
||||
$replace = array('\\\\', '\+', '\(', '\)', '\[', '\]', '\-');
|
||||
|
||||
foreach ($answers as $answer) {
|
||||
|
||||
$answer->answer = trim($answer->answer); // Just in case
|
||||
|
||||
if (empty($result->answers) || $answer->fraction
|
||||
> $result->answers[$nameprefix]->fraction) {
|
||||
|
||||
if (!$answer->usecase) { // Don't compare case
|
||||
$response0 = strtolower($response0);
|
||||
$answer0 = strtolower($answer->answer);
|
||||
} else {
|
||||
$answer0 = $answer->answer;
|
||||
}
|
||||
|
||||
if (strpos(' '.$answer0, '*')) {
|
||||
$answer0 = str_replace('\*','@@@@@@',$answer0);
|
||||
$answer0 = str_replace('*','.*',$answer0);
|
||||
$answer0 = str_replace($search, $replace, $answer0);
|
||||
$answer0 = str_replace('@@@@@@', '\*',$answer0);
|
||||
|
||||
if (ereg('^'.$answer0.'$', $response0)) {
|
||||
$result->answers[$nameprefix] = $answer;
|
||||
}
|
||||
|
||||
} else if ($answer0 == $response0) {
|
||||
$result->answers[$nameprefix] = $answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result->grade = isset($result->answers[$nameprefix])
|
||||
? $result->answers[$nameprefix]->fraction
|
||||
: 0.0;
|
||||
$result->correctanswers = quiz_extract_correctanswers($answers,
|
||||
$nameprefix);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//// INITIATION - Without this line the question type is not in use... ///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
$QUIZ_QTYPES[SHORTANSWER]= new quiz_shortanswer_qtype();
|
||||
|
||||
?>
|
||||
<?php // $Id$
|
||||
|
||||
///////////////////
|
||||
/// SHORTANSWER ///
|
||||
///////////////////
|
||||
|
||||
/// QUESTION TYPE CLASS //////////////////
|
||||
|
||||
///
|
||||
/// This class contains some special features in order to make the
|
||||
/// question type embeddable within a multianswer (cloze) question
|
||||
///
|
||||
|
||||
class quiz_shortanswer_qtype extends quiz_default_questiontype {
|
||||
|
||||
function name() {
|
||||
return 'shortanswer';
|
||||
}
|
||||
|
||||
function get_question_options(&$question) {
|
||||
// Get additional information from database
|
||||
// and attach it to the question object
|
||||
if (!$question->options = get_record('quiz_shortanswer', 'question', $question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$question->options->answers = get_records('quiz_answers', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question answers!');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save_question_options($question) {
|
||||
if (!$oldanswers = get_records("quiz_answers", "question", $question->id, "id ASC")) {
|
||||
$oldanswers = array();
|
||||
}
|
||||
|
||||
$answers = array();
|
||||
$maxfraction = -1;
|
||||
|
||||
// Insert all the new answers
|
||||
foreach ($question->answer as $key => $dataanswer) {
|
||||
if ($dataanswer != "") {
|
||||
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
|
||||
$answer = $oldanswer;
|
||||
$answer->answer = trim($dataanswer);
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!update_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not update quiz answer! (id=$answer->id)";
|
||||
return $result;
|
||||
}
|
||||
} else { // This is a completely new answer
|
||||
unset($answer);
|
||||
$answer->answer = trim($dataanswer);
|
||||
$answer->question = $question->id;
|
||||
$answer->fraction = $question->fraction[$key];
|
||||
$answer->feedback = $question->feedback[$key];
|
||||
if (!$answer->id = insert_record("quiz_answers", $answer)) {
|
||||
$result->error = "Could not insert quiz answer!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$answers[] = $answer->id;
|
||||
if ($question->fraction[$key] > $maxfraction) {
|
||||
$maxfraction = $question->fraction[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options = get_record("quiz_shortanswer", "question", $question->id)) {
|
||||
$options->answers = implode(",",$answers);
|
||||
$options->usecase = $question->usecase;
|
||||
if (!update_record("quiz_shortanswer", $options)) {
|
||||
$result->error = "Could not update quiz shortanswer options! (id=$options->id)";
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
unset($options);
|
||||
$options->question = $question->id;
|
||||
$options->answers = implode(",",$answers);
|
||||
$options->usecase = $question->usecase;
|
||||
if (!insert_record("quiz_shortanswer", $options)) {
|
||||
$result->error = "Could not insert quiz shortanswer options!";
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform sanity checks on fractional grades
|
||||
if ($maxfraction != 1) {
|
||||
$maxfraction = $maxfraction * 100;
|
||||
$result->noticeyesno = get_string("fractionsnomax", "quiz", $maxfraction);
|
||||
return $result;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls(&$question, &$state, $quiz, $options) {
|
||||
/// This implementation is also used by question type NUMERICAL
|
||||
|
||||
$answers = &$question->options->answers;
|
||||
$correctanswers = $this->get_correct_responses($question, $state);
|
||||
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
|
||||
$nameprefix = $question->name_prefix;
|
||||
|
||||
/// Print question text and media
|
||||
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat,
|
||||
NULL, $quiz->course);
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
/// Print input controls
|
||||
|
||||
$stranswer = get_string("answer", "quiz");
|
||||
if (isset($state->responses[''])) {
|
||||
$value = ' value="'.htmlSpecialChars($state->responses['']).'" ';
|
||||
} else {
|
||||
$value = ' value="" ';
|
||||
}
|
||||
$inputname = ' name="'.$nameprefix.'" ';
|
||||
echo "<p align=\"right\">$stranswer: <input type=\"text\" $readonly $inputname size=\"80\" $value /></p>";
|
||||
|
||||
if ($options->feedback) {
|
||||
$testedstate = clone($state);
|
||||
$teststate = clone($state);
|
||||
foreach($answers as $answer) {
|
||||
$teststate->responses[''] = trim($answer->answer);
|
||||
if($this->compare_responses($question, $testedstate, $teststate)) {
|
||||
quiz_print_comment($answer->feedback);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($options->readonly && $options->correct_responses) {
|
||||
$delimiter = '';
|
||||
$correct = '';
|
||||
if ($correctanswers) {
|
||||
foreach ($correctanswers as $correctanswer) {
|
||||
$correct .= $delimiter.$correctanswer;
|
||||
$delimiter = ', ';
|
||||
}
|
||||
}
|
||||
quiz_print_correctanswer($correct);
|
||||
}
|
||||
}
|
||||
|
||||
function grade_responses(&$question, &$state, $quiz) {
|
||||
$answers = &$question->options->answers;
|
||||
$testedstate = clone($state);
|
||||
$teststate = clone($state);
|
||||
|
||||
foreach($answers as $answer) {
|
||||
$teststate->responses[''] = trim($answer->answer);
|
||||
if($this->compare_responses($question, $testedstate, $teststate)) {
|
||||
if (empty($state->raw_grade) && $state->raw_grade < $answer->fraction) {
|
||||
$state->raw_grade = $answer->fraction;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($state->raw_grade)) {
|
||||
$state->raw_grade = 0;
|
||||
}
|
||||
|
||||
// Make sure we don't assign negative or too high marks
|
||||
$state->raw_grade = min(max((float) $state->raw_grade,
|
||||
0.0), 1.0) * $question->maxgrade;
|
||||
$state->penalty = $question->penalty;
|
||||
// Only allow one attempt at the question
|
||||
// $state->event = QUIZ_EVENTCLOSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
function compare_responses(&$question, &$state, &$teststate) {
|
||||
if (isset($state->responses[''])) {
|
||||
$response0 = trim(stripslashes($state->responses['']));
|
||||
} else {
|
||||
$response0 = '';
|
||||
}
|
||||
|
||||
if (isset($teststate->responses[''])) {
|
||||
$response1 = trim(stripslashes($teststate->responses['']));
|
||||
} else {
|
||||
$response1 = '';
|
||||
}
|
||||
|
||||
if (!$question->options->usecase) { // Don't compare case
|
||||
$response0 = strtolower($response0);
|
||||
$response1 = strtolower($response1);
|
||||
}
|
||||
|
||||
/// These are things to protect in the strings when wildcards are used
|
||||
$search = array('\\', '+', '(', ')', '[', ']', '-');
|
||||
$replace = array('\\\\', '\+', '\(', '\)', '\[', '\]', '\-');
|
||||
|
||||
if (strpos(' '.$response1, '*')) {
|
||||
$answer0 = str_replace('\*','@@@@@@',$response1);
|
||||
$answer0 = str_replace('*','.*',$response1);
|
||||
$answer0 = str_replace($search, $replace, $response1);
|
||||
$answer0 = str_replace('@@@@@@', '\*',$response1);
|
||||
|
||||
if (ereg('^'.$response1.'$', $response0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if ($response1 == $response0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function print_question_grading_details(&$question, &$state, $quiz, $options) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//// INITIATION - Without this line the question type is not in use... ///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
$QUIZ_QTYPES[SHORTANSWER]= new quiz_shortanswer_qtype();
|
||||
|
||||
?>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($err["questiontext"])) {
|
||||
formerr($err["questiontext"]);
|
||||
formerr($err["questiontext"]);
|
||||
echo "<br />";
|
||||
}
|
||||
print_textarea($usehtmleditor, 15, 60, 630, 300, "questiontext", $question->questiontext);
|
||||
@@ -65,7 +65,7 @@
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("casesensitive", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<?php
|
||||
<?php
|
||||
unset($menu);
|
||||
$menu[0] = get_string("caseno", "quiz");
|
||||
$menu[1] = get_string("caseyes", "quiz");
|
||||
@@ -73,13 +73,21 @@
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("penalty", "quiz") ?>:</b></td>
|
||||
<td>
|
||||
<input type="text" name="penalty" size="3" value="<?php p($question->penalty) ?>" alt="<?php print_string("penalty", "quiz") ?>" />
|
||||
<?php helpbutton('penalty', get_string('penalty', 'quiz'), 'quiz'); ?>
|
||||
<?php if (isset($err["penalty"])) formerr($err["penalty"]); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string("correctanswers", "quiz") ?></b>:</td>
|
||||
<td>
|
||||
<?php print_string("filloutoneanswer", "quiz") ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
<?php
|
||||
for ($i=1; $i<=QUIZ_MAX_NUMBER_ANSWERS; $i++) {
|
||||
?>
|
||||
<tr valign="top">
|
||||
@@ -115,7 +123,7 @@
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
<?php
|
||||
$QUIZ_QTYPES[$question->qtype]->print_replacement_options($question, $course, $contextquiz);
|
||||
$QUIZ_QTYPES[$question->qtype]->print_question_form_end($question);
|
||||
?>
|
||||
|
||||
@@ -78,92 +78,156 @@ class quiz_truefalse_qtype extends quiz_default_questiontype {
|
||||
return true;
|
||||
}
|
||||
|
||||
function print_question_formulation_and_controls($question,
|
||||
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
|
||||
|
||||
/**
|
||||
* Loads the question type specific options for the question.
|
||||
*/
|
||||
function get_question_options(&$question) {
|
||||
// Get additional information from database
|
||||
// and attach it to the question object
|
||||
if (!$question->options = get_record('quiz_truefalse', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question options!');
|
||||
return false;
|
||||
}
|
||||
// Load possible answers
|
||||
if (!$answers = get_records('quiz_answers', 'question',
|
||||
$question->id)) {
|
||||
notify('Error: Missing question answers!');
|
||||
return false;
|
||||
}
|
||||
$question->options->answers = array(
|
||||
'true' => $answers[$question->options->trueanswer],
|
||||
'false' => $answers[$question->options->falseanswer]);
|
||||
|
||||
if (!$options = get_record("quiz_truefalse", "question", $question->id)) {
|
||||
notify("Error: Missing question options!");
|
||||
}
|
||||
if (!$true = get_record("quiz_answers", "id", $options->trueanswer)) {
|
||||
notify("Error: Missing question answers!");
|
||||
}
|
||||
if (!$false = get_record("quiz_answers", "id", $options->falseanswer)) {
|
||||
notify("Error: Missing question answers!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prints the main content of the question including any interactions
|
||||
*/
|
||||
function print_question_formulation_and_controls(&$question, &$state,
|
||||
$quiz, $options) {
|
||||
|
||||
$answers = &$question->options->answers;
|
||||
$correctanswers = $this->get_correct_responses($question, $state);
|
||||
$readonly = $options->readonly ? ' disabled="disabled"' : '';
|
||||
|
||||
// Print question formulation
|
||||
|
||||
echo format_text($question->questiontext,
|
||||
$question->questiontextformat,
|
||||
NULL, $quiz->course);
|
||||
quiz_print_possible_question_image($quiz->id, $question);
|
||||
|
||||
// Print input controls
|
||||
// Update the answer strings
|
||||
$stranswer = get_string('answer', 'quiz');
|
||||
$strlastanswer = get_string('lastanswer', 'quiz');
|
||||
|
||||
$stranswer = get_string("answer", "quiz");
|
||||
|
||||
if (!$true->answer) {
|
||||
$true->answer = get_string("true", "quiz");
|
||||
if (!$answers['true']->answer) {
|
||||
$answers['true']->answer = get_string('true', 'quiz');
|
||||
}
|
||||
if (!$false->answer) {
|
||||
$false->answer = get_string("false", "quiz");
|
||||
if (!$answers['false']->answer) {
|
||||
$answers['false']->answer = get_string('false', 'quiz');
|
||||
}
|
||||
|
||||
$truechecked = "";
|
||||
$falsechecked = "";
|
||||
|
||||
if (!isset($question->response[$nameprefix])) {
|
||||
$question->response[$nameprefix] = '';
|
||||
// Work out the selected answer and last marked answer
|
||||
$selected = '';
|
||||
$marked = '';
|
||||
$teststate = clone($state);
|
||||
$teststate->responses[''] = $answers['true']->answer;
|
||||
if ($this->compare_responses($question, $state, $teststate)) {
|
||||
$selected = 'true';
|
||||
}
|
||||
if ($true->id == $question->response[$nameprefix]) {
|
||||
$truechecked = 'checked="checked"';
|
||||
} else if ($false->id == $question->response[$nameprefix]) {
|
||||
$falsechecked = 'checked="checked"';
|
||||
if ($this->compare_responses($question, $state->last_graded,
|
||||
$teststate)) {
|
||||
$marked = 'true';
|
||||
}
|
||||
$teststate->responses[''] = $answers['false']->answer;
|
||||
if ($this->compare_responses($question, $state, $teststate)) {
|
||||
$selected = 'false';
|
||||
}
|
||||
if ($this->compare_responses($question, $state->last_graded,
|
||||
$teststate)) {
|
||||
$marked = 'false';
|
||||
}
|
||||
|
||||
$truecorrect = "";
|
||||
$falsecorrect = "";
|
||||
if ($readonly && $quiz->correctanswers) {
|
||||
if (!empty($correctanswers[$nameprefix.$true->id])) {
|
||||
$truecorrect = 'class="highlight"';
|
||||
}
|
||||
if (!empty($correctanswers[$nameprefix.$false->id])) {
|
||||
$falsecorrect = 'class="highlight"';
|
||||
}
|
||||
/* Work out the correct answer if feedback or correct responses are
|
||||
requested */
|
||||
if ($options->feedback || $options->correct_responses) {
|
||||
$correctstate = clone($state);
|
||||
$correctstate->responses[''] = $correctanswers[''];
|
||||
$correct = '';
|
||||
if (!is_null($correctstate->responses)) {
|
||||
$teststate->responses[''] = $answers['true']->answer;
|
||||
if ($this->compare_responses($question, $correctstate,
|
||||
$teststate)) {
|
||||
$correct = 'true';
|
||||
}
|
||||
$teststate->responses[''] = $answers['false']->answer;
|
||||
if ($this->compare_responses($question, $correctstate,
|
||||
$teststate)) {
|
||||
$correct = 'false';
|
||||
}
|
||||
}
|
||||
}
|
||||
$inputname = ' name="'.$nameprefix.'" ';
|
||||
echo "<table align=\"right\" cellpadding=\"5\"><tr><td align=\"right\">$stranswer: </td>";
|
||||
echo "<td $truecorrect>";
|
||||
echo "<input $truechecked type=\"radio\" $readonly $inputname value=\"$true->id\" alt=\"".s($true->answer)."\" />$true->answer";
|
||||
echo "</td><td $falsecorrect>";
|
||||
echo "<input $falsechecked type=\"radio\" $readonly $inputname value=\"$false->id\" alt=\"".s($false->answer)."\" />$false->answer";
|
||||
echo "</td></tr></table><br clear=\"all\" />";// changed from clear=ALL jm
|
||||
if ($quiz->feedback && isset($answers[$nameprefix])
|
||||
&& $feedback = $answers[$nameprefix]->feedback) {
|
||||
quiz_print_comment(
|
||||
"$feedback");
|
||||
|
||||
// Work out which radio button to select (if either)
|
||||
$truechecked = ('true' === $selected) ? ' checked="checked"' : '';
|
||||
$falsechecked = ('false' === $selected) ? ' checked="checked"' : '';
|
||||
|
||||
// Work out which answer is correct if we need to highlight it
|
||||
if ($options->correct_responses) {
|
||||
$truecorrect = ('true' === $correct) ? ' class="highlight"' : '';
|
||||
$falsecorrect = ('false' === $correct) ? ' class="highlight"' : '';
|
||||
} else {
|
||||
$truecorrect = '';
|
||||
$falsecorrect = '';
|
||||
}
|
||||
|
||||
// Print the controls
|
||||
$inputname = ' name="'.$question->name_prefix.'" ';
|
||||
echo '<table align="right" cellpadding="5"><tr><td align="right">';
|
||||
echo $stranswer . ': </td>';
|
||||
echo '<td' . $truecorrect . '>';
|
||||
echo '<input type="radio"' . $truechecked . $readonly . $inputname;
|
||||
echo 'value="' . $answers['true']->answer . '" alt="';
|
||||
echo s($answers['true']->answer) . '" />' . s($answers['true']->answer);
|
||||
echo '</td><td' . $falsecorrect . '>';
|
||||
echo '<input type="radio"' . $falsechecked . $readonly . $inputname;
|
||||
echo 'value="' . $answers['false']->answer . '" alt="';
|
||||
echo s($answers['false']->answer) . '" />';
|
||||
p($answers['false']->answer);
|
||||
if (!empty($marked) && (!$options->readonly || $marked !== $selected)) {
|
||||
/* This should never happen but it is here both for robustness and
|
||||
to serve as an example for question type authors */
|
||||
echo '</td></tr><tr><td><font size="1">';
|
||||
echo $strlastanswer . ': </font></td>';
|
||||
echo '</td>';
|
||||
if ('true' === $marked) {
|
||||
echo '<td><font size="1">' . $answers['true']->answer;
|
||||
echo '</font></td><td></td>';
|
||||
} else {
|
||||
echo '<td></td><td><font size="1">';
|
||||
echo $answers['false']->answer . '</font></td>';
|
||||
}
|
||||
}
|
||||
echo '</td></tr></table><br clear="all" />';
|
||||
|
||||
if ($options->feedback && !empty($marked)) {
|
||||
quiz_print_comment($answers[$marked]->feedback);
|
||||
}
|
||||
}
|
||||
|
||||
function grade_response($question, $nameprefix) {
|
||||
|
||||
$answers = get_records("quiz_answers", "question", $question->id);
|
||||
if (isset($question->response[$nameprefix])
|
||||
&& isset($answers[$question->response[$nameprefix]])) {
|
||||
$result->answers = array($nameprefix
|
||||
=> $answers[$question->response[$nameprefix]]);
|
||||
$result->grade = $result->answers[$nameprefix]->fraction;
|
||||
|
||||
} else {
|
||||
$result->answers = array();
|
||||
$result->grade = 0.0;
|
||||
}
|
||||
$result->correctanswers = quiz_extract_correctanswers($answers,
|
||||
$nameprefix);
|
||||
|
||||
return $result;
|
||||
/**
|
||||
* This questiontype does not have to print any additional grading details
|
||||
*
|
||||
* @param object $question
|
||||
* @param object $state
|
||||
* @param object $quiz
|
||||
* @param object $options
|
||||
*/
|
||||
function print_question_grading_details(&$question, &$state, $quiz, $options) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
//// END OF CLASS ////
|
||||
|
||||
+24
-46
@@ -8,7 +8,7 @@
|
||||
optional_variable($id); // Course Module ID, or
|
||||
optional_variable($q); // quiz ID
|
||||
|
||||
optional_variable($mode, "overview"); // Report mode
|
||||
optional_variable($mode, "simplestat"); // Report mode
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_record("course_modules", "id", $id)) {
|
||||
@@ -41,50 +41,29 @@
|
||||
error("You are not allowed to use this script");
|
||||
}
|
||||
|
||||
add_to_log($course->id, "quiz", "report", "report.php?id=$cm->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// Print the page header
|
||||
if (empty($noheader)) {
|
||||
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strquiz = get_string("modulename", "quiz");
|
||||
$strreport = get_string("report", "quiz");
|
||||
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>
|
||||
-> <a href=\"view.php?id=$cm->id\">".format_string($quiz->name,true)."</a> -> $strreport",
|
||||
"", "", true, update_module_button($cm->id, $course->id, $strquiz), navmenu($course, $cm));
|
||||
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
|
||||
/// Print list of available quiz reports
|
||||
|
||||
$allreports = get_list_of_plugins("mod/quiz/report");
|
||||
$reportlist = array ("overview", "regrade"); // Standard reports we want to show first
|
||||
|
||||
foreach ($allreports as $report) {
|
||||
if (!in_array($report, $reportlist)) {
|
||||
$reportlist[] = $report;
|
||||
}
|
||||
}
|
||||
|
||||
$tabs = array();
|
||||
$row = array();
|
||||
$currenttab = '';
|
||||
foreach ($reportlist as $report) {
|
||||
$row[] = new tabobject($report, "report.php?id=$cm->id&mode=$report",
|
||||
get_string("report$report", "quiz"));
|
||||
if ($report == $mode) {
|
||||
$currenttab = $report;
|
||||
}
|
||||
}
|
||||
$tabs[] = $row;
|
||||
|
||||
print_tabs($tabs, $currenttab);
|
||||
|
||||
// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quiz->questions and isteacheredit($course->id)) {
|
||||
redirect('edit.php?quizid='.$quiz->id);
|
||||
}
|
||||
|
||||
add_to_log($course->id, "quiz", "report", "report.php?id=$cm->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// Define some strings
|
||||
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strquiz = get_string("modulename", "quiz");
|
||||
|
||||
/// Print the page header
|
||||
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>
|
||||
-> ".format_string($quiz->name),
|
||||
"", "", true, update_module_button($cm->id, $course->id, $strquiz), navmenu($course, $cm));
|
||||
|
||||
/// Print the tabs
|
||||
|
||||
$currenttab = 'reports';
|
||||
include('tabs.php');
|
||||
|
||||
/// Open the selected quiz report and display it
|
||||
|
||||
@@ -103,9 +82,8 @@
|
||||
error("Error occurred during pre-processing!");
|
||||
}
|
||||
|
||||
if (empty($noheader)) {
|
||||
print_footer($course);
|
||||
}
|
||||
/// Print footer
|
||||
|
||||
print_footer($course);
|
||||
|
||||
?>
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
class quiz_report extends quiz_default_report {
|
||||
|
||||
function display($quiz, $cm, $course) { /// This function just displays the report
|
||||
|
||||
print_heading('Not yet implemented');
|
||||
return true;
|
||||
|
||||
global $CFG;
|
||||
global $download, $quests,$qtally,$table_colcount,$max_choices, $analysis,$qs_in_order,$total_user_count,$match_number, $thismin,$thismax,$myxls,$match_qs,$formatbc,$workbook,$strquestion,$showtext,$debug;
|
||||
@@ -29,7 +32,7 @@ class quiz_report extends quiz_default_report {
|
||||
$strdiscrimination = get_string('discrimination', 'quiz');
|
||||
|
||||
//Get the question ids
|
||||
//$showtext causes M/C text to whos in top table.
|
||||
//$showtext causes M/C text to whos in top table.
|
||||
//This could be made into a user toggle if we want to complicate matters
|
||||
$showtext = 1;
|
||||
$containsMCTF = 0; //used to toggle title in final listing
|
||||
@@ -86,7 +89,7 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +105,7 @@ class quiz_report extends quiz_default_report {
|
||||
$userdata[$thisresp->userid][$thisresp->attemptno]['name']=fullname($thisresp);
|
||||
$userdata[$thisresp->userid][$thisresp->attemptno]['attemptid']=$thisresp->aid;
|
||||
}
|
||||
|
||||
|
||||
if($debug and !$download){
|
||||
print("<h3>User Data</h3>");
|
||||
print_object($userdata);
|
||||
@@ -114,7 +117,7 @@ class quiz_report extends quiz_default_report {
|
||||
// //print_object($thisattempt);
|
||||
$reportline++;
|
||||
$data_tally[$reportline][$thisattempt['attemptid']][] = $thisattempt['name'];
|
||||
$data_tally[$reportline][$thisattempt['attemptid']][] =round(($thisattempt['grade']/$quiz->sumgrades)*100,0);
|
||||
$data_tally[$reportline][$thisattempt['attemptid']][] =round(($thisattempt['grade']/$quiz->sumgrades)*100,0);
|
||||
//now for each question, record response as it should be printed and whether right, wrong or skipped
|
||||
//SHORTASNSWER the answer as in $userdata; TF or MULTI need response looked by from cid from $quests
|
||||
//MATCH needs elaborate processing
|
||||
@@ -173,7 +176,7 @@ class quiz_report extends quiz_default_report {
|
||||
print("<h3>Data Tally</h3>");
|
||||
print_object($data_tally);
|
||||
}
|
||||
|
||||
|
||||
//Create a list of all attempts with their scores for item analysis
|
||||
//Also create $data2 that has attempt id as key
|
||||
foreach ($data_tally as $thistally){
|
||||
@@ -184,7 +187,7 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//now go through scores from top to bottom and from $data2 accumulate number correct for top 1/3 and bottom 1/3 of scorers
|
||||
if ($scores) {
|
||||
arsort($scores);
|
||||
@@ -223,7 +226,7 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($debug and !$download){
|
||||
print("<h3>Scores</h3>");
|
||||
print_object($scores);
|
||||
@@ -232,7 +235,7 @@ class quiz_report extends quiz_default_report {
|
||||
print("<h3>Bottom Scores</h3>");
|
||||
print_object($bott_scores);
|
||||
}
|
||||
|
||||
|
||||
//Create here an array with the response analysis data for use with both screen display & Excel
|
||||
// 2 dimensional array has as many cells across as items + title, as many down as $max_choices
|
||||
// plus one row [0] for correct items
|
||||
@@ -297,7 +300,7 @@ class quiz_report extends quiz_default_report {
|
||||
$current_column += $match_number[$qid] -1;
|
||||
break;
|
||||
default:
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -325,7 +328,7 @@ class quiz_report extends quiz_default_report {
|
||||
$format =& $workbook->add_format();
|
||||
$format->set_bold(0);
|
||||
$formaty =& $workbook->add_format();
|
||||
$formaty->set_bg_color('yellow');
|
||||
$formaty->set_bg_color('yellow');
|
||||
$formatyc =& $workbook->add_format();
|
||||
$formatyc->set_bg_color('yellow'); //bold text on yellow bg
|
||||
$formatyc->set_bold(1);
|
||||
@@ -392,7 +395,7 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
|
||||
//now print the lines of answers
|
||||
|
||||
|
||||
$row = 2;
|
||||
foreach ($data_tally as $thisuserno=>$thisuser){
|
||||
foreach($thisuser as $thisattemptno=>$thisattempt){
|
||||
@@ -401,7 +404,7 @@ class quiz_report extends quiz_default_report {
|
||||
//$thisitemkeys 1 & 2 are name and total score
|
||||
//There needs to be a 3-way branch, keys0 & 1 just print $thisitem
|
||||
//else if $thisitem['qtype'] = 5, then processing for MATCH is needed
|
||||
//else the data to be printed is in $thisitem['data'] and
|
||||
//else the data to be printed is in $thisitem['data'] and
|
||||
//$thisitem['score'] == 1 shows that the item was correct
|
||||
if ($thisitem['score'] < 1) {$thiscolor = $formatred;} else {$thiscolor = $formatblue;}
|
||||
$col++;
|
||||
@@ -571,7 +574,7 @@ class quiz_report extends quiz_default_report {
|
||||
if ($download == "txt") {
|
||||
/// Print header to force download
|
||||
|
||||
header("Content-Type: application/download\n");
|
||||
header("Content-Type: application/download\n");
|
||||
$downloadfilename = clean_filename("$course->shortname ".format_string($quiz->name,true));
|
||||
header("Content-Disposition: attachment; filename=\"$downloadfilename.txt\"");
|
||||
header("Expires: 0");
|
||||
@@ -582,7 +585,7 @@ class quiz_report extends quiz_default_report {
|
||||
|
||||
echo format_string($quiz->name,true);
|
||||
echo "\n";
|
||||
|
||||
|
||||
/// Print all the user data
|
||||
$colcount = count($question_ids);
|
||||
foreach ($data_tally as $thisuserno=>$thisuser){
|
||||
@@ -697,9 +700,9 @@ class quiz_report extends quiz_default_report {
|
||||
print("</tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
print("</table><br />\n");
|
||||
|
||||
|
||||
if($debug and !$download){
|
||||
print("<h3>Qtally</h3>");
|
||||
print_object($qtally);
|
||||
@@ -730,7 +733,7 @@ class quiz_report extends quiz_default_report {
|
||||
$nowdata = $analysis[$i][$j];
|
||||
if (strpos($nowdata,"//")>0) {$nowdata = str_replace("//","<br />",$nowdata);}
|
||||
print("<td align=\"center\"> $nowdata</td>");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
print("</tr>\n");
|
||||
@@ -797,7 +800,7 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
print("</table>\n");
|
||||
|
||||
|
||||
echo "<br />\n";
|
||||
echo "<table border=\"0\" align=\"center\"><tr>\n";
|
||||
echo "<td>";
|
||||
@@ -821,7 +824,7 @@ function qr_quiz_responses($quiz) {
|
||||
// Given any quiz number, get all responses and place in
|
||||
// $response object
|
||||
global $CFG;
|
||||
|
||||
|
||||
$resp_recs =get_records_sql("SELECT r.id as rid, r.attempt, r.answer, r.question, a.attempt as attemptno, a.id as aid, a.quiz, a.userid, a.sumgrades, u.id as uid, u.lastname, u.firstname FROM {$CFG->prefix}quiz_responses r, {$CFG->prefix}quiz_attempts a, {$CFG->prefix}user u WHERE a.id = r.attempt AND a.quiz = '$quiz' AND a.userid = u.id ORDER BY u.lastname ASC, u.firstname ASC, r.id ASC");
|
||||
return $resp_recs;
|
||||
}
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
<?php // $Id$
|
||||
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
/// Overview report just displays a big table of all the attempts
|
||||
|
||||
class quiz_report extends quiz_default_report {
|
||||
|
||||
function display($quiz, $cm, $course) { /// This function just displays the report
|
||||
|
||||
global $CFG, $SESSION, $db;
|
||||
|
||||
$strreallydel = addslashes(get_string('deleteattemptcheck','quiz'));
|
||||
$strnoattempts = get_string('noattempts','quiz');
|
||||
$strtimeformat = get_string('strftimedatetime');
|
||||
|
||||
$action = optional_param('action', '');
|
||||
|
||||
switch($action) {
|
||||
case 'delete': /// Some attempts need to be deleted
|
||||
|
||||
$attemptids = isset($_POST['attemptid']) ? $_POST['attemptid'] : array();
|
||||
if(!is_array($attemptids) || empty($attemptids)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach($attemptids as $num => $attemptid) {
|
||||
if(empty($attemptid)) {
|
||||
unset($attemptids[$num]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($attemptids as $attemptid) {
|
||||
if ($todelete = get_record('quiz_attempts', 'id', $attemptid)) {
|
||||
|
||||
delete_records('quiz_attempts', 'id', $attemptid);
|
||||
delete_records('quiz_responses', 'attempt', $attemptid);
|
||||
|
||||
// Search quiz_attempts for other instances by this user.
|
||||
// If none, then delete record for this quiz, this user from quiz_grades
|
||||
// else recalculate best grade
|
||||
|
||||
$userid = $todelete->userid;
|
||||
if (!record_exists('quiz_attempts', 'userid', $userid, 'quiz', $quiz->id)) {
|
||||
delete_records('quiz_grades', 'userid', $userid,'quiz', $quiz->id);
|
||||
} else {
|
||||
quiz_save_best_grade($quiz, $userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isset($SESSION->quiz_overview_table)) {
|
||||
$SESSION->quiz_overview_table = array('noattempts' => false, 'teacherattempts' => true, 'detailedmarks' => false);
|
||||
}
|
||||
|
||||
foreach($SESSION->quiz_overview_table as $option => $value) {
|
||||
$urlparam = optional_param($option, NULL);
|
||||
if($urlparam === NULL) {
|
||||
$$option = $value;
|
||||
}
|
||||
else {
|
||||
$$option = $SESSION->quiz_overview_table[$option] = $urlparam;
|
||||
}
|
||||
}
|
||||
|
||||
$tablecolumns = array('checkbox', 'picture', 'fullname', 'timefinish', 'duration', 'sumgrades');
|
||||
$tableheaders = array(NULL, '', get_string('fullname'), get_string('attemptedon', 'quiz'), get_string('attemptduration', 'quiz'), get_string('grade', 'quiz').'/'.$quiz->grade);
|
||||
|
||||
if($detailedmarks) {
|
||||
$questions = explode(',', $quiz->questions);
|
||||
foreach($questions as $number => $questionid) {
|
||||
$tablecolumns[] = '$'.$questionid;
|
||||
$tableheaders[] = '#'.($number + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$table = new flexible_table('mod-quiz-report-overview-report');
|
||||
|
||||
$table->define_columns($tablecolumns);
|
||||
$table->define_headers($tableheaders);
|
||||
$table->define_baseurl($CFG->wwwroot.'/mod/quiz/report.php?id='.$cm->id);
|
||||
|
||||
$table->sortable(true);
|
||||
$table->collapsible(true);
|
||||
$table->initialbars(true);
|
||||
|
||||
$table->column_suppress('picture');
|
||||
$table->column_suppress('fullname');
|
||||
|
||||
$table->column_class('picture', 'picture');
|
||||
|
||||
$table->set_attribute('cellspacing', '0');
|
||||
$table->set_attribute('id', 'attempts');
|
||||
$table->set_attribute('class', 'generaltable generalbox');
|
||||
|
||||
// Start working -- this is necessary as soon as the niceties are over
|
||||
$table->setup();
|
||||
|
||||
|
||||
/// Check to see if groups are being used in this quiz
|
||||
if ($groupmode = groupmode($course, $cm)) { // Groups are being used
|
||||
$currentgroup = setup_and_print_groups($course, $groupmode, "report.php?id=$cm->id&mode=overview");
|
||||
} else {
|
||||
$currentgroup = false;
|
||||
}
|
||||
|
||||
/// Get all teachers and students
|
||||
if ($currentgroup) {
|
||||
$users = get_group_users($currentgroup);
|
||||
}
|
||||
else {
|
||||
$users = get_course_users($course->id);
|
||||
}
|
||||
|
||||
if(!$teacherattempts) {
|
||||
$teachers = get_course_teachers($course->id);
|
||||
if(!empty($teachers)) {
|
||||
$keys = array_keys($teachers);
|
||||
}
|
||||
foreach($keys as $key) {
|
||||
unset($users[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($users)) {
|
||||
print_heading($strnoattempts);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Construct the SQL
|
||||
|
||||
if($where = $table->get_sql_where()) {
|
||||
$where .= ' AND ';
|
||||
}
|
||||
|
||||
if($sort = $table->get_sql_sort()) {
|
||||
$sortparts = explode(',', $sort);
|
||||
$newsort = array();
|
||||
$firsttime = true;
|
||||
foreach($sortparts as $sortpart) {
|
||||
$sortpart = trim($sortpart);
|
||||
if(substr($sortpart, 0, 1) == '$') {
|
||||
if($firsttime) {
|
||||
$qnum = intval(substr($sortpart, 1));
|
||||
$where .= '('.sql_isnull('qr.question').' OR qr.question = '.$qnum.') AND ';
|
||||
$newsort[] = 'grade '.(strpos($sortpart, 'ASC')? 'ASC' : 'DESC');
|
||||
$firsttime = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$newsort[] = $sortpart;
|
||||
}
|
||||
}
|
||||
$sort = ' ORDER BY '.implode(', ', $newsort);
|
||||
}
|
||||
|
||||
$select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uvsa, u.id AS userid, u.firstname, u.lastname, u.picture, qa.id AS attempt, qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration ';
|
||||
$group = 'GROUP BY uvsa';
|
||||
$sql = 'FROM '.$CFG->prefix.'user u '.
|
||||
'LEFT JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '.
|
||||
'LEFT JOIN '.$CFG->prefix.'quiz_responses qr ON qr.attempt = qa.id '.
|
||||
'WHERE '.$where.'u.id IN ('.implode(',', array_keys($users)).') AND ('.($noattempts ? sql_isnull('qa.quiz').' OR ' : '') . 'qa.quiz = '.$quiz->id.') ';
|
||||
|
||||
|
||||
$total = count_records_sql('SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$sql);
|
||||
$table->pagesize(10, $total);
|
||||
|
||||
if($table->get_page_start() !== '' && $table->get_page_size() !== '') {
|
||||
$limit = ' '.sql_paging_limit($table->get_page_start(), $table->get_page_size());
|
||||
}
|
||||
else {
|
||||
$limit = '';
|
||||
}
|
||||
|
||||
$attempts = get_records_sql($select.$sql.$group.$sort.$limit);
|
||||
|
||||
if(!empty($attempts)) {
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
|
||||
$picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true);
|
||||
|
||||
$row = array(
|
||||
'<input type="checkbox" name="attemptid[]" value="'.$attempt->attempt.'" />',
|
||||
$picture,
|
||||
'<a href="'.$CFG->wwwroot.'/user/view.php?id='.$attempt->userid.'&course='.$course->id.'">'.fullname($attempt).'</a>',
|
||||
empty($attempt->attempt) ? '-' : '<a href="review.php?q='.$quiz->id.'&attempt='.$attempt->attempt.'">'.userdate($attempt->timestart, $strtimeformat).'</a>',
|
||||
empty($attempt->attempt) || empty($attempt->timefinish) ? '-' : format_time($attempt->duration),
|
||||
$attempt->sumgrades === NULL ? '-' : format_float($attempt->sumgrades / $quiz->sumgrades * $quiz->grade,$quiz->decimalpoints)
|
||||
);
|
||||
|
||||
if($detailedmarks) {
|
||||
if(empty($attempt->attempt)) {
|
||||
foreach($questions as $question) {
|
||||
$row[] = '-';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$responses = get_records('quiz_responses', 'attempt', $attempt->attempt, 'question');
|
||||
foreach($responses as $response) {
|
||||
$row[] = $response->grade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$table->add_data($row);
|
||||
}
|
||||
|
||||
echo '<div id="tablecontainer">';
|
||||
echo '<form id="attemptsform" method="post" action="report.php" onsubmit="var menu = document.getElementById(\'actionmenu\'); return (menu.options[menu.selectedIndex].value == \'delete\' ? \''.$strreallydel.'\' : true);">';
|
||||
echo '<input type="hidden" name="id" value="'.$cm->id.'" />';
|
||||
|
||||
}
|
||||
|
||||
$table->print_html();
|
||||
|
||||
if(!empty($attempts)) {
|
||||
echo '<table id="commands">';
|
||||
echo '<tr><td>';
|
||||
echo '<a href="javascript:select_all_in(\'DIV\', null, \'tablecontainer\');">'.get_string('selectall', 'quiz').'</a> / ';
|
||||
echo '<a href="javascript:deselect_all_in(\'DIV\', null, \'tablecontainer\');">'.get_string('selectnone', 'quiz').'</a> ';
|
||||
echo '</td><td style="text-align: right;">';
|
||||
$options = array('dummy' => ' ', 'delete' => get_string('delete'));
|
||||
$menu = str_replace('<select', '<select id="actionmenu"', choose_from_menu($options, 'action', '', get_string('selectedattempts', 'quiz'), 'if(this.selectedIndex > 1) submitFormById(\'attemptsform\');', '', true));
|
||||
echo $menu;
|
||||
echo '<noscript id="noscriptactionmenu" style="display: inline;">';
|
||||
echo '<input type="submit" value="'.get_string('go').'" /></noscript>';
|
||||
echo '<script type="text/javascript">'."\n<!--\n".'document.getElementById("noscriptactionmenu").style.display = "none";'."\n-->\n".'</script>';
|
||||
echo '</td></tr></table>';
|
||||
echo '</form></div>';
|
||||
}
|
||||
else {
|
||||
print_heading(get_string('noattemptsmatchingfilter', 'quiz', strtolower($course->students)));
|
||||
}
|
||||
|
||||
echo '<div class="controls">';
|
||||
echo '<form method="report.php">';
|
||||
echo '<p>'.get_string('displayoptions', 'quiz').': ';
|
||||
echo '<input type="hidden" name="id" value="'.$cm->id.'" />';
|
||||
echo '<input type="hidden" name="noattempts" value="0" />';
|
||||
echo '<input type="hidden" name="teacherattempts" value="0" />';
|
||||
echo '<input type="hidden" name="detailedmarks" value="0" />';
|
||||
echo '<input type="checkbox" id="checknoattempts" name="noattempts" '.($noattempts?'checked="checked" ':'').'value="1" /> <label for="checknoattempts">'.get_string('shownoattempts', 'quiz').'</label> ';
|
||||
echo '<input type="checkbox" id="checkteacherattempts" name="teacherattempts" '.($teacherattempts?'checked="checked" ':'').'value="1" /> <label for="checkteacherattempts">'.get_string('showteacherattempts', 'quiz').'</label> ';
|
||||
echo '<input type="checkbox" id="checkdetailedmarks" name="detailedmarks" '.($detailedmarks?'checked="checked" ':'').'value="1" /> <label for="checkdetailedmarks">'.get_string('showdetailedmarks', 'quiz').'</label> ';
|
||||
echo '<input type="submit" value="'.get_string('go').'" />';
|
||||
echo '</p>';
|
||||
echo '</form>';
|
||||
echo '</div>';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function quiz_get_user_attempts_list($quiz, $attempts, $bestgrade, $timeformat) {
|
||||
/// Returns a little list of all attempts, one per line,
|
||||
/// with each grade linked to the feedback report and with the best grade highlighted
|
||||
/// Each also has information about date and lapsed time
|
||||
|
||||
$bestgrade = format_float($bestgrade,$quiz->decimalpoints);
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
$attemptgrade = format_float(($attempt->sumgrades / $quiz->sumgrades) * $quiz->grade,$quiz->decimalpoints);
|
||||
$attemptdate = userdate($attempt->timestart, $timeformat);
|
||||
if ($attempt->timefinish) {
|
||||
$attemptlapse = format_time($attempt->timefinish - $attempt->timestart);
|
||||
} else {
|
||||
$attemptlapse = "...";
|
||||
}
|
||||
$button = "<input type=\"checkbox\" name=\"box$attempt->id\" value=\"$attempt->id\" alt=\"box$attempt->id\" />";
|
||||
$revurl = "review.php?q=$quiz->id&attempt=$attempt->id";
|
||||
if ($attemptgrade == $bestgrade) {
|
||||
$userattempts[] = "$button <span class=\"highlight\">$attemptgrade</span> <a href=\"$revurl\">$attemptdate</a> ($attemptlapse)";
|
||||
} else {
|
||||
$userattempts[] = "$button $attemptgrade <a href=\"$revurl\">$attemptdate</a> ($attemptlapse)";
|
||||
}
|
||||
}
|
||||
return implode("<br />\n", $userattempts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php // $Id$
|
||||
|
||||
/// Overview report just displays a big table of all the attempts
|
||||
|
||||
class quiz_report extends quiz_default_report {
|
||||
|
||||
function display($quiz, $cm, $course) { /// This function just displays the report
|
||||
|
||||
global $CFG;
|
||||
|
||||
if (!$attempts = get_records("quiz_attempts", "quiz", $quiz->id)) {
|
||||
print_header(get_string("noattempts", "quiz"));
|
||||
print_continue("report.php?id=$cm->id");
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
$users = array();
|
||||
$count->attempt = 0;
|
||||
$count->changed = 0;
|
||||
|
||||
foreach ($attempts as $attempt) {
|
||||
|
||||
set_time_limit(120);
|
||||
|
||||
if (!$attempt->timefinish) { // Skip incomplete attempts
|
||||
continue;
|
||||
}
|
||||
|
||||
if($quiz->timelimit > 0) {
|
||||
$timelimit = ($quiz->timelimit * 60) + 60;
|
||||
$timetaken = $attempt->timefinish - $attempt->timestart;
|
||||
if($timetaken > $timelimit) {
|
||||
// Skip overdued attempts
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$count->attempt++;
|
||||
|
||||
if (! $questions = quiz_get_attempt_questions($quiz, $attempt)) {
|
||||
error("Could not reconstruct quiz results for attempt $attempt->id!");
|
||||
}
|
||||
|
||||
if (!$result = quiz_grade_responses($quiz, $questions)) {
|
||||
error("Could not re-grade this quiz attempt!");
|
||||
}
|
||||
|
||||
if ($attempt->sumgrades != $result->sumgrades) {
|
||||
$attempt->sumgrades = $result->sumgrades;
|
||||
$count->changed++;
|
||||
|
||||
if (! update_record("quiz_attempts", $attempt)) {
|
||||
notify("Could not regrade attempt $attempt->id");
|
||||
}
|
||||
}
|
||||
|
||||
$users[$attempt->userid] = $attempt->userid;
|
||||
}
|
||||
|
||||
if ($users) {
|
||||
foreach ($users as $userid) {
|
||||
if (! quiz_save_best_grade($quiz, $userid)) {
|
||||
notify("Could not save best grade for user $userid!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_heading(get_string("regradecomplete", "quiz"));
|
||||
print_heading(get_string("regradecount", "quiz", $count));
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -20,11 +20,11 @@ function quiz_report_simplestat_get_attempt_responses($attempt) {
|
||||
global $CFG;
|
||||
|
||||
if (!$responses = get_records_sql("SELECT q.id, q.qtype, q.category, q.questiontext,
|
||||
q.defaultgrade, q.image, r.answer
|
||||
FROM {$CFG->prefix}quiz_responses r,
|
||||
q.defaultgrade, q.image, s.answer
|
||||
FROM {$CFG->prefix}quiz_states s,
|
||||
{$CFG->prefix}quiz_questions q
|
||||
WHERE r.attempt = '$attempt->id'
|
||||
AND q.id = r.question")) {
|
||||
AND q.id = s.question")) {
|
||||
notify("Could not find any responses for that attempt!");
|
||||
return false;
|
||||
}
|
||||
@@ -402,7 +402,7 @@ function quiz_report_simplestat_grade_attempt_results($quiz, $questions) {
|
||||
error("No questions!");
|
||||
}
|
||||
|
||||
if (!$grades = get_records_menu("quiz_question_grades", "quiz", $quiz->id, "", "question,grade")) {
|
||||
if (!$grades = get_records_menu('quiz_question_instances', 'quiz', $quiz->id, '', 'question,grade')) {
|
||||
error("No grades defined for these quiz questions!");
|
||||
}
|
||||
|
||||
@@ -448,6 +448,9 @@ class quiz_report extends quiz_default_report {
|
||||
global $CFG;
|
||||
global $download;
|
||||
|
||||
print_heading('Not yet implemented');
|
||||
return true;
|
||||
|
||||
optional_variable($download, "");
|
||||
|
||||
/// Check to see if groups are being used in this quiz
|
||||
@@ -550,12 +553,12 @@ class quiz_report extends quiz_default_report {
|
||||
/// Print names of all the fields
|
||||
$myxls->write_string(0,0,format_string($quiz->name,true));
|
||||
$myxls->set_column(0,0,25);
|
||||
|
||||
|
||||
$myxls->set_column(1,$count,9);
|
||||
for ($i=1; $i<=$count; $i++) {
|
||||
$myxls->write_string(0,$i,$i);
|
||||
}
|
||||
|
||||
|
||||
/// Print all the user data
|
||||
|
||||
$row=1;
|
||||
@@ -587,16 +590,16 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
|
||||
$workbook->close();
|
||||
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// If a text file is wanted, produce one
|
||||
if ($download == "txt") {
|
||||
/// Print header to force download
|
||||
|
||||
header("Content-Type: application/download\n");
|
||||
|
||||
header("Content-Type: application/download\n");
|
||||
$downloadfilename = clean_filename("$course->shortname ".format_string($quiz->name,true));
|
||||
header("Content-Disposition: attachment; filename=$downloadfilename.txt");
|
||||
header("Expires: 0");
|
||||
@@ -605,13 +608,13 @@ class quiz_report extends quiz_default_report {
|
||||
|
||||
|
||||
/// Print names of all the fields
|
||||
|
||||
|
||||
echo format_string($quiz->name,true);
|
||||
for ($i=1; $i<=$count; $i++) {
|
||||
echo "\t$i";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
|
||||
/// Print all the user data
|
||||
|
||||
foreach ($data as $userid => $datum) {
|
||||
@@ -639,7 +642,7 @@ class quiz_report extends quiz_default_report {
|
||||
echo "\t$percent";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -697,7 +700,7 @@ class quiz_report extends quiz_default_report {
|
||||
$options["download"] = "txt";
|
||||
print_single_button("report.php", $options, get_string("downloadtext"));
|
||||
echo "</table>";
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+141
-46
@@ -28,6 +28,24 @@
|
||||
// | (CL,pk->id,fk->definition)
|
||||
// |
|
||||
// |
|
||||
// |.......................................
|
||||
// | .
|
||||
// quiz | .
|
||||
// (CL,pk->id) | ----quiz_question_datasets---- .
|
||||
// | | | (CL,pk->id,fk->question, | .
|
||||
// |-------------------| | | fk->dataset_definition) | .
|
||||
// quiz_grades | | | | .
|
||||
// (UL,pk->id,fk->quiz) |---------------------| | | | .
|
||||
// | | | | | .
|
||||
// quiz_attempts | quiz_questions quiz_dataset_definitions
|
||||
// (UL,pk->id,fk->quiz) | (CL,pk->id,fk->category,files) (CL,pk->id,fk->category)
|
||||
// | |--------------------------------| |
|
||||
// | quiz_question_instances | |
|
||||
// | (CL,pk->id,fk->quiz,fk->question) | |
|
||||
// | | | quiz_dataset_items
|
||||
// |---------------------| | (CL,pk->id,fk->definition)
|
||||
// quiz_states |
|
||||
// (UL,pk->id, fk->attempt, fk->question_instance) |
|
||||
// |
|
||||
// --------------------------------------------------------------------------------------------------------------
|
||||
// | | | | | | |
|
||||
@@ -88,7 +106,7 @@
|
||||
// - quiz_question_grades
|
||||
// - quiz_attempts
|
||||
// - quiz_grades
|
||||
// - quiz_responses
|
||||
// - quiz_states
|
||||
// This step is the standard mod backup. (course dependent).
|
||||
|
||||
//STEP 1. Restore categories/questions and associated structures
|
||||
@@ -327,7 +345,7 @@
|
||||
$question->hidden = backup_todb($que_info['#']['HIDDEN']['0']['#']);
|
||||
|
||||
// If it is a random question then hide it
|
||||
if ($question->qtype == 4) {
|
||||
if ($question->qtype = RANDOM) {
|
||||
$question->hidden = 1;
|
||||
}
|
||||
|
||||
@@ -1394,39 +1412,39 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//This function restores the quiz_question_grades
|
||||
function quiz_question_grades_restore_mods($quiz_id,$info,$restore) {
|
||||
//This function restores the quiz_question_instances
|
||||
function quiz_question_instances_restore_mods($quiz_id,$info,$restore) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
//Get the quiz_question_grades array
|
||||
$grades = $info['MOD']['#']['QUESTION_GRADES']['0']['#']['QUESTION_GRADE'];
|
||||
//Get the quiz_question_instances array
|
||||
$instances = $info['MOD']['#']['QUESTION_INSTANCES']['0']['#']['QUESTION_INSTANCE'];
|
||||
|
||||
//Iterate over question_grades
|
||||
for($i = 0; $i < sizeof($grades); $i++) {
|
||||
$gra_info = $grades[$i];
|
||||
//traverse_xmlize($gra_info); //Debug
|
||||
//Iterate over question_instances
|
||||
for($i = 0; $i < sizeof($instances); $i++) {
|
||||
$ins_info = $instances[$i];
|
||||
//traverse_xmlize($ins_info); //Debug
|
||||
//print_object ($GLOBALS['traverse_array']); //Debug
|
||||
//$GLOBALS['traverse_array']=""; //Debug
|
||||
|
||||
//We'll need this later!!
|
||||
$oldid = backup_todb($gra_info['#']['ID']['0']['#']);
|
||||
$oldid = backup_todb($ins_info['#']['ID']['0']['#']);
|
||||
|
||||
//Now, build the QUESTION_GRADES record structure
|
||||
$grade->quiz = $quiz_id;
|
||||
$grade->question = backup_todb($gra_info['#']['QUESTION']['0']['#']);
|
||||
$grade->grade = backup_todb($gra_info['#']['GRADE']['0']['#']);
|
||||
//Now, build the QUESTION_INSTANCES record structure
|
||||
$instance->quiz = $quiz_id;
|
||||
$instance->question = backup_todb($ins_info['#']['QUESTION']['0']['#']);
|
||||
$instance->grade = backup_todb($ins_info['#']['GRADE']['0']['#']);
|
||||
|
||||
//We have to recode the question field
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$grade->question);
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$instance->question);
|
||||
if ($question) {
|
||||
$grade->question = $question->new_id;
|
||||
$instance->question = $question->new_id;
|
||||
}
|
||||
|
||||
//The structure is equal to the db, so insert the quiz_question_grades
|
||||
$newid = insert_record ("quiz_question_grades",$grade);
|
||||
//The structure is equal to the db, so insert the quiz_question_instances
|
||||
$newid = insert_record ("quiz_question_instances",$instance);
|
||||
|
||||
//Do some output
|
||||
if (($i+1) % 10 == 0) {
|
||||
@@ -1439,7 +1457,78 @@
|
||||
|
||||
if ($newid) {
|
||||
//We have the newid, update backup_ids
|
||||
backup_putid($restore->backup_unique_code,"quiz_question_grades",$oldid,
|
||||
backup_putid($restore->backup_unique_code,"quiz_question_instances",$oldid,
|
||||
$newid);
|
||||
} else {
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
//This function restores the quiz_question_versions
|
||||
function quiz_question_versions_restore_mods($quiz_id,$info,$restore) {
|
||||
|
||||
global $CFG, $USER;
|
||||
|
||||
$status = true;
|
||||
|
||||
//Get the quiz_question_versions array
|
||||
$versions = $info['MOD']['#']['QUESTION_VERSIONS']['0']['#']['QUESTION_VERSION'];
|
||||
|
||||
//Iterate over question_versions
|
||||
for($i = 0; $i < sizeof($versions); $i++) {
|
||||
$ver_info = $versions[$i];
|
||||
//traverse_xmlize($ver_info); //Debug
|
||||
//print_object ($GLOBALS['traverse_array']); //Debug
|
||||
//$GLOBALS['traverse_array']=""; //Debug
|
||||
|
||||
//We'll need this later!!
|
||||
$oldid = backup_todb($ver_info['#']['ID']['0']['#']);
|
||||
|
||||
//Now, build the QUESTION_VERSIONS record structure
|
||||
$version->quiz = $quiz_id;
|
||||
$version->oldquestion = backup_todb($ver_info['#']['OLDQUESTION']['0']['#']);
|
||||
$version->newquestion = backup_todb($ver_info['#']['NEWQUESTION']['0']['#']);
|
||||
$version->userid = backup_todb($ver_info['#']['USERID']['0']['#']);
|
||||
$version->timestamp = backup_todb($ver_info['#']['TIMESTAMP']['0']['#']);
|
||||
|
||||
//We have to recode the oldquestion field
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$version->oldquestion);
|
||||
if ($question) {
|
||||
$version->oldquestion = $question->new_id;
|
||||
}
|
||||
|
||||
//We have to recode the newquestion field
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$version->newquestion);
|
||||
if ($question) {
|
||||
$version->newquestion = $question->new_id;
|
||||
}
|
||||
|
||||
//We have to recode the userid field
|
||||
$user = backup_getid($restore->backup_unique_code,"user",$version->userid);
|
||||
if ($user) {
|
||||
$version->userid = $user->new_id;
|
||||
} else { //Assign to current user
|
||||
$version->userid = $USER->id;
|
||||
}
|
||||
|
||||
//The structure is equal to the db, so insert the quiz_question_versions
|
||||
$newid = insert_record ("quiz_question_versions",$version);
|
||||
|
||||
//Do some output
|
||||
if (($i+1) % 10 == 0) {
|
||||
echo ".";
|
||||
if (($i+1) % 200 == 0) {
|
||||
echo "<br />";
|
||||
}
|
||||
backup_flush(300);
|
||||
}
|
||||
|
||||
if ($newid) {
|
||||
//We have the newid, update backup_ids
|
||||
backup_putid($restore->backup_unique_code,"quiz_question_versions",$oldid,
|
||||
$newid);
|
||||
} else {
|
||||
$status = false;
|
||||
@@ -1572,8 +1661,8 @@
|
||||
//We have the newid, update backup_ids
|
||||
backup_putid($restore->backup_unique_code,"quiz_attempts",$oldid,
|
||||
$newid);
|
||||
//Now process quiz_responses
|
||||
$status = quiz_responses_restore_mods($newid,$att_info,$restore);
|
||||
//Now process quiz_states
|
||||
$status = quiz_states_restore_mods($newid,$att_info,$restore);
|
||||
} else {
|
||||
$status = false;
|
||||
}
|
||||
@@ -1582,18 +1671,18 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//This function restores the quiz_responses
|
||||
function quiz_responses_restore_mods($attempt_id,$info,$restore) {
|
||||
//This function restores the quiz_states
|
||||
function quiz_states_restore_mods($attempt_id,$info,$restore) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
//Get the quiz_responses array
|
||||
$responses = $info['#']['RESPONSES']['0']['#']['RESPONSE'];
|
||||
//Iterate over responses
|
||||
for($i = 0; $i < sizeof($responses); $i++) {
|
||||
$res_info = $responses[$i];
|
||||
//Get the quiz_states array
|
||||
$states = $info['#']['STATES']['0']['#']['STATE'];
|
||||
//Iterate over states
|
||||
for($i = 0; $i < sizeof($states); $i++) {
|
||||
$res_info = $states[$i];
|
||||
//traverse_xmlize($res_info); //Debug
|
||||
//print_object ($GLOBALS['traverse_array']); //Debug
|
||||
//$GLOBALS['traverse_array']=""; //Debug
|
||||
@@ -1609,9 +1698,15 @@
|
||||
$response->grade = backup_todb($res_info['#']['GRADE']['0']['#']);
|
||||
|
||||
//We have to recode the question field
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$response->question);
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$state->question);
|
||||
if ($question) {
|
||||
$response->question = $question->new_id;
|
||||
$state->question = $question->new_id;
|
||||
}
|
||||
|
||||
//We have to recode the originalquestion field
|
||||
$question = backup_getid($restore->backup_unique_code,"quiz_questions",$response->originalquestion);
|
||||
if ($question) {
|
||||
$response->originalquestion = $question->new_id;
|
||||
}
|
||||
|
||||
//We have to recode the originalquestion field
|
||||
@@ -1623,7 +1718,7 @@
|
||||
//We have to recode the answer field
|
||||
//It depends of the question type !!
|
||||
//We get the question first
|
||||
$question = get_record("quiz_questions","id",$response->question);
|
||||
$question = get_record("quiz_questions","id",$state->question);
|
||||
//It exists
|
||||
if ($question) {
|
||||
//Depending of the qtype, we make different recodes
|
||||
@@ -1633,16 +1728,16 @@
|
||||
break;
|
||||
case 2: //TRUEFALSE QTYPE
|
||||
//The answer is one answer id. We must recode it
|
||||
$answer = backup_getid($restore->backup_unique_code,"quiz_answers",$response->answer);
|
||||
$answer = backup_getid($restore->backup_unique_code,"quiz_answers",$state->answer);
|
||||
if ($answer) {
|
||||
$response->answer = $answer->new_id;
|
||||
$state->answer = $answer->new_id;
|
||||
}
|
||||
break;
|
||||
case 3: //MULTICHOICE QTYPE
|
||||
//The answer is a comma separated list of answers. We must recode them
|
||||
$answer_field = "";
|
||||
$in_first = true;
|
||||
$tok = strtok($response->answer,",");
|
||||
$tok = strtok($state->answer,",");
|
||||
while ($tok) {
|
||||
//Get the answer from backup_ids
|
||||
$answer = backup_getid($restore->backup_unique_code,"quiz_answers",$tok);
|
||||
@@ -1657,20 +1752,20 @@
|
||||
//check for next
|
||||
$tok = strtok(",");
|
||||
}
|
||||
$response->answer = $answer_field;
|
||||
$state->answer = $answer_field;
|
||||
break;
|
||||
case 4: //RANDOM QTYPE
|
||||
//The answer links to another question id, we must recode it
|
||||
$answer_link = backup_getid($restore->backup_unique_code,"quiz_questions",$response->answer);
|
||||
$answer_link = backup_getid($restore->backup_unique_code,"quiz_questions",$state->answer);
|
||||
if ($answer_link) {
|
||||
$response->answer = $answer_link->new_id;
|
||||
$state->answer = $answer_link->new_id;
|
||||
}
|
||||
break;
|
||||
case 5: //MATCH QTYPE
|
||||
//The answer is a comma separated list of hypen separated math_subs (for question and answer)
|
||||
$answer_field = "";
|
||||
$in_first = true;
|
||||
$tok = strtok($response->answer,",");
|
||||
$tok = strtok($state->answer,",");
|
||||
while ($tok) {
|
||||
//Extract the match_sub for the question and the answer
|
||||
$exploded = explode("-",$tok);
|
||||
@@ -1695,13 +1790,13 @@
|
||||
//check for next
|
||||
$tok = strtok(",");
|
||||
}
|
||||
$response->answer = $answer_field;
|
||||
$state->answer = $answer_field;
|
||||
break;
|
||||
case 6: //RANDOMSAMATCH QTYPE
|
||||
//The answer is a comma separated list of hypen separated question_id and answer_id. We must recode them
|
||||
$answer_field = "";
|
||||
$in_first = true;
|
||||
$tok = strtok($response->answer,",");
|
||||
$tok = strtok($state->answer,",");
|
||||
while ($tok) {
|
||||
//Extract the question_id and the answer_id
|
||||
$exploded = explode("-",$tok);
|
||||
@@ -1726,7 +1821,7 @@
|
||||
//check for next
|
||||
$tok = strtok(",");
|
||||
}
|
||||
$response->answer = $answer_field;
|
||||
$state->answer = $answer_field;
|
||||
break;
|
||||
case 7: //DESCRIPTION QTYPE
|
||||
//Nothing to do (there is no awser to this qtype)
|
||||
@@ -1739,7 +1834,7 @@
|
||||
//The answer is a comma separated list of hypen separated multianswer_id and answers. We must recode them.
|
||||
$answer_field = "";
|
||||
$in_first = true;
|
||||
$tok = strtok($response->answer,",");
|
||||
$tok = strtok($state->answer,",");
|
||||
while ($tok) {
|
||||
//Extract the multianswer_id and the answer
|
||||
$exploded = explode("-",$tok);
|
||||
@@ -1775,7 +1870,7 @@
|
||||
//check for next
|
||||
$tok = strtok(",");
|
||||
}
|
||||
$response->answer = $answer_field;
|
||||
$state->answer = $answer_field;
|
||||
break;
|
||||
case 10: //CALCULATED QTYPE
|
||||
//Nothing to do. The response is a text.
|
||||
@@ -1790,7 +1885,7 @@
|
||||
}
|
||||
|
||||
//The structure is equal to the db, so insert the quiz_attempts
|
||||
$newid = insert_record ("quiz_responses",$response);
|
||||
$newid = insert_record ("quiz_states",$state);
|
||||
|
||||
//Do some output
|
||||
if (($i+1) % 10 == 0) {
|
||||
@@ -1803,7 +1898,7 @@
|
||||
|
||||
if ($newid) {
|
||||
//We have the newid, update backup_ids
|
||||
backup_putid($restore->backup_unique_code,"quiz_responses",$oldid,
|
||||
backup_putid($restore->backup_unique_code,"quiz_states",$oldid,
|
||||
$newid);
|
||||
} else {
|
||||
$status = false;
|
||||
|
||||
+161
-85
@@ -1,56 +1,64 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This page prints a review of a particular quiz attempt
|
||||
/**
|
||||
* This page prints a review of a particular quiz attempt
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been completely
|
||||
* rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
|
||||
* the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
|
||||
optional_variable($id); // Course Module ID, or
|
||||
optional_variable($q); // quiz ID
|
||||
|
||||
require_variable($attempt); // A particular attempt ID for review
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_record("course_modules", "id", $id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
|
||||
if (! $course = get_record("course", "id", $cm->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
|
||||
if (! $quiz = get_record("quiz", "id", $cm->instance)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (! $quiz = get_record("quiz", "id", $q)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
if (! $course = get_record("course", "id", $quiz->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
}
|
||||
$attempt = required_param('attempt', PARAM_INT); // A particular attempt ID for review
|
||||
$page = optional_param('page', 0, PARAM_INT); // The required page
|
||||
$showall = optional_param('showall', 0, PARAM_BOOL);
|
||||
|
||||
if (! $attempt = get_record("quiz_attempts", "id", $attempt)) {
|
||||
error("No such attempt ID exists");
|
||||
}
|
||||
if (! $quiz = get_record("quiz", "id", $attempt->quiz)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
if (! $course = get_record("course", "id", $quiz->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
|
||||
if (!count_records('quiz_newest_states', 'attemptid', $attempt->id)) {
|
||||
// this question has not yet been upgraded to the new model
|
||||
quiz_upgrade_states($attempt);
|
||||
}
|
||||
|
||||
require_login($course->id, false, $cm);
|
||||
$isteacher = isteacher($course->id);
|
||||
$popup = false; // controls whether the protected window is used
|
||||
|
||||
if (!isteacher($course->id)) {
|
||||
if (!$quiz->review) {
|
||||
if (!$isteacher) {
|
||||
if (!$attempt->timefinish) {
|
||||
redirect('attempt.php?q='.$quiz->id);
|
||||
}
|
||||
// If not even responses are to be shown in review then we
|
||||
// don't allow any review
|
||||
if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
|
||||
error(get_string("noreview", "quiz"));
|
||||
}
|
||||
if (time() < $quiz->timeclose and $quiz->review == QUIZ_REVIEW_AFTER) {
|
||||
error(get_string("noreviewuntil", "quiz", userdate($quiz->timeclose)));
|
||||
}
|
||||
if (time() > $quiz->timeclose and $quiz->review == QUIZ_REVIEW_BEFORE) {
|
||||
error(get_string("noreview", "quiz"));
|
||||
if ((time() - $attempt->timefinish) > 120) { // always allow review right after attempt
|
||||
if (time() < $quiz->timeclose and !($quiz->review & QUIZ_REVIEW_OPEN)) {
|
||||
error(get_string("noreviewuntil", "quiz", userdate($quiz->timeclose)));
|
||||
}
|
||||
if (time() >= $quiz->timeclose and !($quiz->review & QUIZ_REVIEW_CLOSED)) {
|
||||
error(get_string("noreview", "quiz"));
|
||||
}
|
||||
} else {
|
||||
// immediately after the attempt the review should be in the protected window if required
|
||||
$popup = $quiz->popup;
|
||||
}
|
||||
if ($attempt->userid != $USER->id) {
|
||||
error("This is not your attempt!");
|
||||
@@ -59,57 +67,82 @@
|
||||
|
||||
add_to_log($course->id, "quiz", "review", "review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", "$cm->id");
|
||||
|
||||
|
||||
// Print the page header
|
||||
/// Print the page header
|
||||
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$strquiz = get_string("modulename", "quiz");
|
||||
$strreport = get_string("report", "quiz");
|
||||
$strreview = get_string("review", "quiz");
|
||||
$strname = get_string("name");
|
||||
$strattempts = get_string("attempts", "quiz");
|
||||
$strscore = get_string("score", "quiz");
|
||||
$strgrade = get_string("grade");
|
||||
$strbestgrade = get_string("bestgrade", "quiz");
|
||||
$strtimetaken = get_string("timetaken", "quiz");
|
||||
$strtimecompleted = get_string("timecompleted", "quiz");
|
||||
$strtimecompleted = get_string("completedon", "quiz");
|
||||
$stroverdue = get_string("overdue", "quiz");
|
||||
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
if (!empty($popup)) {
|
||||
define('MESSAGE_WINDOW', true); // This prevents the message window coming up
|
||||
print_header($course->shortname.': '.format_string($quiz->name), '', '', '', '', false, '', '', false, '');
|
||||
/// Include Javascript protection for this page
|
||||
include('protect_js.php');
|
||||
} else {
|
||||
$strupdatemodule = isteacheredit($course->id)
|
||||
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
|
||||
: "";
|
||||
print_header_simple(format_string($quiz->name), "",
|
||||
"<a href=\"index.php?id=$course->id\">$strquizzes</a>
|
||||
-> <a href=\"view.php?id=$cm->id\">".format_string($quiz->name,true)."</a> -> $strreview",
|
||||
"", "", true);
|
||||
|
||||
"", "", true, $strupdatemodule);
|
||||
}
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
|
||||
/// Include Javascript protection for this page if required
|
||||
|
||||
if (!empty($quiz->popup)) {
|
||||
include("protect_js.php");
|
||||
/// Print heading and tabs if this is part of a preview
|
||||
if ($isteacher) {
|
||||
$currenttab = ($attempt->userid == $USER->id) ? 'preview' : '';
|
||||
include('tabs.php');
|
||||
} else {
|
||||
print_heading(format_string($quiz->name));
|
||||
}
|
||||
|
||||
if (!($questions = quiz_get_attempt_questions($quiz, $attempt))) {
|
||||
error("Unable to get questions from database for quiz $quiz->id attempt $attempt->id number $attempt->attempt");
|
||||
/// Load all the questions and states needed by this script
|
||||
|
||||
// load the questions needed by page
|
||||
$pagelist = $showall ? quiz_questions_in_quiz($attempt->layout) : quiz_questions_on_page($attempt->layout, $page);
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}quiz_questions q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($pagelist)";
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
error('No questions found');
|
||||
}
|
||||
|
||||
if (!$result = quiz_grade_responses($quiz, $questions)) {
|
||||
error("Could not re-grade this quiz attempt!");
|
||||
// Load the question type specific information
|
||||
if (!quiz_get_question_options($questions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// Restore the question sessions to their most recent states
|
||||
// creating new sessions where required
|
||||
if (!$states = quiz_restore_question_sessions($questions, $quiz, $attempt)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
/// Print infobox
|
||||
|
||||
$timelimit = (int)$quiz->timelimit * 60;
|
||||
$overtime = 0;
|
||||
|
||||
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
|
||||
if($timelimit && $timetaken > ($timelimit + 60)) {
|
||||
$overtime = $timetaken - $timelimit;
|
||||
$overtime = format_time($overtime);
|
||||
if ($attempt->timefinish) {
|
||||
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
|
||||
if($timelimit && $timetaken > ($timelimit + 60)) {
|
||||
$overtime = $timetaken - $timelimit;
|
||||
$overtime = format_time($overtime);
|
||||
}
|
||||
$timetaken = format_time($timetaken);
|
||||
} else {
|
||||
$timetaken = "-";
|
||||
}
|
||||
$timetaken = format_time($timetaken);
|
||||
} else {
|
||||
$timetaken = "-";
|
||||
$timetaken = get_string('unfinished', 'quiz');
|
||||
}
|
||||
|
||||
$table->align = array("right", "left");
|
||||
@@ -118,41 +151,84 @@
|
||||
$picture = print_user_picture($student->id, $course->id, $student->picture, false, true);
|
||||
$table->data[] = array($picture, fullname($student, true));
|
||||
}
|
||||
$table->data[] = array("$strtimetaken:", $timetaken);
|
||||
$table->data[] = array("$strtimecompleted:", userdate($attempt->timefinish));
|
||||
if ($isteacher and count($attempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND userid = '$attempt->userid'", 'attempt ASC')) > 1) {
|
||||
// print list of attempts
|
||||
$attemptlist = '';
|
||||
foreach ($attempts as $at) {
|
||||
$attemptlist .= ($at->id == $attempt->id)
|
||||
? '<b>'.$at->attempt.'</b>, '
|
||||
: '<a href="review.php?attempt='.$at->id.($showall?'&showall=true':'').'">'.$at->attempt.'</a>, ';
|
||||
}
|
||||
$table->data[] = array(get_string('attempts', 'quiz').':', trim($attemptlist, ' ,'));
|
||||
}
|
||||
|
||||
$table->data[] = array(get_string('startedon', 'quiz').':', userdate($attempt->timestart));
|
||||
if ($attempt->timefinish) {
|
||||
$table->data[] = array("$strtimecompleted:", userdate($attempt->timefinish));
|
||||
$table->data[] = array("$strtimetaken:", $timetaken);
|
||||
}
|
||||
if (!empty($overtime)) {
|
||||
$table->data[] = array("$stroverdue:", $overtime);
|
||||
}
|
||||
if ($quiz->grade) {
|
||||
if($overtime) {
|
||||
$result->sumgrades = "0";
|
||||
$result->percentage = "0";
|
||||
$result->grade = "0.0";
|
||||
}
|
||||
$table->data[] = array("$strscore:", "$result->sumgrades/$quiz->sumgrades ($result->percentage %)");
|
||||
$table->data[] = array("$strgrade:", "$result->grade/$quiz->grade");
|
||||
$percentage = format_float(($attempt->sumgrades/$quiz->sumgrades)*100, 0);
|
||||
$grade = format_float(($attempt->sumgrades/$quiz->sumgrades)*$quiz->grade, $CFG->quiz_decimalpoints);
|
||||
$table->data[] = array("$strscore:", "$attempt->sumgrades/$quiz->sumgrades ($percentage %)");
|
||||
$table->data[] = array("$strgrade:", $grade.get_string('outof', 'quiz').$quiz->grade);
|
||||
}
|
||||
if ($isteacher and $attempt->userid == $USER->id) {
|
||||
// the teacher is at the end of a preview. Print button to start new preview
|
||||
unset($buttonoptions);
|
||||
$buttonoptions['q'] = $quiz->id;
|
||||
$buttonoptions['forcenew'] = true;
|
||||
echo '<center>';
|
||||
print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz'));
|
||||
echo '</center>';
|
||||
} else { // print number of the attempt
|
||||
print_heading(get_string('reviewofattempt', 'quiz', $attempt->attempt));
|
||||
}
|
||||
|
||||
print_table($table);
|
||||
|
||||
if (isteacher($course->id)) {
|
||||
print_continue("report.php?q=$quiz->id");
|
||||
} else {
|
||||
print_continue("view.php?q=$quiz->id");
|
||||
/// Print the navigation panel if required
|
||||
$numpages = quiz_number_of_pages($attempt->layout);
|
||||
if ($numpages > 1 and !$showall) {
|
||||
print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&');
|
||||
echo '<center><a href="review.php?attempt='.$attempt->id.'&showall=true">';
|
||||
print_string('showall', 'quiz');
|
||||
echo '</a></center>';
|
||||
}
|
||||
|
||||
$quiz->feedback = true;
|
||||
$quiz->correctanswers = true;
|
||||
$quiz->shuffleanswers = false;
|
||||
$quiz->shufflequestions = false;
|
||||
quiz_print_quiz_questions($quiz, $questions, $result);
|
||||
/// Print all the questions
|
||||
|
||||
if (isteacher($course->id)) {
|
||||
print_continue("report.php?q=$quiz->id");
|
||||
} else {
|
||||
print_continue("view.php?q=$quiz->id");
|
||||
$pagequestions = explode(',', $pagelist);
|
||||
$number = quiz_first_questionnumber($attempt->layout, $pagelist);
|
||||
foreach ($pagequestions as $i) {
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
$options->validation = QUIZ_EVENTVALIDATE === $states[$i]->event;
|
||||
$options->history = ($isteacher and !$attempt->preview) ? 'all' : 'graded';
|
||||
// Print the question
|
||||
if ($i > 0) {
|
||||
echo "<br />\n";
|
||||
}
|
||||
quiz_print_quiz_question($questions[$i], $states[$i], $number, $quiz, $options);
|
||||
$number += $questions[$i]->length;
|
||||
}
|
||||
|
||||
print_footer($course);
|
||||
// Print the navigation panel if required
|
||||
if ($numpages > 1 and !$showall) {
|
||||
print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&');
|
||||
}
|
||||
|
||||
// print javascript button to close the window, if necessary
|
||||
if (!$isteacher) {
|
||||
include('attempt_close_js.php');
|
||||
}
|
||||
|
||||
if (empty($popup)) {
|
||||
print_footer($course);
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<!-- This form fragment is used to set the review options -->
|
||||
|
||||
<td align="right">
|
||||
<table>
|
||||
<tr valign="top">
|
||||
<tr><td align="right"><b><?php print_string("reviewoptions", "quiz") ?>:</b></td></tr>
|
||||
<tr><td align="right"><?php print_string("reviewimmediately", "quiz") ?>:</td></tr>
|
||||
<tr><td align="right"><?php print_string("reviewopen", "quiz") ?>:</td></tr>
|
||||
<tr><td align="right"><?php print_string("reviewclosed", "quiz") ?>:</td></tr>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table>
|
||||
<tr valign="top">
|
||||
<td align="center"><?php print_string('responses', 'quiz') ?></td>
|
||||
<td align="center"><?php print_string('scores', 'quiz') ?></td>
|
||||
<td align="center"><?php print_string('feedback', 'quiz') ?></td>
|
||||
<td align="center"><?php print_string('answers', 'quiz') ?></td>
|
||||
<!-- <td align="center"><?php print_string('solutions', 'quiz') ?></td>-->
|
||||
<td>
|
||||
<?php helpbutton("review2", get_string("allowreview","quiz"), "quiz"); ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="responsesimmediately" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_IMMEDIATELY) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="scoreimmediately" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_IMMEDIATELY) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="feedbackimmediately" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="answersimmediately" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<!--
|
||||
<td align="center">
|
||||
<input type="checkbox" name="solutionsimmediately" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_IMMEDIATELY) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="responsesopen" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_OPEN) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="scoreopen" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="feedbackopen" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="answersopen" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_OPEN) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<!--
|
||||
<td align="center">
|
||||
<input type="checkbox" name="solutionsopen" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_OPEN) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="responsesclosed" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_CLOSED) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="scoreclosed" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="feedbackclosed" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<td align="center">
|
||||
<input type="checkbox" name="answersclosed" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_CLOSED) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
<!--
|
||||
<td align="center">
|
||||
<input type="checkbox" name="solutionsclosed" value="Yes" <?php echo ($form->review & QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_CLOSED) ? 'checked="checked"' : '' ?> />
|
||||
</td>
|
||||
-->
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* This page prints a review of a particular question attempt
|
||||
*
|
||||
* @version $Id$
|
||||
* @author Martin Dougiamas and many others. This has recently been completely
|
||||
* rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
|
||||
* the Serving Mathematics project
|
||||
* {@link http://maths.york.ac.uk/serving_maths}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once('locallib.php');
|
||||
|
||||
// Either stateid or (attemptid AND questionid) must be given
|
||||
$stateid = optional_param('state', 0, PARAM_INT); // state id
|
||||
$attemptid = optional_param('attempt', 0, PARAM_INT); // attempt id
|
||||
$questionid = optional_param('question', 0, PARAM_INT); // attempt id
|
||||
|
||||
$number = required_param('number', PARAM_INT); // question number
|
||||
|
||||
if ($stateid) {
|
||||
if (! $state = get_record('quiz_states', 'id', $stateid)) {
|
||||
error('Invalid state id');
|
||||
}
|
||||
if (! $attempt = get_record('quiz_attempts', 'id', $state->attempt)) {
|
||||
error('No such attempt ID exists');
|
||||
}
|
||||
} elseif ($attemptid) {
|
||||
if (! $attempt = get_record('quiz_attempts', 'id', $attemptid)) {
|
||||
error('No such attempt ID exists');
|
||||
}
|
||||
if (! $neweststate = get_field('quiz_newest_states', 'new', 'attemptid', $attemptid, 'questionid', $questionid)) {
|
||||
// newest_state not set, probably because this is an old attempt from the old quiz module code
|
||||
if (! $state = get_record('quiz_states', 'question', $questionid, 'attempt', $attemptid)) {
|
||||
error('Invalid question id');
|
||||
}
|
||||
} else {
|
||||
if (! $state = get_record('quiz_states', 'id', $neweststate->new)) {
|
||||
error('Invalid state id');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error('Parameter missing');
|
||||
}
|
||||
if (! $question = get_record('quiz_questions', 'id', $state->question)) {
|
||||
error('Question for this state is missing');
|
||||
}
|
||||
if (! $quiz = get_record('quiz', 'id', $attempt->quiz)) {
|
||||
error('Course module is incorrect');
|
||||
}
|
||||
if (! $course = get_record('course', 'id', $quiz->course)) {
|
||||
error('Course is misconfigured');
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id)) {
|
||||
error('Course Module ID was incorrect');
|
||||
}
|
||||
|
||||
require_login($course->id, false, $cm);
|
||||
$isteacher = isteacher($course->id);
|
||||
|
||||
if (!$isteacher) {
|
||||
if (!$attempt->timefinish) {
|
||||
redirect('attempt.php?q='.$quiz->id);
|
||||
}
|
||||
// If not even responses are to be shown in review then we
|
||||
// don't allow any review
|
||||
if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
|
||||
error(get_string("noreview", "quiz"));
|
||||
}
|
||||
if ((time() - $attempt->timefinish) > 120) { // always allow review right after attempt
|
||||
if (time() < $quiz->timeclose and !($quiz->review & QUIZ_REVIEW_OPEN)) {
|
||||
error(get_string("noreviewuntil", "quiz", userdate($quiz->timeclose)));
|
||||
}
|
||||
if (time() >= $quiz->timeclose and !($quiz->review & QUIZ_REVIEW_CLOSED)) {
|
||||
error(get_string("noreview", "quiz"));
|
||||
}
|
||||
}
|
||||
if ($attempt->userid != $USER->id) {
|
||||
error('This is not your attempt!');
|
||||
}
|
||||
}
|
||||
|
||||
//add_to_log($course->id, 'quiz', 'review', "review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// Print the page header
|
||||
|
||||
$strquizzes = get_string('modulenameplural', 'quiz');
|
||||
$strreviewquestion = get_string('reviewquestion', 'quiz');
|
||||
|
||||
print_header();
|
||||
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
/// Print heading
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
$instance = get_record('quiz_question_instances', 'quiz', $quiz->id, 'question', $question->id);
|
||||
$question->instance = $instance->id;
|
||||
$question->maxgrade = $instance->grade;
|
||||
$question->name_prefix = 'r';
|
||||
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
|
||||
|
||||
quiz_restore_state($question, $state);
|
||||
$state->last_graded = $state;
|
||||
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
$options->validation = ($state->event == QUIZ_EVENTVALIDATE);
|
||||
$options->history = 'all';
|
||||
|
||||
quiz_print_quiz_question($question, $state, $number, $quiz, $options);
|
||||
|
||||
print_footer();
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
.feedbacktext {
|
||||
display:block;
|
||||
}
|
||||
|
||||
body#mod-quiz-attempts table#attempts,
|
||||
body#mod-quiz-attempts table#commands
|
||||
{
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts {
|
||||
margin: 20px auto;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .header,
|
||||
body#mod-quiz-attempts table#attempts .cell
|
||||
{
|
||||
padding: 4px;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .header .commands {
|
||||
display: inline;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .picture {
|
||||
width: 40px;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts td {
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-left-style: solid;
|
||||
border-right-style: solid;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.feedbacktext {
|
||||
text-align:right;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .header {
|
||||
text-align: left;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .picture {
|
||||
text-align: center !important;
|
||||
}
|
||||
body#mod-quiz-attempts .controls {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
body#mod-quiz-attempts table#attempts td {
|
||||
border-color: #dddddd;
|
||||
}
|
||||
body#mod-quiz-attempts table#attempts .r1 {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.editorhelptext {
|
||||
font-size:0.7em;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php // $Id$
|
||||
/// This file to be included so we can assume config.php has already been included.
|
||||
|
||||
if (empty($quiz)) {
|
||||
error('You cannot call this script in that way');
|
||||
}
|
||||
if (!isset($currenttab)) {
|
||||
$currenttab = '';
|
||||
}
|
||||
if (!isset($cm)) {
|
||||
$cm = get_coursemodule_from_instance('quiz', $quiz->id);
|
||||
}
|
||||
if (!isset($course)) {
|
||||
$course = get_record('course', 'id', $quiz->course);
|
||||
}
|
||||
|
||||
//print_heading(format_string($quiz->name));
|
||||
|
||||
$tabs = array();
|
||||
$row = array();
|
||||
$inactive = array();
|
||||
|
||||
$row[] = new tabobject('info', "view.php?q=$quiz->id", get_string('info', 'quiz'));
|
||||
$row[] = new tabobject('attempts', "attempts.php?q=$quiz->id", get_string('attempts', 'quiz'));
|
||||
$row[] = new tabobject('preview', "attempt.php?q=$quiz->id", get_string('preview', 'quiz'));
|
||||
if (isteacheredit($course->id)) {
|
||||
$row[] = new tabobject('edit', "edit.php?quizid=$quiz->id", get_string('editquiz', 'quiz'));
|
||||
//$row[] = new tabobject('update', "$CFG->wwwroot/course/mod.php?update=$cm->id&sesskey=$USER->sesskey", get_string('updatethis', '', get_string('modulename', 'quiz')));
|
||||
}
|
||||
$row[] = new tabobject('reports', "report.php?q=$quiz->id", get_string('reports', 'quiz'));
|
||||
|
||||
$tabs[] = $row;
|
||||
|
||||
if ($currenttab == 'reports' and isset($mode)) {
|
||||
$inactive[] = 'reports';
|
||||
$allreports = get_list_of_plugins("mod/quiz/report");
|
||||
$reportlist = array ('simplestat', 'fullstat'); // Standard reports we want to show first
|
||||
|
||||
foreach ($allreports as $report) {
|
||||
if (!in_array($report, $reportlist)) {
|
||||
$reportlist[] = $report;
|
||||
}
|
||||
}
|
||||
|
||||
$row = array();
|
||||
$currenttab = '';
|
||||
foreach ($reportlist as $report) {
|
||||
$row[] = new tabobject($report, "report.php?q=$quiz->id&mode=$report",
|
||||
get_string("report$report", "quiz"));
|
||||
if ($report == $mode) {
|
||||
$currenttab = $report;
|
||||
}
|
||||
}
|
||||
$tabs[] = $row;
|
||||
}
|
||||
|
||||
print_tabs($tabs, $currenttab, $inactive);
|
||||
|
||||
?>
|
||||
+1
-1
@@ -10,7 +10,7 @@ function countdown_clock() {
|
||||
|
||||
if(quizTimerValue == 0) {
|
||||
clearTimeout(timeout_id);
|
||||
//alert(timesup);
|
||||
document.forms[0].timeup.value = 1;
|
||||
document.forms[0].submit();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$module->version = 2005042002; // The (date) version of this module
|
||||
$module->version = 2005050405; // The (date) version of this module
|
||||
$module->requires = 2005021600; // Requires this Moodle version
|
||||
$module->cron = 0; // How often should cron check this module (seconds)?
|
||||
|
||||
|
||||
+111
-57
@@ -11,15 +11,16 @@
|
||||
$q = optional_param('q', 0, PARAM_INT); // quiz ID
|
||||
$edit = optional_param('edit', '');
|
||||
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_record("course_modules", "id", $id)) {
|
||||
error("Course Module ID was incorrect");
|
||||
}
|
||||
|
||||
|
||||
if (! $course = get_record("course", "id", $cm->course)) {
|
||||
error("Course is misconfigured");
|
||||
}
|
||||
|
||||
|
||||
if (! $quiz = get_record("quiz", "id", $cm->instance)) {
|
||||
error("Course module is incorrect");
|
||||
}
|
||||
@@ -37,7 +38,8 @@
|
||||
}
|
||||
|
||||
require_login($course->id, false, $cm);
|
||||
|
||||
$isteacher = isteacher($course->id);
|
||||
|
||||
// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quiz->questions and isteacheredit($course->id)) {
|
||||
redirect('edit.php?quizid='.$quiz->id);
|
||||
@@ -75,30 +77,54 @@
|
||||
|
||||
echo '<td id="middle-column">';
|
||||
|
||||
if (isteacher($course->id)) {
|
||||
$attemptcount = count_records_select("quiz_attempts", "quiz = '$quiz->id' AND timefinish > 0");
|
||||
$a->count = count_records("quiz_grades", "quiz", "$quiz->id");
|
||||
$a->items = get_string('users');
|
||||
$strusers = get_string('counteditems', '', $a);
|
||||
$strviewallanswers = get_string("viewallanswers","quiz",$attemptcount);
|
||||
echo "<div class=\"reportlink\"><a href=\"report.php?id=$cm->id\">$strviewallanswers ($strusers)</a></div>";
|
||||
} else if (!$cm->visible) {
|
||||
notice(get_string("activityiscurrentlyhidden"));
|
||||
}
|
||||
|
||||
$available = ($quiz->timeopen < $timenow and $timenow < $quiz->timeclose) || isteacher($course->id);
|
||||
$available = ($quiz->timeopen < $timenow and $timenow < $quiz->timeclose) || $isteacher;
|
||||
|
||||
// Print the main part of the page
|
||||
|
||||
// Print heading and tabs for teacher
|
||||
if ($isteacher) {
|
||||
$currenttab = 'info';
|
||||
include('tabs.php');
|
||||
}
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
print_simple_box(format_text($quiz->intro), 'center', '70%', '', 5, 'generalbox', 'intro');
|
||||
print_simple_box(format_text($quiz->intro), "center");
|
||||
}
|
||||
|
||||
if ($quiz->attempts > 1) {
|
||||
echo "<p align=\"center\">".get_string("attemptsallowed", "quiz").": $quiz->attempts</p>";
|
||||
echo "<p align=\"center\">".get_string("grademethod", "quiz").": ".$QUIZ_GRADE_METHOD[$quiz->grademethod]."</p>";
|
||||
} else {
|
||||
echo "<br />";
|
||||
}
|
||||
if ($available) {
|
||||
if ($quiz->timelimit) {
|
||||
echo "<p align=\"center\">".get_string("quiztimelimit","quiz", format_time($quiz->timelimit * 60))."</p>";
|
||||
}
|
||||
echo "<p align=\"center\">".get_string("quizavailable", "quiz", userdate($quiz->timeclose));
|
||||
} else if ($timenow < $quiz->timeopen) {
|
||||
echo "<p align=\"center\">".get_string("quiznotavailable", "quiz", userdate($quiz->timeopen));
|
||||
} else {
|
||||
echo "<p align=\"center\">".get_string("quizclosed", "quiz", userdate($quiz->timeclose));
|
||||
}
|
||||
|
||||
|
||||
// This is all the teacher will get
|
||||
if ($isteacher) {
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isguest()) {
|
||||
print_heading(get_string("guestsno", "quiz"));
|
||||
|
||||
$wwwroot = $CFG->wwwroot.'/login/index.php';
|
||||
if (!empty($CFG->loginhttps)) {
|
||||
$wwwroot = str_replace('http','https', $wwwroot);
|
||||
}
|
||||
|
||||
notice_yesno(get_string('guestno', 'quiz').'<br /><br />'.get_string('liketologin'),
|
||||
$wwwroot, $_SERVER['HTTP_REFERER']);
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
@@ -109,11 +135,10 @@
|
||||
$numattempts = 0;
|
||||
}
|
||||
|
||||
if ($quiz->attempts > 1) {
|
||||
echo "<p align=\"center\">".get_string("attemptsallowed", "quiz").": $quiz->attempts</p>";
|
||||
echo "<p align=\"center\">".get_string("grademethod", "quiz").": ".$QUIZ_GRADE_METHOD[$quiz->grademethod]."</p>";
|
||||
} else {
|
||||
echo "<br />";
|
||||
$unfinished = false;
|
||||
if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
|
||||
$attempts[] = $unfinishedattempt;
|
||||
$unfinished = true;
|
||||
}
|
||||
|
||||
$strattempt = get_string("attempt", "quiz");
|
||||
@@ -125,7 +150,7 @@
|
||||
|
||||
$mygrade = quiz_get_best_grade($quiz, $USER->id);
|
||||
|
||||
if ($numattempts) {
|
||||
if ($numattempts) {
|
||||
if ($quiz->grade and $quiz->sumgrades) {
|
||||
if ($quiz->grade <> $quiz->sumgrades) {
|
||||
$table->head = array($strattempt, $strtimetaken, $strtimecompleted, "$strmarks / $quiz->sumgrades", "$strgrade / $quiz->grade");
|
||||
@@ -142,61 +167,88 @@
|
||||
$table->size = array("", "", "");
|
||||
}
|
||||
foreach ($attempts as $attempt) {
|
||||
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
|
||||
$timetaken = format_time($timetaken);
|
||||
$timetaken = '';
|
||||
$datecompleted = '';
|
||||
if ($attempt->timefinish > 0) {
|
||||
$timetaken = format_time($attempt->timefinish - $attempt->timestart);
|
||||
$datecompleted = userdate($attempt->timefinish);
|
||||
} else if ($available) {
|
||||
$timetaken = format_time(time() - $attempt->timestart);
|
||||
$strconfirmstartattempt = addslashes(get_string("confirmstartattempt","quiz"));
|
||||
$datecompleted = "\n".'<script language="javascript" type="text/javascript">';
|
||||
$datecompleted .= "\n<!--\n"; // -->
|
||||
if (!empty($quiz->popup)) {
|
||||
$datecompleted .= "var windowoptions = 'left=0, top=0, height='+window.screen.height+
|
||||
', width='+window.screen.width+', channelmode=yes, fullscreen=yes, scrollbars=yes, '+
|
||||
'resizeable=no, directories=no, toolbar=no, titlebar=no, location=no, status=no, '+
|
||||
'menubar=no';\n";
|
||||
$jslink = 'javascript:';
|
||||
if ($quiz->timelimit) {
|
||||
$jslink .= "if (confirm('$strconfirmstartattempt')) ";
|
||||
}
|
||||
$jslink .= "var popup = window.open(\\'attempt.php?id=$cm->id\\', \\'quizpopup\\', windowoptions);";
|
||||
} else {
|
||||
$jslink = "attempt.php?id=$cm->id";
|
||||
}
|
||||
|
||||
$linktext = get_string('continueattemptquiz', 'quiz');
|
||||
$datecompleted .= "document.write('<a href=\"$jslink\" alt=\"$linktext\">$linktext</a>');";
|
||||
$datecompleted .= "\n-->\n";
|
||||
$datecompleted .= '</script>';
|
||||
$datecompleted .= '<noscript>';
|
||||
$datecompleted .= '<strong>'.get_string('noscript', 'quiz').'</strong>';
|
||||
$datecompleted .= '</noscript>';
|
||||
} else {
|
||||
$timetaken = "-";
|
||||
$timetaken = format_time($quiz->timeclose - $attempt->timestart);
|
||||
$datecompleted = userdate($quiz->timeclose);
|
||||
}
|
||||
|
||||
if ($quiz->grade and $quiz->sumgrades) {
|
||||
$attemptmark = '';
|
||||
$attemptmark = format_float($attempt->sumgrades,$quiz->decimalpoints);
|
||||
$attemptgrade = format_float(($attempt->sumgrades/$quiz->sumgrades)*$quiz->grade,$quiz->decimalpoints);
|
||||
if ($attemptgrade == $mygrade) {
|
||||
$attemptgrade = "<span class=\"highlight\">$attemptgrade</span>";
|
||||
}
|
||||
if (quiz_review_allowed($quiz)) {
|
||||
$attemptmark = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attempt->sumgrades</a>";
|
||||
|
||||
// if attempt is closed and review is allowed then make attemptnumber and
|
||||
// mark and grade into links to review page
|
||||
if (quiz_review_allowed($quiz) and $attempt->timefinish > 0) {
|
||||
$attemptmark = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">".format_float($attempt->sumgrades,$quiz->decimalpoints).'</a>';
|
||||
$attemptgrade = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">$attemptgrade</a>";
|
||||
$attempt->attempt = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">#$attempt->attempt</a>";
|
||||
}
|
||||
|
||||
if ($quiz->grade <> $quiz->sumgrades) {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
format_time($attempt->timefinish - $attempt->timestart),
|
||||
userdate($attempt->timefinish),
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$timetaken,
|
||||
$datecompleted,
|
||||
$attemptmark, $attemptgrade);
|
||||
} else {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
format_time($attempt->timefinish - $attempt->timestart),
|
||||
userdate($attempt->timefinish),
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$timetaken,
|
||||
$datecompleted,
|
||||
$attemptgrade);
|
||||
}
|
||||
} else { // No grades are being used
|
||||
if (quiz_review_allowed($quiz)) {
|
||||
$attempt->attempt = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">#$attempt->attempt</a>";
|
||||
if($attempt->timefinish > 0) {
|
||||
$attempt->attempt = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">#$attempt->attempt</a>";
|
||||
} else {
|
||||
$attempt->attempt = "<a href=\"attempt.php?id=$id\">#$attempt->attempt</a>";
|
||||
}
|
||||
}
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
format_time($attempt->timefinish - $attempt->timestart),
|
||||
userdate($attempt->timefinish));
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$timetaken,
|
||||
$datecompleted);
|
||||
}
|
||||
}
|
||||
print_table($table);
|
||||
}
|
||||
|
||||
if ($available) {
|
||||
if ($quiz->timelimit) {
|
||||
echo "<p align=\"center\">".get_string("quiztimelimit","quiz", format_time($quiz->timelimit * 60))."</p>";
|
||||
}
|
||||
echo "<p align=\"center\">".get_string("quizavailable", "quiz", userdate($quiz->timeclose));
|
||||
} else if ($timenow < $quiz->timeopen) {
|
||||
echo "<p align=\"center\">".get_string("quiznotavailable", "quiz", userdate($quiz->timeopen));
|
||||
} else {
|
||||
echo "<p align=\"center\">".get_string("quizclosed", "quiz", userdate($quiz->timeclose));
|
||||
}
|
||||
|
||||
|
||||
if (!$quiz->questions) {
|
||||
print_heading(get_string("noquestions", "quiz"));
|
||||
} else {
|
||||
if ($numattempts < $quiz->attempts or !$quiz->attempts) {
|
||||
if ($numattempts < $quiz->attempts or !$quiz->attempts) {
|
||||
if ($available) {
|
||||
$options["id"] = $cm->id;
|
||||
if ($numattempts and $quiz->grade) {
|
||||
@@ -216,22 +268,24 @@
|
||||
if ($quiz->grade) {
|
||||
print_heading(get_string("yourfinalgradeis", "quiz", "$mygrade / $quiz->grade"));
|
||||
}
|
||||
print_continue('../../course/view.php?id='.$course->id);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the page
|
||||
echo '</td></tr></table>';
|
||||
|
||||
print_footer($course);
|
||||
|
||||
|
||||
function quiz_review_allowed($quiz) {
|
||||
if (!$quiz->review) {
|
||||
// If not even responses are to be shown in review then we
|
||||
// don't allow any review
|
||||
if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
|
||||
return false;
|
||||
}
|
||||
if ((time() < $quiz->timeclose) and ($quiz->review == QUIZ_REVIEW_AFTER)) {
|
||||
if ((time() < $quiz->timeclose) and !($quiz->review & QUIZ_REVIEW_OPEN)) {
|
||||
return false;
|
||||
}
|
||||
if ((time() > $quiz->timeclose) and ($quiz->review == QUIZ_REVIEW_BEFORE)) {
|
||||
if ((time() > $quiz->timeclose) and !($quiz->review & QUIZ_REVIEW_CLOSED)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -4,13 +4,13 @@ defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
|
||||
$window = (!empty($quiz->popup)) ? 'quizpopup' : '_self';
|
||||
$windowoptions = ($window == '_self') ? '' : "left=0, top=0, height='+window.screen.height+', width='+window.screen.width+', channelmode=yes, fullscreen=yes, scrollbars=yes, resizeable=no, directories=no, toolbar=no, titlebar=no, location=no, status=no, menubar=no";
|
||||
$buttontext = ($numattempts) ? get_string('reattemptquiz', 'quiz') : get_string('attemptquiznow', 'quiz');
|
||||
|
||||
$buttontext = ($unfinished) ? get_string('continueattemptquiz', 'quiz') : $buttontext;
|
||||
?>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
<!--
|
||||
document.write('<input type="button" value="<?php echo $buttontext ?>" '+
|
||||
'onclick="javascript: <?php if ($quiz->timelimit) echo "if (confirm(\\'$strconfirmstartattempt\\'))"; ?> '+
|
||||
'onclick="javascript: <?php if ($quiz->timelimit and !$unfinished) echo "if (confirm(\\'$strconfirmstartattempt\\'))"; ?> '+
|
||||
'window.open(\'attempt.php?id=<?php echo $cm->id ?>\', \'<?php echo $window ?>\', \'<?php echo $windowoptions ?>\'); " />');
|
||||
// -->
|
||||
</script>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
// This is compared against the values stored in the database to determine
|
||||
// whether upgrades should be performed (see lib/db/*.php)
|
||||
|
||||
$version = 2005043000; // YYYYMMDD = date
|
||||
$version = 2005050600; // YYYYMMDD = date
|
||||
// XY = increments within a single day
|
||||
|
||||
$release = '1.5 ALPHA'; // Human-friendly version name
|
||||
|
||||
Reference in New Issue
Block a user