mirror of
https://github.com/moodle/moodle.git
synced 2025-07-26 00:31:35 +02:00
MDL-5270 - give students a feeback comment for their performance in the entire quiz.
Also, along the way I noticed and fixed MDL-6290 student grades not rescaled when quiz maximum grade changed.
This commit is contained in:
14
lang/en_utf8/help/quiz/overallfeedback.html
Normal file
14
lang/en_utf8/help/quiz/overallfeedback.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<p align="center"><b>Overall feedback</b></p>
|
||||
|
||||
<p>The overall feedback is some text that is shown to a student after
|
||||
they have completed an attempt at the quiz. The text that is shown
|
||||
can depend on the grade the student got.</p>
|
||||
|
||||
<p>For example, if you type "Well done" into the first feedback box, type 40%
|
||||
in the first grade boundary box, and type "Please study this week's work again"
|
||||
in the second feedback box, then students who score 40% or better will see the
|
||||
"Well done" message, and students who score less than 40% will see the other message.</p>
|
||||
|
||||
<p>The grade boundaries can be specified either as a percentage, for example "31.41%", or
|
||||
as a number, for example "7". If your quiz is out of 10 marks, a grade boundary of 7 means
|
||||
7/10 or better.</p>
|
@@ -180,6 +180,11 @@ $string['exportnameformat'] = '%%Y%%m%%d-%%H%%M';
|
||||
$string['exportquestions'] = 'Export questions to file';
|
||||
$string['false'] = 'False';
|
||||
$string['feedback'] = 'Feedback';
|
||||
$string['feedbackerrorboundaryformat'] = 'Feedback grade boundaries must be either a percentage or a number. The value you entered in boundary $a is not recognised.';
|
||||
$string['feedbackerrorboundaryoutofrange'] = 'Feedback grade boundaries must be between 0%% and 100%%. The value you entered in boundary $a is out of range.';
|
||||
$string['feedbackerrorjunkinboundary'] = 'You must fill in the feedback boxes without leaving any gaps.';
|
||||
$string['feedbackerrorjunkinfeedback'] = 'You must fill in the feedback boxes without leaving any gaps.';
|
||||
$string['feedbackerrororder'] = 'Feedback grade boundaries must in order, highest first. The value you entered in boundary $a is out of sequence.';
|
||||
$string['file'] = 'File';
|
||||
$string['fileformat'] = 'File format';
|
||||
$string['fillcorrect'] = 'Fill with correct';
|
||||
@@ -203,6 +208,7 @@ $string['geometric'] = 'Geometric';
|
||||
$string['gift'] = 'GIFT format';
|
||||
$string['grade'] = 'Grade';
|
||||
$string['gradeaverage'] = 'Average grade';
|
||||
$string['gradeboundary'] = 'Grade boundary';
|
||||
$string['gradehighest'] = 'Highest grade';
|
||||
$string['grademethod'] = 'Grading method';
|
||||
$string['gradingdetails'] = 'Marks for this submission: $a->raw/$a->max. ';
|
||||
@@ -320,6 +326,7 @@ $string['onlyteachersimport'] = 'Only teachers with editing rights can import qu
|
||||
$string['onlyteachersexport'] = 'Only teachers can export questions';
|
||||
$string['optional'] = 'optional';
|
||||
$string['outof'] = '$a->grade out of a maximum of $a->maxgrade';
|
||||
$string['overallfeedback'] = 'Overall feedback';
|
||||
$string['overdue'] = 'Overdue';
|
||||
$string['pagesize'] = 'Attempts shown per page: ';
|
||||
$string['paragraphquestion'] = 'Paragraph Question not supported at line $a. The question will be ignored';
|
||||
@@ -361,6 +368,7 @@ $string['quizcloses'] = 'Quiz closes';
|
||||
$string['quiznotavailable'] = 'The quiz will not be available until: $a';
|
||||
$string['quizopen'] = 'Open the quiz';
|
||||
$string['quizopens'] = 'Quiz opens';
|
||||
$string['quizsettings'] = 'Quiz settings';
|
||||
$string['quiztimelimit'] = 'Time limit: $a';
|
||||
$string['quiztimer'] = 'Quiz Timer';
|
||||
$string['pleaseclose'] = 'Your request has been processed. You can now close this window';
|
||||
|
@@ -9,12 +9,12 @@
|
||||
// (CL,pk->id)
|
||||
// |
|
||||
// -------------------------------------------------------------------
|
||||
// | | | |
|
||||
// | quiz_grades | quiz_question_versions
|
||||
// | (UL,pk->id,fk->quiz) | (CL,pk->id,fk->quiz)
|
||||
// | |
|
||||
// quiz_attempts quiz_question_instances
|
||||
// (UL,pk->id,fk->quiz) (CL,pk->id,fk->quiz,question)
|
||||
// | | | | |
|
||||
// | quiz_grades | quiz_question_versions |
|
||||
// | (UL,pk->id,fk->quiz) | (CL,pk->id,fk->quiz) |
|
||||
// | | |
|
||||
// quiz_attempts quiz_question_instances quiz_feedback
|
||||
// (UL,pk->id,fk->quiz) (CL,pk->id,fk->quiz,question) (CL,pk->id,fk->quiz)
|
||||
//
|
||||
// Meaning: pk->primary key field of the table
|
||||
// fk->foreign key to link with parent
|
||||
@@ -204,6 +204,8 @@
|
||||
fwrite ($bf,full_tag("DELAY2",4,false,$quiz->delay2));
|
||||
//Now we print to xml question_instances (Course Level)
|
||||
$status = backup_quiz_question_instances($bf,$preferences,$quiz->id);
|
||||
//Now we print to xml quiz_feedback (Course Level)
|
||||
$status = backup_quiz_feedback($bf,$preferences,$quiz->id);
|
||||
//Now we print to xml question_versions (Course Level)
|
||||
$status = backup_quiz_question_versions($bf,$preferences,$quiz->id);
|
||||
//if we've selected to backup users info, then execute:
|
||||
@@ -242,9 +244,6 @@
|
||||
|
||||
//Backup quiz_question_instances contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_question_instances ($bf,$preferences,$quiz) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_question_instances = get_records("quiz_question_instances","quiz",$quiz,"id");
|
||||
@@ -269,11 +268,41 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//Backup quiz_question_instances contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_feedback ($bf,$preferences,$quiz) {
|
||||
$status = true;
|
||||
|
||||
$quiz_feedback = get_records('quiz_feedback', 'quizid', $quiz, 'id');
|
||||
// If there are question_instances ...
|
||||
if ($quiz_feedback) {
|
||||
// Write start tag.
|
||||
$status = $status & fwrite($bf,start_tag('FEEDBACKS', 4, true));
|
||||
|
||||
// Iterate over each question_instance.
|
||||
foreach ($quiz_feedback as $feedback) {
|
||||
|
||||
//Start feedback instance
|
||||
$status = $status & fwrite($bf, start_tag('FEEDBACK',5,true));
|
||||
|
||||
//Print question_instance contents.
|
||||
$status = $status & fwrite($bf, full_tag('ID', 6, false, $feedback->id));
|
||||
$status = $status & fwrite($bf, full_tag('QUIZID', 6, false, $feedback->quizid));
|
||||
$status = $status & fwrite($bf, full_tag('FEEDBACKTEXT', 6, false, $feedback->feedbacktext));
|
||||
$status = $status & fwrite($bf, full_tag('MINGRADE', 6, false, $feedback->mingrade));
|
||||
$status = $status & fwrite($bf, full_tag('MAXGRADE', 6, false, $feedback->maxgrade));
|
||||
|
||||
// End feedback instance.
|
||||
$status = $status & fwrite($bf, end_tag('FEEDBACK', 5, true));
|
||||
}
|
||||
|
||||
// Write end tag.
|
||||
$status = $status & fwrite($bf, end_tag('FEEDBACKS', 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");
|
||||
@@ -304,9 +333,6 @@
|
||||
|
||||
//Backup quiz_grades contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_grades ($bf,$preferences,$quiz) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_grades = get_records("quiz_grades","quiz",$quiz,"id");
|
||||
@@ -334,9 +360,6 @@
|
||||
|
||||
//Backup quiz_attempts contents (executed from quiz_backup_mods)
|
||||
function backup_quiz_attempts ($bf,$preferences,$quiz) {
|
||||
|
||||
global $CFG;
|
||||
|
||||
$status = true;
|
||||
|
||||
$quiz_attempts = get_records("quiz_attempts","quiz",$quiz,"id");
|
||||
|
@@ -1113,6 +1113,25 @@ function quiz_upgrade($oldversion) {
|
||||
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK) << 3));
|
||||
}
|
||||
|
||||
if ($success && $oldversion < 2006081400) {
|
||||
$success = $success && modify_database('', "
|
||||
CREATE TABLE prefix_quiz_feedback (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quizid int(10) unsigned NOT NULL default '0',
|
||||
feedbacktext text NOT NULL default '',
|
||||
mingrade double NOT NULL default '0',
|
||||
maxgrade double NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY quizid (quizid)
|
||||
) TYPE=MyISAM COMMENT='Feedback given to students based on their overall score on the test';
|
||||
");
|
||||
|
||||
$success = $success && execute_sql("
|
||||
INSERT INTO {$CFG->prefix}quiz_feedback (quizid, feedbacktext, maxgrade, mingrade)
|
||||
SELECT id, '', grade + 1, 0 FROM {$CFG->prefix}quiz;
|
||||
");
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
@@ -62,6 +62,16 @@ CREATE TABLE prefix_quiz_question_versions (
|
||||
PRIMARY KEY (id)
|
||||
) TYPE=MyISAM COMMENT='The mapping between old and new versions of a question';
|
||||
|
||||
CREATE TABLE prefix_quiz_feedback (
|
||||
id int(10) unsigned NOT NULL auto_increment,
|
||||
quizid int(10) unsigned NOT NULL default '0',
|
||||
feedbacktext text NOT NULL default '',
|
||||
mingrade double NOT NULL default '0',
|
||||
maxgrade double NOT NULL default '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY quizid (quizid),
|
||||
) TYPE=MyISAM COMMENT='Feedback given to students based on their overall score on the test';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
-- Quiz module, quiz runtime data.
|
||||
-- --------------------------------------------------------
|
||||
|
@@ -1435,6 +1435,24 @@ function quiz_upgrade($oldversion) {
|
||||
(($CFG->quiz_review & QUIZ_REVIEW_FEEDBACK) << 3));
|
||||
}
|
||||
|
||||
if ($success && $oldversion < 2006081400) {
|
||||
$success = $success && modify_database('', "
|
||||
CREATE TABLE prefix_quiz_feedback (
|
||||
id SERIAL PRIMARY KEY,
|
||||
quizid integer NOT NULL default '0',
|
||||
feedbacktext text NOT NULL default '',
|
||||
maxgrade real NOT NULL default '0',
|
||||
mingrade real NOT NULL default '0'
|
||||
);
|
||||
");
|
||||
$success = $success && modify_database('',
|
||||
"CREATE INDEX prefix_quiz_feedback_quizid_idx ON prefix_quiz_feedback (quizid);");
|
||||
|
||||
$success = $success && execute_sql("
|
||||
INSERT INTO {$CFG->prefix}quiz_feedback (quizid, feedbacktext, maxgrade, mingrade)
|
||||
SELECT id, '', grade + 1, 0 FROM {$CFG->prefix}quiz;
|
||||
");
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
@@ -59,6 +59,15 @@ CREATE TABLE prefix_quiz_question_versions (
|
||||
timestamp integer NOT NULL default '0'
|
||||
);
|
||||
|
||||
CREATE TABLE prefix_quiz_feedback (
|
||||
id SERIAL PRIMARY KEY,
|
||||
quizid integer NOT NULL default '0',
|
||||
feedbacktext text NOT NULL default '',
|
||||
mingrade real NOT NULL default '0',
|
||||
maxgrade real NOT NULL default '0'
|
||||
);
|
||||
CREATE INDEX prefix_quiz_feedback_quizid_idx ON prefix_quiz_feedback (quizid);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
-- Quiz module, quiz runtime data.
|
||||
-- --------------------------------------------------------
|
||||
@@ -207,7 +216,7 @@ CREATE TABLE prefix_question_states (
|
||||
penalty real NOT NULL default '0'
|
||||
);
|
||||
CREATE INDEX prefix_question_states_attempt_idx ON prefix_question_states (attempt);
|
||||
CREATE INDEX prefix_question_states_question_idx ON prefix_question_states (question);;
|
||||
CREATE INDEX prefix_question_states_question_idx ON prefix_question_states (question);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
-- Quiz log actions.
|
||||
|
@@ -274,9 +274,8 @@ if (self.name == 'editquestion') {
|
||||
|
||||
// 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');
|
||||
if (!quiz_set_grade(optional_param('maxgrade', 0), $modform)) {
|
||||
error('Could not set a new maximum grade for the quiz');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +307,7 @@ if (self.name == 'editquestion') {
|
||||
|
||||
// Print basic page layout.
|
||||
|
||||
if (isset($modform->instance) and record_exists_sql("SELECT * FROM {$CFG->prefix}quiz_attempts WHERE quiz = '$modform->instance' AND preview = '0' LIMIT 1")){
|
||||
if (isset($modform->instance) and record_exists_select('quiz_attempts', "quiz = '$modform->instance' AND preview = '0'")){
|
||||
// one column layout with table of questions used in this quiz
|
||||
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
|
||||
? update_module_button($modform->cmid, $course->id, get_string('modulename', 'quiz'))
|
||||
|
@@ -121,20 +121,23 @@
|
||||
// or the quiz has no grade, display nothing in grade col
|
||||
if ($bestgrade === NULL || $quiz->grade == 0) {
|
||||
$gradecol = "";
|
||||
$feedbackcol = '';
|
||||
} else {
|
||||
//If all quiz's attempts have visible results, show bestgrade
|
||||
if(all_attempt_results_visible($quiz, $USER)) {
|
||||
$gradecol = "$bestgrade / $quiz->grade";
|
||||
$feedbackcol = quiz_get_feedback($quiz, $bestgrade);
|
||||
} else {
|
||||
$gradecol = "";
|
||||
$feedbackcol = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($course->format == "weeks" or $course->format == "topics") {
|
||||
$table->data[] = array ($printsection, $link, $closequiz, $gradecol);
|
||||
$table->data[] = array ($printsection, $link, $closequiz, $gradecol, $feedbackcol);
|
||||
} else {
|
||||
$table->data[] = array ($link, $closequiz, $gradecol);
|
||||
$table->data[] = array ($link, $closequiz, $gradecol, $feedbackcol);
|
||||
}
|
||||
}
|
||||
|
||||
|
104
mod/quiz/lib.php
104
mod/quiz/lib.php
@@ -58,14 +58,18 @@ define("QUIZ_MAX_EVENT_LENGTH", "432000"); // 5 days maximum
|
||||
* of the new instance.
|
||||
*
|
||||
* @param object $quiz the data that came from the form.
|
||||
* @return integer the id of the new instance.
|
||||
* @return mixed the id of the new instance on success,
|
||||
* false or a string error message on failure.
|
||||
*/
|
||||
function quiz_add_instance($quiz) {
|
||||
|
||||
// Process the options from the form.
|
||||
$quiz->created = time();
|
||||
quiz_process_options($quiz);
|
||||
$quiz->questions = '';
|
||||
$result = quiz_process_options($quiz);
|
||||
if ($result && is_string($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Try to store it in the database.
|
||||
if (!$quiz->id = insert_record("quiz", $quiz)) {
|
||||
@@ -84,12 +88,15 @@ function quiz_add_instance($quiz) {
|
||||
* will update an existing instance with new data.
|
||||
*
|
||||
* @param object $quiz the data that came from the form.
|
||||
* @return boolean true on success, false on failure.
|
||||
* @return mixed true on success, false or a string error message on failure.
|
||||
*/
|
||||
function quiz_update_instance($quiz) {
|
||||
|
||||
// Process the options from the form.
|
||||
quiz_process_options($quiz);
|
||||
$result = quiz_process_options($quiz);
|
||||
if ($result && is_string($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Update the database.
|
||||
$quiz->id = $quiz->instance;
|
||||
@@ -120,7 +127,7 @@ function quiz_delete_instance($id) {
|
||||
|
||||
if ($attempts = get_records("quiz_attempts", "quiz", "$quiz->id")) {
|
||||
foreach ($attempts as $attempt) {
|
||||
// TODO: this should use function in questionlib.php
|
||||
// TODO: this should use the delete_attempt($attempt->uniqueid) function in questionlib.php
|
||||
if (! delete_records("question_states", "attempt", "$attempt->uniqueid")) {
|
||||
$result = false;
|
||||
}
|
||||
@@ -130,20 +137,18 @@ function quiz_delete_instance($id) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! delete_records("quiz_attempts", "quiz", "$quiz->id")) {
|
||||
$tables_to_purge = array(
|
||||
'quiz_attempts' => 'quiz',
|
||||
'quiz_grades' => 'quiz',
|
||||
'quiz_question_instances' => 'quiz',
|
||||
'quiz_grades' => 'quiz',
|
||||
'quiz_feedback' => 'quizid',
|
||||
'quiz' => 'id'
|
||||
);
|
||||
foreach ($tables_to_purge as $table => $keyfield) {
|
||||
if (!delete_records($table, $keyfield, $quiz->id)) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (! delete_records("quiz_grades", "quiz", "$quiz->id")) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (! delete_records("quiz_question_instances", "quiz", "$quiz->id")) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (! delete_records("quiz", "id", "$quiz->id")) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
$pagetypes = page_import_types('mod/quiz/');
|
||||
@@ -496,6 +501,57 @@ function quiz_process_options(&$quiz) {
|
||||
}
|
||||
$quiz->timelimit = round($quiz->timelimit);
|
||||
|
||||
// Quiz feedback
|
||||
|
||||
// Clean up the boundary text.
|
||||
for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
|
||||
if (empty($quiz->feedbacktext[$i])) {
|
||||
$quiz->feedbacktext[$i] = '';
|
||||
} else {
|
||||
$quiz->feedbacktext[$i] = trim($quiz->feedbacktext[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the boundary value is a number or a percentage, and in range.
|
||||
$i = 0;
|
||||
while (!empty($quiz->feedbackboundaries[$i])) {
|
||||
$boundary = trim($quiz->feedbackboundaries[$i]);
|
||||
if (!is_numeric($boundary)) {
|
||||
if (strlen($boundary) > 0 && $boundary[strlen($boundary) - 1] == '%') {
|
||||
$boundary = substr($boundary, 0, -1);
|
||||
if (is_numeric($boundary)) {
|
||||
$boundary = $boundary * $quiz->grade / 100.0;
|
||||
} else {
|
||||
return get_string('feedbackerrorboundaryformat', 'quiz', $i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($boundary <= 0 || $boundary >= $quiz->grade) {
|
||||
return get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1);
|
||||
}
|
||||
if ($i > 0 && $boundary >= $quiz->feedbackboundaries[$i - 1]) {
|
||||
return get_string('feedbackerrororder', 'quiz', $i + 1);
|
||||
}
|
||||
$quiz->feedbackboundaries[$i] = $boundary;
|
||||
$i += 1;
|
||||
}
|
||||
$numboundaries = $i;
|
||||
|
||||
// Check there is nothing in the remaining unused fields.
|
||||
for ($i = $numboundaries; $i < count($quiz->feedbackboundaries); $i += 1) {
|
||||
if (!empty($quiz->feedbackboundaries[$i]) && trim($quiz->feedbackboundaries[$i]) != '') {
|
||||
return get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1);
|
||||
}
|
||||
}
|
||||
for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
|
||||
if (!empty($quiz->feedbacktext[$i]) && trim($quiz->feedbacktext[$i]) != '') {
|
||||
return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
|
||||
}
|
||||
}
|
||||
$quiz->feedbackboundaries[-1] = $quiz->grade + 1; // Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
|
||||
$quiz->feedbackboundaries[$numboundaries] = 0;
|
||||
$quiz->feedbackboundarycount = $numboundaries;
|
||||
|
||||
// Settings that get combined to go into the optionflags column.
|
||||
$quiz->optionflags = 0;
|
||||
if (!empty($quiz->adaptive)) {
|
||||
@@ -593,6 +649,20 @@ function quiz_process_options(&$quiz) {
|
||||
*/
|
||||
function quiz_after_add_or_update($quiz) {
|
||||
|
||||
// Save the feedback
|
||||
delete_records('quiz_feedback', 'quizid', $quiz->id);
|
||||
|
||||
for ($i = 0; $i <= $quiz->feedbackboundarycount; $i += 1) {
|
||||
$feedback = new stdClass;
|
||||
$feedback->quizid = $quiz->id;
|
||||
$feedback->feedbacktext = $quiz->feedbacktext[$i];
|
||||
$feedback->mingrade = $quiz->feedbackboundaries[$i];
|
||||
$feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
|
||||
if (!insert_record('quiz_feedback', $feedback, false)) {
|
||||
return "Could not save quiz feedback.";
|
||||
}
|
||||
}
|
||||
|
||||
// Remember whether this user likes the advanced settings visible or hidden.
|
||||
if (isset($quiz->optionsettingspref)) {
|
||||
set_user_preference('quiz_optionsettingspref', $quiz->optionsettingspref);
|
||||
|
@@ -83,10 +83,18 @@ function quiz_get_user_attempt_unfinished($quizid, $userid) {
|
||||
return get_record("quiz_attempts", "quiz", $quizid, "userid", $userid, "timefinish", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $quizid the quiz id.
|
||||
* @param integer $userid the userid.
|
||||
* @return an array of all the ueser's attempts at this quiz. Returns an empty array if there are none.
|
||||
*/
|
||||
function quiz_get_user_attempts($quizid, $userid) {
|
||||
// Returns a list of all attempts by a user
|
||||
return get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0",
|
||||
"attempt ASC");
|
||||
if ($attempts = get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0",
|
||||
"attempt ASC")) {
|
||||
return $attempts;
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -249,30 +257,132 @@ function quiz_get_all_question_grades($quiz) {
|
||||
return $grades;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the best current grade for a particular user in a quiz.
|
||||
*
|
||||
* @param object $quiz the quiz object.
|
||||
* @param integer $userid the id of the user.
|
||||
* @return float the user's current grade for this quiz.
|
||||
*/
|
||||
function quiz_get_best_grade($quiz, $userid) {
|
||||
/// Get the best current grade for a particular user in a quiz
|
||||
if (!$grade = get_record('quiz_grades', 'quiz', $quiz->id, 'userid', $userid)) {
|
||||
$grade = get_field('quiz_grades', 'grade', 'quiz', $quiz->id, 'userid', $userid);
|
||||
|
||||
// Need to detect errors/no result, without catching 0 scores.
|
||||
if (is_numeric($grade)) {
|
||||
return round($grade,$quiz->decimalpoints);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (round($grade->grade,$quiz->decimalpoints));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the overall grade for a user at a quiz in the quiz_grades table
|
||||
*
|
||||
* @return boolean Indicates success or failure.
|
||||
* @param object $quiz The quiz for which the best grade is to be calculated
|
||||
* and then saved.
|
||||
* @param integer $userid The id of the user to save the best grade for. Can be
|
||||
* null in which case the current user is assumed.
|
||||
*/
|
||||
function quiz_save_best_grade($quiz, $userid=null) {
|
||||
* Convert the raw grade stored in $attempt into a grade out of the maximum
|
||||
* grade for this quiz.
|
||||
*
|
||||
* @param float $rawgrade the unadjusted grade, fof example $attempt->sumgrades
|
||||
* @param object $quiz the quiz object. Only the fields grade, sumgrades and decimalpoints are used.
|
||||
* @return float the rescaled grade.
|
||||
*/
|
||||
function quiz_rescale_grade($rawgrade, $quiz) {
|
||||
if ($quiz->sumgrades) {
|
||||
return round($rawgrade*$quiz->grade/$quiz->sumgrades, $quiz->decimalpoints);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the feedback text that should be show to a student who
|
||||
* got this grade on this quiz.
|
||||
*
|
||||
* @param float $grade a grade on this quiz.
|
||||
* @param integer $quizid the id of the quiz object.
|
||||
* @return string the comment that corresponds to this grade (empty string if there is not one.
|
||||
*/
|
||||
function quiz_feedback_for_grade($grade, $quizid) {
|
||||
$feedback = get_field_select('quiz_feedback', 'feedbacktext',
|
||||
"quizid = $quizid AND mingrade <= $grade AND $grade < maxgrade");
|
||||
|
||||
if (empty($feedback)) {
|
||||
$feedback = '';
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $quizid the id of the quiz object.
|
||||
* @return boolean Whether this quiz has any non-blank feedback text.
|
||||
*/
|
||||
function quiz_has_feedback($quizid) {
|
||||
static $cache = array();
|
||||
if (!array_key_exists($quizid, $cache)) {
|
||||
$cache[$quizid] = record_exists_select('quiz_feedback',
|
||||
"quizid = $quizid AND feedbacktext <> ''");
|
||||
}
|
||||
return $cache[$quizid];
|
||||
}
|
||||
|
||||
/**
|
||||
* The quiz grade is the score that student's results are marked out of. When it
|
||||
* changes, the corresponding data in quiz_grades and quiz_feedback needs to be
|
||||
* rescaled.
|
||||
*
|
||||
* @param float $newgrade the new maximum grade for the quiz.
|
||||
* @param object $quiz the quiz we are updating. Passed by reference so its grade field can be updated too.
|
||||
* @return boolean indicating success or failure.
|
||||
*/
|
||||
function quiz_set_grade($newgrade, &$quiz) {
|
||||
// This is potentially expensive, so only do it if necessary.
|
||||
if (abs($quiz->grade - $newgrade) < 1e-7) {
|
||||
// Nothing to do.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use a transaction, so that on those databases that support it, this is safer.
|
||||
begin_sql();
|
||||
|
||||
// Update the quiz table.
|
||||
$success = set_field('quiz', 'grade', $newgrade, 'id', $quiz->instance);
|
||||
|
||||
// Rescaling the other data is only possible if the old grade was non-zero.
|
||||
if ($quiz->grade > 1e-7) {
|
||||
global $CFG;
|
||||
|
||||
$factor = $newgrade/$quiz->grade;
|
||||
$quiz->grade = $newgrade;
|
||||
|
||||
// Update the quiz_grades table.
|
||||
$timemodified = time();
|
||||
$success = $success && set_field('quiz_grades', 'grade',
|
||||
'$factor * grade, timemodified = $timemodified',
|
||||
'quiz', $quiz->id);
|
||||
|
||||
// Update the quiz_grades table.
|
||||
$success = $success && execute_sql('quiz_feedback', 'mingrade',
|
||||
'$factor * mingrade, maxgrade = $factor * maxgrade',
|
||||
'quizid', $quiz->id);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
return commit_sql();
|
||||
} else {
|
||||
rollback_sql();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the overall grade for a user at a quiz in the quiz_grades table
|
||||
*
|
||||
* @param object $quiz The quiz for which the best grade is to be calculated and then saved.
|
||||
* @param integer $userid The userid to calculate the grade for. Defaults to the current user.
|
||||
* @return boolean Indicates success or failure.
|
||||
*/
|
||||
function quiz_save_best_grade($quiz, $userid = null) {
|
||||
global $USER;
|
||||
|
||||
// Assume the current user if $userid is null
|
||||
if (is_null($userid)) {
|
||||
if (empty($userid)) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
|
||||
@@ -284,12 +394,10 @@ function quiz_save_best_grade($quiz, $userid=null) {
|
||||
|
||||
// Calculate the best grade
|
||||
$bestgrade = quiz_calculate_best_grade($quiz, $attempts);
|
||||
$bestgrade = $quiz->sumgrades ? (($bestgrade / $quiz->sumgrades) * $quiz->grade) : 0;
|
||||
$bestgrade = round($bestgrade, $quiz->decimalpoints);
|
||||
$bestgrade = quiz_rescale_grade($bestgrade, $quiz);
|
||||
|
||||
// Save the best grade in the database
|
||||
if ($grade = get_record('quiz_grades', 'quiz', $quiz->id, 'userid',
|
||||
$userid)) {
|
||||
if ($grade = get_record('quiz_grades', 'quiz', $quiz->id, 'userid', $userid)) {
|
||||
$grade->grade = $bestgrade;
|
||||
$grade->timemodified = time();
|
||||
if (!update_record('quiz_grades', $grade)) {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<!-- 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. -->
|
||||
|
||||
<?php
|
||||
// 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. -->
|
||||
|
||||
require_once("$CFG->dirroot/mod/quiz/locallib.php");
|
||||
|
||||
// Set any form variables that have not been initialized to their default value.
|
||||
@@ -78,6 +78,37 @@
|
||||
$form->delay2 = $CFG->quiz_delay2;
|
||||
}
|
||||
|
||||
// Get any existing feedback text out of the database.
|
||||
if (!empty($form->id)) {
|
||||
$feedbacks = get_records('quiz_feedback', 'quizid', $form->id, 'mingrade DESC');
|
||||
} else {
|
||||
$feedbacks = array();
|
||||
}
|
||||
$form->feedbacktext = array();
|
||||
$form->feedbackboundaries = array();
|
||||
foreach ($feedbacks as $feedback) {
|
||||
$form->feedbacktext[] = $feedback->feedbacktext;
|
||||
if ($feedback->mingrade > 0) {
|
||||
$form->feedbackboundaries[] = (100.0 * $feedback->mingrade / $form->grade) . '%';
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure there are at least 5 feedbacktexts, or a bit more than the current nubmer.
|
||||
$numfeedbacks = max(
|
||||
count($form->feedbacktext) * 1.5,
|
||||
count($form->feedbackboundaries) * 1.5,
|
||||
5
|
||||
);
|
||||
|
||||
for ($i = 0; $i < $numfeedbacks; $i += 1) {
|
||||
if (!array_key_exists($i, $form->feedbacktext)) {
|
||||
$form->feedbacktext[$i] = '';
|
||||
}
|
||||
if (!array_key_exists($i, $form->feedbackboundaries)) {
|
||||
$form->feedbackboundaries[$i] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// The following are used for drop-down menus
|
||||
$yesnooptions = array(get_string("no"), get_string("yes"));
|
||||
|
||||
@@ -186,11 +217,47 @@
|
||||
// This time out put the ones that were not fixed.
|
||||
$fix = output_quiz_options_fields($form, 0);
|
||||
|
||||
// Output standard module settings.
|
||||
print_standard_coursemodule_settings($form);
|
||||
|
||||
// Output the boxes for typing feedback depending on overall quiz score.
|
||||
?>
|
||||
<tr><td colspan="2">
|
||||
<?php print_heading_with_help(get_string('overallfeedback', 'quiz'), 'overallfeedback', 'quiz'); ?>
|
||||
</td></tr>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('gradeboundary', 'quiz') ?>:</b></td>
|
||||
<td align="left">100%</td>
|
||||
</tr>
|
||||
|
||||
<?php for ($i = 0; $i < count($form->feedbacktext); $i = $i + 1) { ?>
|
||||
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('feedback', 'quiz') ?>:</b></td>
|
||||
<td align="left">
|
||||
<input type="text" name="feedbacktext[]" size="60" value="<?php p($form->feedbacktext[$i]) ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php if ($i < count($form->feedbacktext) - 1) { ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('gradeboundary', 'quiz') ?>:</b></td>
|
||||
<td align="left">
|
||||
<input type="text" name="feedbackboundaries[]" size="20" value="<?php p($form->feedbackboundaries[$i]) ?>" />
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
|
||||
<?php } ?>
|
||||
<tr valign="top">
|
||||
<td align="right"><b><?php print_string('gradeboundary', 'quiz') ?>:</b></td>
|
||||
<td align="left">0%</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
if ($fix) {
|
||||
// Some options were fixed by the admin. Show them, but hidden behind an Advanced button.
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td align="right"><b><?php print_string('advancedsettings') ?>:</b>
|
||||
|
@@ -88,6 +88,7 @@ class quiz_report extends quiz_default_report {
|
||||
$noattempts = optional_param('noattempts', 0, PARAM_INT);
|
||||
$detailedmarks = optional_param('detailedmarks', 0, PARAM_INT);
|
||||
$pagesize = optional_param('pagesize', 10, PARAM_INT);
|
||||
$hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.e-7 && $quiz->sumgrades > 1.e-7;
|
||||
|
||||
// Now check if asked download of data
|
||||
if ($download) {
|
||||
@@ -134,6 +135,11 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasfeedback) {
|
||||
$tablecolumns[] = 'feedback';
|
||||
$tableheaders[] = get_string('feedback', 'quiz');
|
||||
}
|
||||
|
||||
if (!$download) {
|
||||
// Set up the table
|
||||
|
||||
@@ -200,6 +206,9 @@ class quiz_report extends quiz_default_report {
|
||||
$headers[] = '#'.$questions[$id]->number;
|
||||
}
|
||||
}
|
||||
if ($hasfeedback) {
|
||||
$headers[] = get_string('feedback', 'quiz');
|
||||
}
|
||||
$colnum = 0;
|
||||
foreach ($headers as $item) {
|
||||
$myxls->write(0,$colnum,$item,$formatbc);
|
||||
@@ -225,6 +234,9 @@ class quiz_report extends quiz_default_report {
|
||||
$headers .= "\t#".$question->number;
|
||||
}
|
||||
}
|
||||
if ($hasfeedback) {
|
||||
$headers .= "\t" . get_string('feedback', 'quiz');
|
||||
}
|
||||
echo $headers." \n";
|
||||
}
|
||||
|
||||
@@ -333,6 +345,14 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
|
||||
// If there is feedback, include it in the query.
|
||||
if ($hasfeedback) {
|
||||
$factor = $quiz->grade/$quiz->sumgrades;
|
||||
$select .= ', qf.feedbacktext ';
|
||||
$from .= " JOIN {$CFG->prefix}quiz_feedback AS qf ON " .
|
||||
"qf.quizid = $quiz->id AND qf.mingrade <= qa.sumgrades * $factor AND qa.sumgrades * $factor < qf.maxgrade";
|
||||
}
|
||||
|
||||
// Fetch the attempts
|
||||
if (!empty($from)) { // if we're in the site course and displaying no attempts, it makes no sense to do the query.
|
||||
$attempts = get_records_sql($select.$from.$where.$sort.$limit);
|
||||
@@ -403,6 +423,13 @@ class quiz_report extends quiz_default_report {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($hasfeedback) {
|
||||
if ($attempt->timefinish) {
|
||||
$row[] = $attempt->feedbacktext;
|
||||
} else {
|
||||
$row[] = '-';
|
||||
}
|
||||
}
|
||||
if (!$download) {
|
||||
$table->add_data($row);
|
||||
} else if ($download == 'Excel') {
|
||||
|
@@ -103,6 +103,8 @@
|
||||
$mod->id, $newid);
|
||||
//We have to restore the question_instances now (course level table)
|
||||
$status = quiz_question_instances_restore_mods($newid,$info,$restore);
|
||||
//We have to restore the feedback now (course level table)
|
||||
$status = quiz_feedback_restore_mods($newid, $info, $restore, $quiz);
|
||||
//We have to restore the question_versions now (course level table)
|
||||
$status = quiz_question_versions_restore_mods($newid,$info,$restore);
|
||||
//Now check if want to restore user data and do it.
|
||||
@@ -132,7 +134,11 @@
|
||||
$status = true;
|
||||
|
||||
//Get the quiz_question_instances array
|
||||
if (array_key_exists('QUESTION_INSTANCES', $info['MOD']['#'])) {
|
||||
$instances = $info['MOD']['#']['QUESTION_INSTANCES']['0']['#']['QUESTION_INSTANCE'];
|
||||
} else {
|
||||
$instances = array();
|
||||
}
|
||||
|
||||
//Iterate over question_instances
|
||||
for($i = 0; $i < sizeof($instances); $i++) {
|
||||
@@ -181,6 +187,52 @@
|
||||
return $status;
|
||||
}
|
||||
|
||||
//This function restores the quiz_question_instances
|
||||
function quiz_feedback_restore_mods($quiz_id, $info, $restore, $quiz) {
|
||||
$status = true;
|
||||
|
||||
//Get the quiz_feedback array
|
||||
if (array_key_exists('FEEDBACKS', $info['MOD']['#'])) {
|
||||
$feedbacks = $info['MOD']['#']['FEEDBACKS']['0']['#']['FEEDBACK'];
|
||||
|
||||
//Iterate over the feedbacks
|
||||
foreach ($feedbacks as $feedback_info) {
|
||||
//traverse_xmlize($feedback_info); //Debug
|
||||
//print_object ($GLOBALS['traverse_array']); //Debug
|
||||
//$GLOBALS['traverse_array']=""; //Debug
|
||||
|
||||
//We'll need this later!!
|
||||
$oldid = backup_todb($feedback_info['#']['ID']['0']['#']);
|
||||
|
||||
//Now, build the quiz_feedback record structure
|
||||
$feedback = new stdClass();
|
||||
$feedback->quizid = $quiz_id;
|
||||
$feedback->feedbacktext = backup_todb($feedback_info['#']['FEEDBACKTEXT']['0']['#']);
|
||||
$feedback->mingrade = backup_todb($feedback_info['#']['MINGRADE']['0']['#']);
|
||||
$feedback->maxgrade = backup_todb($feedback_info['#']['MAXGRADE']['0']['#']);
|
||||
|
||||
//The structure is equal to the db, so insert the quiz_question_instances
|
||||
$newid = insert_record('quiz_feedback', $feedback);
|
||||
|
||||
if ($newid) {
|
||||
//We have the newid, update backup_ids
|
||||
backup_putid($restore->backup_unique_code, 'quiz_feedback', $oldid, $newid);
|
||||
} else {
|
||||
$status = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$feedback = new stdClass();
|
||||
$feedback->quizid = $quiz_id;
|
||||
$feedback->feedbacktext = '';
|
||||
$feedback->mingrade = 0;
|
||||
$feedback->maxgrade = $quiz->grade + 1;
|
||||
insert_record('quiz_feedback', $feedback);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
//This function restores the quiz_question_versions
|
||||
function quiz_question_versions_restore_mods($quiz_id,$info,$restore) {
|
||||
|
||||
|
@@ -31,6 +31,9 @@
|
||||
error("The course module for the quiz with id $quiz->id is missing");
|
||||
}
|
||||
|
||||
$grade = quiz_rescale_grade($attempt->sumgrades, $quiz);
|
||||
$feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
|
||||
|
||||
if (!count_records('question_sessions', 'attemptid', $attempt->uniqueid)) {
|
||||
// this question has not yet been upgraded to the new model
|
||||
quiz_upgrade_states($attempt);
|
||||
@@ -194,13 +197,16 @@
|
||||
|
||||
$a = new stdClass;
|
||||
$percentage = round(($attempt->sumgrades/$quiz->sumgrades)*100, 0);
|
||||
$a->grade = round(($attempt->sumgrades/$quiz->sumgrades)*$quiz->grade, $CFG->quiz_decimalpoints);
|
||||
$a->grade = $grade;
|
||||
$a->maxgrade = $quiz->grade;
|
||||
$rawscore = round($attempt->sumgrades, $CFG->quiz_decimalpoints);
|
||||
$table->data[] = array("$strscore:", "$rawscore/$quiz->sumgrades ($percentage %)");
|
||||
$table->data[] = array("$strgrade:", get_string('outof', 'quiz', $a));
|
||||
}
|
||||
}
|
||||
if ($options->feedback && $feedback) {
|
||||
$table->data[] = array(get_string('feedback', 'quiz'), $feedback);
|
||||
}
|
||||
if ($isteacher and $attempt->userid == $USER->id) {
|
||||
// the teacher is at the end of a preview. Print button to start new preview
|
||||
unset($buttonoptions);
|
||||
|
@@ -5,7 +5,7 @@
|
||||
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$module->version = 2006081000; // The (date) version of this module
|
||||
$module->version = 2006081400; // The (date) version of this module
|
||||
$module->requires = 2006080900; // Requires this Moodle version
|
||||
$module->cron = 0; // How often should cron check this module (seconds)?
|
||||
|
||||
|
@@ -48,15 +48,13 @@
|
||||
|
||||
$timenow = time();
|
||||
|
||||
// Initialize $PAGE, compute blocks
|
||||
|
||||
// Initialize $PAGE, compute blocks
|
||||
$PAGE = page_create_instance($quiz->id);
|
||||
$pageblocks = blocks_setup($PAGE);
|
||||
$blocks_preferred_width = bounded_number(180, blocks_preferred_width($pageblocks[BLOCK_POS_LEFT]), 210);
|
||||
|
||||
// Print the page header
|
||||
|
||||
if (($edit != -1) and $PAGE->user_allowed_editing()) {
|
||||
// Print the page header
|
||||
if ($edit != -1 and $PAGE->user_allowed_editing()) {
|
||||
$USER->editing = $edit;
|
||||
}
|
||||
|
||||
@@ -77,13 +75,15 @@
|
||||
|
||||
$available = ($quiz->timeopen < $timenow and ($timenow < $quiz->timeclose or !$quiz->timeclose)) || $isteacher;
|
||||
|
||||
// Print the main part of the page
|
||||
// Print the main part of the page
|
||||
|
||||
// Print heading and tabs for teacher
|
||||
if ($isteacher) {
|
||||
$currenttab = 'info';
|
||||
include('tabs.php');
|
||||
}
|
||||
|
||||
// Print quiz name and description.
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
@@ -91,12 +91,15 @@
|
||||
print_simple_box(format_text($quiz->intro, FORMAT_MOODLE, $formatoptions), "center");
|
||||
}
|
||||
|
||||
// Print information about number of attempts and grading method.
|
||||
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 ($quiz->attempts != 1) {
|
||||
echo "<p align=\"center\">".get_string("grademethod", "quiz").": ".$QUIZ_GRADE_METHOD[$quiz->grademethod]."</p>";
|
||||
}
|
||||
|
||||
// Print information about timings.
|
||||
if ($available) {
|
||||
if ($quiz->timelimit) {
|
||||
echo "<p align=\"center\">".get_string("quiztimelimit","quiz", format_time($quiz->timelimit * 60))."</p>";
|
||||
@@ -118,199 +121,190 @@
|
||||
notify("<a href=\"report.php?mode=overview&id=$cm->id\">".get_string('numattempts', 'quiz', $a).'</a>');
|
||||
}
|
||||
|
||||
echo '</td></tr></table>';
|
||||
print_footer($course);
|
||||
end_page($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Guests can't do a quiz, so offer them a choice of logging in going back.
|
||||
if (isguest()) {
|
||||
|
||||
$wwwroot = $CFG->wwwroot.'/login/index.php';
|
||||
$loginurl = $CFG->wwwroot.'/login/index.php';
|
||||
if (!empty($CFG->loginhttps)) {
|
||||
$wwwroot = str_replace('http:','https:', $wwwroot);
|
||||
$loginurl = str_replace('http:','https:', $loginurl);
|
||||
}
|
||||
|
||||
notice_yesno(get_string('guestsno', 'quiz').'<br /><br />'.get_string('liketologin'),
|
||||
$wwwroot, $_SERVER['HTTP_REFERER']);
|
||||
print_footer($course);
|
||||
echo '</td></tr></table>';
|
||||
notice_yesno('<p>' . get_string('guestsno', 'quiz') . "</p>\n\n</p>" .
|
||||
get_string('liketologin') . '</p>', $loginurl, $_SERVER['HTTP_REFERER']);
|
||||
|
||||
end_page($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($attempts = quiz_get_user_attempts($quiz->id, $USER->id)) {
|
||||
$numattempts = count($attempts);
|
||||
} else {
|
||||
$numattempts = 0;
|
||||
}
|
||||
|
||||
// Get this user's attempts.
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $USER->id);
|
||||
$unfinished = false;
|
||||
if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
|
||||
$attempts[] = $unfinishedattempt;
|
||||
$unfinished = true;
|
||||
}
|
||||
$numattempts = count($attempts);
|
||||
|
||||
$strattempt = get_string("attempt", "quiz");
|
||||
$strtimetaken = get_string("timetaken", "quiz");
|
||||
$strtimecompleted = get_string("timecompleted", "quiz");
|
||||
$strgrade = get_string("grade");
|
||||
$strmarks = get_string('marks', 'quiz');
|
||||
$strbestgrade = $QUIZ_GRADE_METHOD[$quiz->grademethod];
|
||||
|
||||
$windowoptions = "left=0, top=0, channelmode=yes, fullscreen=yes, scrollbars=yes, resizeable=no, directories=no, toolbar=no, titlebar=no, location=no, status=no, menubar=no";
|
||||
$strfeedback = get_string('feedback', 'quiz');
|
||||
|
||||
$mygrade = quiz_get_best_grade($quiz, $USER->id);
|
||||
|
||||
/// Now print table with existing attempts
|
||||
$gradecolumn=0;
|
||||
$overallstats=1;
|
||||
|
||||
if ($attempts) {
|
||||
// Print table with existing attempts
|
||||
|
||||
//step thru each attempt, checking there are any attempts
|
||||
//for which the score can be displayed (need grade columns),
|
||||
//and checking if overall grades can be displayed - no attempts for
|
||||
//which the score cannot be displayed
|
||||
// Work out which columns we need, taking account what data is available in each attempt.
|
||||
$gradecolumn = 0;
|
||||
$overallstats = 1;
|
||||
foreach ($attempts as $attempt) {
|
||||
$attemptoptions = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
$attemptoptions->scores ? $gradecolumn=1 : $overallstats=0;
|
||||
if ($attemptoptions->scores) {
|
||||
$gradecolumn = 1;
|
||||
} else {
|
||||
$overallstats = 0;
|
||||
}
|
||||
/// prepare table header
|
||||
}
|
||||
$gradecolumn = $gradecolumn && $quiz->grade && $quiz->sumgrades;
|
||||
$markcolumn = $gradecolumn && ($quiz->grade <> $quiz->sumgrades);
|
||||
$feedbackcolumn = quiz_has_feedback($quiz->id);
|
||||
|
||||
// prepare table header
|
||||
$table->head = array($strattempt, $strtimecompleted);
|
||||
$table->align = array("center", "left");
|
||||
$table->size = array("", "");
|
||||
if ($gradecolumn && $quiz->grade and $quiz->sumgrades) { // Grades used so have more columns in table
|
||||
if ($quiz->grade <> $quiz->sumgrades) {
|
||||
if ($markcolumn) {
|
||||
$table->head[] = "$strmarks / $quiz->sumgrades";
|
||||
$table->align[] = 'right';
|
||||
$table->size[] = '';
|
||||
}
|
||||
if ($gradecolumn) {
|
||||
$table->head[] = "$strgrade / $quiz->grade";
|
||||
$table->align[] = 'right';
|
||||
$table->size[] = '';
|
||||
}
|
||||
if ($feedbackcolumn) {
|
||||
$table->head[] = $strfeedback;
|
||||
$table->align[] = 'left';
|
||||
$table->size[] = '';
|
||||
}
|
||||
if (isset($quiz->showtimetaken)) {
|
||||
$table->head[] = $strtimetaken;
|
||||
$table->align[] = 'center';
|
||||
$table->align[] = 'left';
|
||||
$table->size[] = '';
|
||||
}
|
||||
|
||||
/// One row for each attempt
|
||||
// One row for each attempt
|
||||
foreach ($attempts as $attempt) {
|
||||
$attemptoptions = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
$row = array();
|
||||
|
||||
/// prepare strings for time taken and date completed
|
||||
// Add the attempt number, making it a link, if appropriate.
|
||||
$row[] = make_review_link('#' . $attempt->attempt, $quiz, $attempt);
|
||||
|
||||
// prepare strings for time taken and date completed
|
||||
$timetaken = '';
|
||||
$datecompleted = '';
|
||||
if ($attempt->timefinish > 0) { // attempt has finished
|
||||
if ($attempt->timefinish > 0) {
|
||||
// attempt has finished
|
||||
$timetaken = format_time($attempt->timefinish - $attempt->timestart);
|
||||
$datecompleted = userdate($attempt->timefinish);
|
||||
} else if ($available) { // The student can continue this attempt, so put appropriate link
|
||||
} else if ($available) {
|
||||
// The attempt is still in progress.
|
||||
$timetaken = format_time(time() - $attempt->timestart);
|
||||
$datecompleted = "\n".'<script language="javascript" type="text/javascript">';
|
||||
$datecompleted .= "\n<!--\n"; // -->
|
||||
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
|
||||
$attempturl=sid_process_url("attempt.php?id=$cm->id");
|
||||
} else {
|
||||
$attempturl="attempt.php?id=$cm->id";
|
||||
};
|
||||
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:var popup = window.open(\\'$attempturl\\', \\'quizpopup\\', windowoptions);";
|
||||
} else {
|
||||
$jslink = $attempturl;
|
||||
}
|
||||
|
||||
$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 { // attempt was not completed but is also not available any more.
|
||||
$datecompleted = '';
|
||||
} else if ($quiz->timeclose) {
|
||||
// The attempt was not completed but is also not available any more becuase the quiz is closed.
|
||||
$timetaken = format_time($quiz->timeclose - $attempt->timestart);
|
||||
$datecompleted = $quiz->timeclose ? userdate($quiz->timeclose) : '';
|
||||
$datecompleted = userdate($quiz->timeclose);
|
||||
} else {
|
||||
// Something wheird happened.
|
||||
$timetaken = '';
|
||||
$datecompleted = '';
|
||||
}
|
||||
$row[] = $datecompleted;
|
||||
|
||||
if ($markcolumn) {
|
||||
if ($attemptoptions->scores) {
|
||||
$row[] = make_review_link(round($attempt->sumgrades, $quiz->decimalpoints), $quiz, $attempt);
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$attemptoptions = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
|
||||
/// prepare strings for attempt number, mark and grade
|
||||
//if attempt's score is allowed to be viewed, & qz->sumgrades and qz->sumgrades defined:
|
||||
if ($attemptoptions->scores && $quiz->grade and $quiz->sumgrades) {
|
||||
$attemptmark = round($attempt->sumgrades,$quiz->decimalpoints);
|
||||
$attemptgrade = round(($attempt->sumgrades/$quiz->sumgrades)*$quiz->grade,$quiz->decimalpoints);
|
||||
|
||||
// Ouside the if becuase we may be showing feedback but not grades.
|
||||
$attemptgrade = quiz_rescale_grade($attempt->sumgrades, $quiz);
|
||||
if ($gradecolumn) {
|
||||
if ($attemptoptions->scores) {
|
||||
// highlight the highest grade if appropriate
|
||||
if ($overallstats && $attemptgrade == $mygrade and ($quiz->grademethod == QUIZ_GRADEHIGHEST)) {
|
||||
$attemptgrade = "<span class=\"highlight\">$attemptgrade</span>";
|
||||
if ($overallstats && !is_null($mygrade) && $attemptgrade == $mygrade && $quiz->grademethod == QUIZ_GRADEHIGHEST) {
|
||||
$formattedgrade = "<span class='highlight'>$attemptgrade</span>";
|
||||
} else {
|
||||
$formattedgrade = $attemptgrade;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if ($quiz->popup) { // need to link to popup window
|
||||
$attemptmark = link_to_popup_window ("/mod/quiz/review.php?q=$quiz->id&attempt=$attempt->id", 'quizpopup', round($attempt->sumgrades,$quiz->decimalpoints), '+window.screen.height+', '+window.screen.width+', '', $windowoptions, true);
|
||||
$attemptgrade = link_to_popup_window ("/mod/quiz/review.php?q=$quiz->id&attempt=$attempt->id", 'quizpopup', $attemptgrade, '+window.screen.height+', '+window.screen.width+', '', $windowoptions, true);
|
||||
$attempt->attempt = link_to_popup_window ("/mod/quiz/review.php?q=$quiz->id&attempt=$attempt->id", 'quizpopup', "#$attempt->attempt", '+window.screen.height+', '+window.screen.width+', '', $windowoptions, true);
|
||||
$row[] = make_review_link($formattedgrade, $quiz, $attempt);
|
||||
} else {
|
||||
$attemptmark = "<a href=\"review.php?q=$quiz->id&attempt=$attempt->id\">".round($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>";
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ($quiz->grade <> $quiz->sumgrades) {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$datecompleted,
|
||||
$attemptmark, $attemptgrade);
|
||||
if ($feedbackcolumn) {
|
||||
if ($attemptoptions->feedback) {
|
||||
$row[] = quiz_feedback_for_grade($attemptgrade, $quiz->id);
|
||||
} else {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$datecompleted,
|
||||
$attemptgrade);
|
||||
}
|
||||
} else { // No grades are being used
|
||||
if (quiz_review_allowed($quiz)) {
|
||||
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>";
|
||||
$row[] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$helpbutton=helpbutton('missing\ grade', get_string('wheregrade', 'quiz'), 'quiz', true, false, '',true);
|
||||
if($gradecolumn) {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$datecompleted,
|
||||
$helpbutton);
|
||||
|
||||
} else {
|
||||
$table->data[] = array( $attempt->attempt,
|
||||
$datecompleted);
|
||||
}
|
||||
}
|
||||
if (isset($quiz->showtimetaken)) {
|
||||
$table->data[] = $timetaken;
|
||||
$row[] = $timetaken;
|
||||
}
|
||||
|
||||
$table->data[] = $row;
|
||||
}
|
||||
print_table($table);
|
||||
}
|
||||
|
||||
if (!$quiz->questions) {
|
||||
print_heading(get_string("noquestions", "quiz"));
|
||||
} else {
|
||||
if ($numattempts < $quiz->attempts or !$quiz->attempts) {
|
||||
|
||||
if ($available) {
|
||||
$options["id"] = $cm->id;
|
||||
//if overall stats are allowed (no attemps' grade not visible),
|
||||
//and there is at least one attempt, and quiz->grade:
|
||||
if ($overallstats and $numattempts and $quiz->grade) {
|
||||
print_heading("$strbestgrade: $mygrade / $quiz->grade.");
|
||||
// Print information about the student's best score for this quiz if possible.
|
||||
$moreattempts = $numattempts < $quiz->attempts || $quiz->attempts == 0;
|
||||
if (!$moreattempts) {
|
||||
print_heading(get_string("nomoreattempts", "quiz"));
|
||||
}
|
||||
|
||||
if ($numattempts && $quiz->sumgrades) {
|
||||
if (!is_null($mygrade)) {
|
||||
if ($available && $moreattempts) {
|
||||
$strbestgrade = $QUIZ_GRADE_METHOD[$quiz->grademethod];
|
||||
$grademessage = "$strbestgrade: $mygrade / $quiz->grade.";
|
||||
} else {
|
||||
$grademessage = get_string("yourfinalgradeis", "quiz", "$mygrade / $quiz->grade");
|
||||
}
|
||||
|
||||
if ($overallstats) {
|
||||
print_heading($grademessage);
|
||||
}
|
||||
|
||||
if ($feedbackcolumn) {
|
||||
echo '<p align="center">', quiz_feedback_for_grade($mygrade, $quiz->id), '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!($moreattempts && $available)) {
|
||||
print_continue($CFG->webroot . '/course/view.php?id=' . $course->id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($quiz->questions) {
|
||||
// Print a button to start the quiz if appropriate.
|
||||
if ($available && $moreattempts) {
|
||||
echo "<br />";
|
||||
echo "</p>";
|
||||
echo "<div align=\"center\">";
|
||||
if ($quiz->delay1 or $quiz->delay2) {
|
||||
//quiz enforced time delay
|
||||
@@ -341,22 +335,17 @@
|
||||
echo "</div>\n";
|
||||
}
|
||||
} else {
|
||||
print_heading(get_string("nomoreattempts", "quiz"));
|
||||
//if $quiz->grade and $quiz->sumgrades, and student is allowed to
|
||||
//see summary statistics (no attempt's grade is concealed),
|
||||
//show the student their final grade
|
||||
if ($quiz->grade and $quiz->sumgrades and $overallstats) {
|
||||
print_heading(get_string("yourfinalgradeis", "quiz", "$mygrade / $quiz->grade"));
|
||||
// No questions in quiz.
|
||||
print_heading(get_string("noquestions", "quiz"));
|
||||
}
|
||||
print_continue('../../course/view.php?id='.$course->id);
|
||||
}
|
||||
}
|
||||
// Finish the page
|
||||
echo '</td></tr></table>';
|
||||
|
||||
// Finish the page - this needs to be the same as in the if teacher block above.
|
||||
echo '</td></tr></table>';
|
||||
print_footer($course);
|
||||
|
||||
function quiz_review_allowed($quiz) {
|
||||
// Utility functions =================================================================
|
||||
|
||||
function quiz_review_allowed($quiz) {
|
||||
// If not even responses are to be shown in review then we
|
||||
// don't allow any review
|
||||
if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
|
||||
@@ -369,10 +358,10 @@
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function print_start_quiz_button($quiz, $attempts, $numattempts, $unfinished, $cm) {
|
||||
function print_start_quiz_button($quiz, $attempts, $numattempts, $unfinished, $cm) {
|
||||
$strconfirmstartattempt = '';
|
||||
|
||||
if ($unfinished) {
|
||||
@@ -422,6 +411,27 @@ document.write('<input type="button" value="<?php echo $buttontext ?>" onclick="
|
||||
<strong><?php print_string('noscript', 'quiz'); ?></strong>
|
||||
</noscript>
|
||||
<?php
|
||||
}
|
||||
|
||||
function make_review_link($linktext, $quiz, $attempt) {
|
||||
$windowoptions = "left=0, top=0, channelmode=yes, fullscreen=yes, scrollbars=yes, resizeable=no, directories=no, toolbar=no, titlebar=no, location=no, status=no, menubar=no";
|
||||
|
||||
$link = $linktext;
|
||||
|
||||
if ($attempt->timefinish && quiz_review_allowed($quiz)) {
|
||||
$url = "review.php?q=$quiz->id&attempt=$attempt->id";
|
||||
if ($quiz->popup) {
|
||||
$link = link_to_popup_window('/mod/quiz/' . $url, 'quizpopup', $linktext, '+window.screen.height+', '+window.screen.width+', '', $windowoptions, true);
|
||||
} else {
|
||||
$link = "<a href='$url'>$linktext</a>";
|
||||
}
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
function end_page($course) {
|
||||
echo '</td></tr></table>';
|
||||
print_footer($course);
|
||||
}
|
||||
?>
|
||||
|
Reference in New Issue
Block a user