Manual grading

This commit is contained in:
gustav_delius 2006-04-07 16:00:29 +00:00
parent 08777eaf25
commit b6e907a245
11 changed files with 264 additions and 78 deletions

View File

@ -82,6 +82,8 @@ $string['choosefile'] = 'Choose a file';
$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?';
@ -159,9 +161,10 @@ $string['event1'] = 'Autosave';
$string['event2'] = 'Save';
$string['event3'] = 'Grade';
$string['event5'] = 'Validate';
$string['event6'] = 'Close';
$string['event6'] = 'Close&amp;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';

View File

@ -685,8 +685,7 @@ function save_question_session(&$question, &$state) {
$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)) {
@ -694,30 +693,30 @@ function save_question_session(&$question, &$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);
}
}
@ -752,7 +751,9 @@ function question_state_is_graded($state) {
* @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);
}
@ -1166,6 +1167,59 @@ function get_question_image($question, $courseid) {
}
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
*

87
mod/quiz/comment.php Normal file
View File

@ -0,0 +1,87 @@
<?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&amp;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();
?>

View File

@ -509,8 +509,11 @@ function quiz_get_renderoptions($reviewoptions, $state) {
/**
* 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
@ -519,27 +522,32 @@ function quiz_get_reviewoptions($cmoptions, $attempt, $isteacher=false) {
$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;
}

View File

@ -113,26 +113,14 @@ class quiz_report extends quiz_default_report {
// 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;
// the following will update the state and attempt
question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']);
// 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);
// 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);
}
// update user's grade
quiz_save_best_grade($quiz, $attempt->userid);
}
notify(get_string('changessaved', 'quiz'));

View File

@ -235,8 +235,6 @@
$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";

View File

@ -96,20 +96,25 @@
/// 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");

26
question/comment.html Normal file
View File

@ -0,0 +1,26 @@
<?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'); ?>" />

View File

@ -8,8 +8,8 @@
<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>
@ -21,7 +21,16 @@
<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');

View File

@ -473,6 +473,14 @@ class default_questiontype {
}
$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.'&amp;question='.$question->id,
'commentquestion', $strcomment, 450, 650, $strcomment, 'none', true).'</div>';
}
$history = $this->history($question, $state, $number, $cmoptions, $options);
@ -503,11 +511,9 @@ class default_questiontype {
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;
@ -530,7 +536,7 @@ class default_questiontype {
$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
);
}

View File

@ -1177,7 +1177,9 @@ body#message-messages {
padding: 0 0 0.3em 0.3em;
border: 1px solid;
}
.que .grading,
.que .grading,
.que .comment,
.que .commentlink,
.que .history {
float: right;
margin: 5px;