$string['close'] = 'Close window';
$string['closepreview'] = 'Close preview';
$string['closereview'] = 'Close review';
+$string['comment'] = 'Comment';
+$string['commentorgrade'] = 'Make comment or override grade';
$string['completedon'] = 'Completed on';
$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['confirmserverdelete'] = 'Are you sure you want to remove the server <b>$a</b> from the list?';
$string['event2'] = 'Save';
$string['event3'] = 'Grade';
$string['event5'] = 'Validate';
-$string['event6'] = 'Close';
+$string['event6'] = 'Close&Grade';
$string['event7'] = 'Submit';
$string['event8'] = 'Close';
+$string['event9'] = 'Manual Grade';
$string['examview'] = 'Examview';
$string['existingcategory1'] = 'a literal from an already existing set of literals that are also used by other questions in this category';
$string['existingcategory2'] = 'a file from an already existing set of files that are also used by other questions in this category';
$state->answer = isset($state->responses['']) ? $state->responses[''] : '';
// Save the state
- if (isset($state->update)) { // this ->update field is only used by the
- // regrading function to force the old state record to be overwritten
+ if (isset($state->update)) { // this forces the old state record to be overwritten
update_record('question_states', $state);
} else {
if (!$state->id = insert_record('question_states', $state)) {
unset($state->answer);
return false;
}
+ }
- // this is the most recent state
- if (!record_exists('question_sessions', 'attemptid',
- $state->attempt, 'questionid', $question->id)) {
- $new->attemptid = $state->attempt;
- $new->questionid = $question->id;
- $new->newest = $state->id;
- $new->sumpenalty = $state->sumpenalty;
- if (!insert_record('question_sessions', $new)) {
- error('Could not insert entry in question_sessions');
- }
- } else {
- set_field('question_sessions', 'newest', $state->id, 'attemptid',
- $state->attempt, 'questionid', $question->id);
+ // create or update the session
+ if (!record_exists('question_sessions', 'attemptid',
+ $state->attempt, 'questionid', $question->id)) {
+ $new->attemptid = $state->attempt;
+ $new->questionid = $question->id;
+ $new->newest = $state->id;
+ $new->sumpenalty = $state->sumpenalty;
+ if (!insert_record('question_sessions', $new)) {
+ error('Could not insert entry in question_sessions');
}
- if (question_state_is_graded($state)) {
- // this is also the most recent graded state
- if ($newest = get_record('question_sessions', 'attemptid',
- $state->attempt, 'questionid', $question->id)) {
- $newest->newgraded = $state->id;
- $newest->sumpenalty = $state->sumpenalty;
- $newest->comment = $state->comment;
- update_record('question_sessions', $newest);
- }
+ } else {
+ set_field('question_sessions', 'newest', $state->id, 'attemptid',
+ $state->attempt, 'questionid', $question->id);
+ }
+ if (question_state_is_graded($state)) {
+ // this is also the most recent graded state
+ if ($newest = get_record('question_sessions', 'attemptid',
+ $state->attempt, 'questionid', $question->id)) {
+ $newest->newgraded = $state->id;
+ $newest->sumpenalty = $state->sumpenalty;
+ $newest->comment = $state->comment;
+ update_record('question_sessions', $newest);
}
}
* @param object $state
*/
function question_state_is_closed($state) {
- return ($state->event == QUESTION_EVENTCLOSE or $state->event == QUESTION_EVENTCLOSEANDGRADE);
+ return ($state->event == QUESTION_EVENTCLOSE
+ or $state->event == QUESTION_EVENTCLOSEANDGRADE
+ or $state->event == QUESTION_EVENTMANUALGRADE);
}
}
return $img;
}
+
+function question_print_comment_box($question, $state, $attempt, $url) {
+ global $CFG;
+ if ($usehtmleditor = can_use_richtext_editor()) {
+ use_html_editor('comment');
+ }
+ $grade = round($state->last_graded->grade, 3);
+ echo '<form method="post" action="'.$url.'">';
+ include($CFG->dirroot.'/question/comment.html');
+ echo '<input type="hidden" name="attempt" value="'.$attempt->uniqueid.'" />';
+ echo '<input type="hidden" name="question" value="'.$question->id.'" />';
+ echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+ echo '</form>';
+}
+
+function question_process_comment($question, &$state, &$attempt, $comment, $grade) {
+
+ // Update the comment and save it in the database
+ $state->comment = $comment;
+ if (!set_field('question_sessions', 'comment', $comment, 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) {
+ error("Cannot save comment");
+ }
+
+ // If the teacher has changed the grade then update the attempt and the state
+ // The modified attempt is stored to the database, the state not yet but the
+ // $state->changed flag is set
+ if (abs($state->last_graded->grade - $grade) > 0.002) {
+ // the teacher has changed the grade
+ $attempt->sumgrades = $attempt->sumgrades - $state->last_graded->grade + $grade;
+ $attempt->timemodified = time();
+ if (!update_record('quiz_attempts', $attempt)) {
+ error('Failed to save the current quiz attempt!');
+ }
+
+ $state->raw_grade = $grade;
+ $state->grade = $grade;
+ $state->penalty = 0;
+ $state->timestamp = time();
+ // We need to indicate that the state has changed in order for it to be saved
+ $state->changed = 1;
+ // We want to update existing state (rather than creating new one) if it
+ // was itself created by a manual grading event
+ $state->update = ($state->event == QUESTION_EVENTMANUALGRADE) ? 1 : 0;
+ $state->event = QUESTION_EVENTMANUALGRADE;
+
+ // Update the last graded state (don't simplify!)
+ unset($state->last_graded);
+ $state->last_graded = clone($state);
+ unset($state->last_graded->changed);
+ }
+
+}
+
/**
* Construct name prefixes for question form element names
*
--- /dev/null
+<?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');
+
+ $attemptid =required_param('attempt', PARAM_INT); // attempt id
+ $questionid =required_param('question', PARAM_INT); // question id
+
+ if (! $attempt = get_record('quiz_attempts', 'uniqueid', $attemptid)) {
+ 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');
+ }
+
+ // Teachers are only allowed to comment and grade on closed attempts
+ if (!($attempt->timefinish > 0)) {
+ error('Attempt has not closed yet');
+ }
+
+ require_login($course->id);
+
+ if (!isteacher($course->id)) {
+ error('This page is for teachers only');
+ }
+
+ // Load question
+ if (! $question = get_record('question', 'id', $questionid)) {
+ error('Question for this session is missing');
+ }
+ $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id);
+ // Some of the questions code is optimised to work with several questions
+ // at once so it wants the question to be in an array.
+ $key = $question->id;
+ $questions[$key] = &$question;
+ // Add additional questiontype specific information to the question objects.
+ if (!get_question_options($questions)) {
+ error("Unable to load questiontype specific question information");
+ }
+
+ // Load state
+ $states = get_question_states($questions, $quiz, $attempt);
+ // The $states array is indexed by question id but because we are dealing
+ // with only one question there is only one entry in this array
+ $state = &$states[$question->id];
+
+ print_header();
+ print_heading(format_string($question->name));
+
+ //add_to_log($course->id, 'quiz', 'review', "review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", "$cm->id");
+
+ if ($data = data_submitted() and confirm_sesskey()) {
+ // the following will update the state and attempt
+ question_process_comment($question, $state, $attempt, $data->comment, $data->grade);
+ // If the state has changed save it and update the quiz grade
+ if ($state->changed) {
+ save_question_session($question, $state);
+ quiz_save_best_grade($quiz);
+ }
+
+ notify(print_string('changessaved'));
+ echo '<input type="button" onclick="window.close()" value="' .
+ get_string('closewindow') . "\" />";
+
+ print_footer();
+ exit;
+ }
+
+ question_print_comment_box($question, $state, $attempt, $CFG->wwwroot.'/mod/quiz/comment.php');
+
+ print_footer();
+
+?>
/**
* Determine review options
*/
-function quiz_get_reviewoptions($cmoptions, $attempt, $isteacher=false) {
+function quiz_get_reviewoptions($quiz, $attempt, $isteacher=false) {
$options->readonly = true;
+ // Provide the links to the question review and comment script
+ $options->questionreviewlink = '/mod/quiz/reviewquestion.php';
+
if ($isteacher and !$attempt->preview) {
// The teacher should be shown everything except during preview when the teachers
// wants to see just what the students see
$options->feedback = true;
$options->correct_responses = true;
$options->solutions = false;
+ // Show a link to the comment box only for closed attempts
+ if ($attempt->timefinish) {
+ $options->questioncommentlink = '/mod/quiz/comment.php';
+ }
return $options;
}
if ((time() - $attempt->timefinish) < 120) {
- $options->responses = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
- $options->scores = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SCORES) ? 1 : 0;
- $options->feedback = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
- $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
- $options->solutions = ($cmoptions->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
- } else if (!$cmoptions->timeclose or time() < $cmoptions->timeclose) {
- $options->responses = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
- $options->scores = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SCORES) ? 1 : 0;
- $options->feedback = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
- $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
- $options->solutions = ($cmoptions->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
+ $options->responses = ($quiz->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
+ $options->scores = ($quiz->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SCORES) ? 1 : 0;
+ $options->feedback = ($quiz->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
+ $options->correct_responses = ($quiz->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
+ $options->solutions = ($quiz->review & QUIZ_REVIEW_IMMEDIATELY & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
+ } else if (!$quiz->timeclose or time() < $quiz->timeclose) {
+ $options->responses = ($quiz->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
+ $options->scores = ($quiz->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SCORES) ? 1 : 0;
+ $options->feedback = ($quiz->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
+ $options->correct_responses = ($quiz->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
+ $options->solutions = ($quiz->review & QUIZ_REVIEW_OPEN & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
} else {
- $options->responses = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
- $options->scores = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SCORES) ? 1 : 0;
- $options->feedback = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
- $options->correct_responses = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
- $options->solutions = ($cmoptions->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
+ $options->responses = ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
+ $options->scores = ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SCORES) ? 1 : 0;
+ $options->feedback = ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
+ $options->correct_responses = ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_ANSWERS) ? 1 : 0;
+ $options->solutions = ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_SOLUTIONS) ? 1 : 0;
}
+
return $options;
}
// with only one question there is only one entry in this array
$state = &$states[$question->id];
- // this is the new response from the teacher
- $state->responses = $response;
-
- // Process the teacher responses
- $QTYPES[$question->qtype]->process_teacher_responses($question, $state, $quiz);
-
- // We need to indicate that the state has changed in order for it to be saved
- $state->changed = 1;
- // We want to update existing state (rather than creating new one) if it
- // was itself created by a manual grading event
- $state->update = ($state->event == QUESTION_EVENTMANUALGRADE) ? 1 : 0;
- // Go ahead and save
- save_question_session($question, $state);
-
- if ($attempt->sumgrades != $sumgrades) {
- set_field('quiz_attempts', 'sumgrades', $sumgrades, 'id', $attempt->id);
- }
+ // the following will update the state and attempt
+ question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']);
- // update user's grade
- quiz_save_best_grade($quiz, $attempt->userid);
+ // If the state has changed save it and update the quiz grade
+ if ($state->changed) {
+ save_question_session($question, $state);
+ quiz_save_best_grade($quiz);
+ }
}
notify(get_string('changessaved', 'quiz'));
$options = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
$options->validation = QUESTION_EVENTVALIDATE === $states[$i]->event;
$options->history = ($isteacher and !$attempt->preview) ? 'all' : 'graded';
- // Provide the links to the question review script
- $options->questionreviewlink = '/mod/quiz/reviewquestion.php';
// Print the question
if ($i > 0) {
echo "<br />\n";
/// Print heading
print_heading(format_string($question->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';
- $QTYPES[$question->qtype]->get_question_options($question);
+ $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id);
+ // Some of the questions code is optimised to work with several questions
+ // at once so it wants the question to be in an array.
+ $key = $question->id;
+ $questions[$key] = &$question;
+ // Add additional questiontype specific information to the question objects.
+ if (!get_question_options($questions)) {
+ error("Unable to load questiontype specific question information");
+ }
+ $session = get_record('question_sessions', 'attemptid', $attempt->uniqueid, 'questionid', $question->id);
+ $state->sumpenalty = $session->sumpenalty;
+ $state->comment = $session->comment;
restore_question_state($question, $state);
$state->last_graded = $state;
$options = quiz_get_reviewoptions($quiz, $attempt, $isteacher);
$options->validation = ($state->event == QUESTION_EVENTVALIDATE);
$options->history = ($isteacher and !$attempt->preview) ? 'all' : 'graded';
- // Provide the links to this question review script
- $options->questionreviewlink = '/mod/quiz/reviewquestion.php';
/// Print infobox
$table->align = array("right", "left");
--- /dev/null
+<?php
+/* This template determines the overall layout of a question. It is included by the
+ * print_question() method.
+ */
+?>
+<table class="que comment">
+ <tr valign="top">
+ <td>
+ <b><?php print_string('comment', 'quiz'); ?>: </b>
+ </td>
+ <td>
+ <?php
+ print_textarea($usehtmleditor, 15, 60, 630, 300, 'comment', $state->comment);
+ ?>
+ </td>
+ </tr>
+ <tr valign="top">
+ <td>
+ <b><?php print_string('grade', 'quiz'); ?>: </b>
+ </td>
+ <td>
+ <input type="text" name="grade" size="2" value="<?php echo $grade; ?>" />/<?php echo $question->maxgrade; ?>
+ </td>
+ </tr>
+</table>
+<input type="submit" name="submit" value="<?php print_string('save', 'quiz'); ?>" />
\ No newline at end of file
<span class="no"><?php echo $number; ?></span>
<?php if ($editlink) { ?>
<span class="edit"><?php echo $editlink; ?></span>
- <?php } ?>
- <?php if ($grade) { ?>
+ <?php }
+ if ($grade) { ?>
<div class="grade">
<?php echo get_string('marks', 'quiz').': '.$grade; ?>
</div>
<div class="grading">
<?php $this->print_question_grading_details($question, $state, $cmoptions, $options); ?>
</div>
- <?php if ($history) { ?>
+ <?php if ($comment) { ?>
+ <div class="comment">
+ <?php
+ echo get_string('comment', 'quiz').': ';
+ echo $comment;
+ ?>
+ </div>
+ <?php }
+ echo $commentlink;
+ if ($history) { ?>
<div class="history">
<?php
print_string('history', 'quiz');
}
$grade .= $question->maxgrade;
}
+
+ $comment = $state->comment;
+ $commentlink = '';
+ if (isset($options->questioncommentlink)) {
+ $strcomment = get_string('commentorgrade', 'quiz');
+ $commentlink = '<div class="commentlink">'.link_to_popup_window ($options->questioncommentlink.'?attempt='.$state->attempt.'&question='.$question->id,
+ 'commentquestion', $strcomment, 450, 650, $strcomment, 'none', true).'</div>';
+ }
$history = $this->history($question, $state, $number, $cmoptions, $options);
get_string('response', 'quiz'),
get_string('time'),
get_string('score', 'quiz'),
- get_string('penalty', 'quiz'),
+ //get_string('penalty', 'quiz'),
get_string('grade', 'quiz'),
);
- $table->align = array ('center', 'center', 'left', 'left', 'left', 'left', 'left');
- $table->size = array ('', '', '', '', '', '', '');
$table->width = '100%';
foreach ($states as $st) {
$st->responses[''] = $st->answer;
$b.$this->response_summary($question, $st).$be,
$b.userdate($st->timestamp, get_string('timestr', 'quiz')).$be,
$b.round($st->raw_grade, $cmoptions->decimalpoints).$be,
- $b.round($st->penalty, $cmoptions->decimalpoints).$be,
+ //$b.round($st->penalty, $cmoptions->decimalpoints).$be,
$b.round($st->grade, $cmoptions->decimalpoints).$be
);
}
padding: 0 0 0.3em 0.3em;
border: 1px solid;
}
-.que .grading,
+.que .grading,
+.que .comment,
+.que .commentlink,
.que .history {
float: right;
margin: 5px;