if (!$quizobj->is_preview_user() && $messages) {
    print_error('attempterror', 'quiz', $quizobj->view_url(), $accessmanager->print_messages($messages, true));
}
$accessmanager->do_password_check($quizobj->is_preview_user());
/// Delete any previous preview attempts belonging to this user.
quiz_delete_previews($quiz, $USER->id);
/// Create the new attempt and initialize the question sessions
$attempt = quiz_create_attempt($quiz, $attemptnumber, $lastattempt, time(), $quizobj->is_preview_user());
/// Save the attempt in the database.
if (!($attempt->id = $DB->insert_record('quiz_attempts', $attempt))) {
    quiz_error($quiz, 'newattemptfail');
}
/// Log the new attempt.
if ($attempt->preview) {
    add_to_log($course->id, 'quiz', 'preview', 'view.php?id=' . $quizobj->get_cmid(), $quizobj->get_quizid(), $quizobj->get_cmid());
} else {
    add_to_log($course->id, 'quiz', 'attempt', 'review.php?attempt=' . $attempt->id, $quizobj->get_quizid(), $quizobj->get_cmid());
}
/// Fully load all the questions in this quiz.
$quizobj->preload_questions();
$quizobj->load_questions();
/// Create initial states for all questions in this quiz.
if (!($states = get_question_states($quizobj->get_questions(), $quizobj->get_quiz(), $attempt, $lastattemptid))) {
    print_error('cannotrestore', 'quiz');
}
/// Save all the newly created states.
foreach ($quizobj->get_questions() as $i => $question) {
    save_question_session($question, $states[$i]);
}
/// Redirect to the attempt page.
redirect($quizobj->attempt_url($attempt->id));
/**
* For a given question in an attempt we walk the complete history of states
* and recalculate the grades as we go along.
*
* This is used when a question is changed and old student
* responses need to be marked with the new version of a question.
*
* TODO: Make sure this is not quiz-specific
*
* @return boolean            Indicates whether the grade has changed
* @param object  $question   A question object
* @param object  $attempt    The attempt, in which the question needs to be regraded.
* @param object  $cmoptions
* @param boolean $verbose    Optional. Whether to print progress information or not.
* @param boolean $dryrun     Optional. Whether to make changes to grades records
* or record that changes need to be made for a later regrade.
*/
function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose = false, $dryrun = false)
{
    global $DB;
    // load all states for this question in this attempt, ordered in sequence
    if ($states = $DB->get_records('question_states', array('attempt' => $attempt->uniqueid, 'question' => $question->id), 'seq_number ASC')) {
        $states = array_values($states);
        // Subtract the grade for the latest state from $attempt->sumgrades to get the
        // sumgrades for the attempt without this question.
        $attempt->sumgrades -= $states[count($states) - 1]->grade;
        // Initialise the replaystate
        $replaystate = question_load_specific_state($question, $cmoptions, $attempt, $states[0]->id);
        $replaystate->sumpenalty = 0;
        $replaystate->last_graded->sumpenalty = 0;
        $changed = false;
        for ($j = 1; $j < count($states); $j++) {
            restore_question_state($question, $states[$j]);
            $action = new stdClass();
            $action->responses = $states[$j]->responses;
            $action->timestamp = $states[$j]->timestamp;
            // Change event to submit so that it will be reprocessed
            if (in_array($states[$j]->event, array(QUESTION_EVENTCLOSE, QUESTION_EVENTGRADE, QUESTION_EVENTCLOSEANDGRADE))) {
                $action->event = QUESTION_EVENTSUBMIT;
                // By default take the event that was saved in the database
            } else {
                $action->event = $states[$j]->event;
            }
            if ($action->event == QUESTION_EVENTMANUALGRADE) {
                // Ensure that the grade is in range - in the past this was not checked,
                // but now it is (MDL-14835) - so we need to ensure the data is valid before
                // proceeding.
                if ($states[$j]->grade < 0) {
                    $states[$j]->grade = 0;
                    $changed = true;
                } else {
                    if ($states[$j]->grade > $question->maxgrade) {
                        $states[$j]->grade = $question->maxgrade;
                        $changed = true;
                    }
                }
                if (!$dryrun) {
                    $error = question_process_comment($question, $replaystate, $attempt, $replaystate->manualcomment, $states[$j]->grade);
                    if (is_string($error)) {
                        notify($error);
                    }
                } else {
                    $replaystate->grade = $states[$j]->grade;
                }
            } else {
                // Reprocess (regrade) responses
                if (!question_process_responses($question, $replaystate, $action, $cmoptions, $attempt) && $verbose) {
                    $a = new stdClass();
                    $a->qid = $question->id;
                    $a->stateid = $states[$j]->id;
                    notify(get_string('errorduringregrade', 'question', $a));
                }
                // We need rounding here because grades in the DB get truncated
                // e.g. 0.33333 != 0.3333333, but we want them to be equal here
                if (round((double) $replaystate->raw_grade, 5) != round((double) $states[$j]->raw_grade, 5) or round((double) $replaystate->penalty, 5) != round((double) $states[$j]->penalty, 5) or round((double) $replaystate->grade, 5) != round((double) $states[$j]->grade, 5)) {
                    $changed = true;
                }
                // If this was previously a closed state, and it has been knoced back to
                // graded, then fix up the state again.
                if ($replaystate->event == QUESTION_EVENTGRADE && ($states[$j]->event == QUESTION_EVENTCLOSE || $states[$j]->event == QUESTION_EVENTCLOSEANDGRADE)) {
                    $replaystate->event = $states[$j]->event;
                }
            }
            $replaystate->id = $states[$j]->id;
            $replaystate->changed = true;
            $replaystate->update = true;
            // This will ensure that the existing database entry is updated rather than a new one created
            if (!$dryrun) {
                save_question_session($question, $replaystate);
            }
        }
        if ($changed) {
            if (!$dryrun) {
                // TODO, call a method in quiz to do this, where 'quiz' comes from
                // the question_attempts table.
                $DB->update_record('quiz_attempts', $attempt);
            }
        }
        if ($changed) {
            $toinsert = new object();
            $toinsert->oldgrade = round((double) $states[count($states) - 1]->grade, 5);
            $toinsert->newgrade = round((double) $replaystate->grade, 5);
            $toinsert->attemptid = $attempt->uniqueid;
            $toinsert->questionid = $question->id;
            //the grade saved is the old grade if the new grade is saved
            //it is the new grade if this is a dry run.
            $toinsert->regraded = $dryrun ? 0 : 1;
            $toinsert->timemodified = time();
            $DB->insert_record('quiz_question_regrade', $toinsert);
            return true;
        } else {
            return false;
        }
    }
    return false;
}
Example #3
0
/**
* For a given question in an attempt we walk the complete history of states
* and recalculate the grades as we go along.
*
* This is used when a question is changed and old student
* responses need to be marked with the new version of a question.
*
* TODO: Make sure this is not quiz-specific
*
* @return boolean            Indicates whether the grade has changed
* @param object  $question   A question object
* @param object  $attempt    The attempt, in which the question needs to be regraded.
* @param object  $cmoptions
* @param boolean $verbose    Optional. Whether to print progress information or not.
*/
function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose = false)
{
    // load all states for this question in this attempt, ordered in sequence
    if ($states = get_records_select('question_states', "attempt = '{$attempt->uniqueid}' AND question = '{$question->id}'", 'seq_number ASC')) {
        $states = array_values($states);
        // Subtract the grade for the latest state from $attempt->sumgrades to get the
        // sumgrades for the attempt without this question.
        $attempt->sumgrades -= $states[count($states) - 1]->grade;
        // Initialise the replaystate
        $state = clone $states[0];
        $state->manualcomment = get_field('question_sessions', 'manualcomment', 'attemptid', $attempt->uniqueid, 'questionid', $question->id);
        restore_question_state($question, $state);
        $state->sumpenalty = 0.0;
        $replaystate = clone $state;
        $replaystate->last_graded = $state;
        $changed = false;
        for ($j = 1; $j < count($states); $j++) {
            restore_question_state($question, $states[$j]);
            $action = new stdClass();
            $action->responses = $states[$j]->responses;
            $action->timestamp = $states[$j]->timestamp;
            // Change event to submit so that it will be reprocessed
            if (QUESTION_EVENTCLOSE == $states[$j]->event or QUESTION_EVENTGRADE == $states[$j]->event or QUESTION_EVENTCLOSEANDGRADE == $states[$j]->event) {
                $action->event = QUESTION_EVENTSUBMIT;
                // By default take the event that was saved in the database
            } else {
                $action->event = $states[$j]->event;
            }
            if ($action->event == QUESTION_EVENTMANUALGRADE) {
                // Ensure that the grade is in range - in the past this was not checked,
                // but now it is (MDL-14835) - so we need to ensure the data is valid before
                // proceeding.
                if ($states[$j]->grade < 0) {
                    $states[$j]->grade = 0;
                } else {
                    if ($states[$j]->grade > $question->maxgrade) {
                        $states[$j]->grade = $question->maxgrade;
                    }
                }
                $error = question_process_comment($question, $replaystate, $attempt, $replaystate->manualcomment, $states[$j]->grade);
                if (is_string($error)) {
                    notify($error);
                }
            } else {
                // Reprocess (regrade) responses
                if (!question_process_responses($question, $replaystate, $action, $cmoptions, $attempt)) {
                    $verbose && notify("Couldn't regrade state #{$state->id}!");
                }
            }
            // We need rounding here because grades in the DB get truncated
            // e.g. 0.33333 != 0.3333333, but we want them to be equal here
            if (round((double) $replaystate->raw_grade, 5) != round((double) $states[$j]->raw_grade, 5) or round((double) $replaystate->penalty, 5) != round((double) $states[$j]->penalty, 5) or round((double) $replaystate->grade, 5) != round((double) $states[$j]->grade, 5)) {
                $changed = true;
            }
            $replaystate->id = $states[$j]->id;
            $replaystate->changed = true;
            $replaystate->update = true;
            // This will ensure that the existing database entry is updated rather than a new one created
            save_question_session($question, $replaystate);
        }
        if ($changed) {
            // TODO, call a method in quiz to do this, where 'quiz' comes from
            // the question_attempts table.
            update_record('quiz_attempts', $attempt);
        }
        return $changed;
    }
    return false;
}
Example #4
0
// 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->response['comment'], $data->response['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, $attempt->userid);
    }
    notify(get_string('changessaved'));
    echo '<div class="boxaligncenter"><input type="button" onclick="window.opener.location.reload(1); self.close();return false;" value="' . get_string('closewindow') . "\" /></div>";
    print_footer();
    exit;
}
question_print_comment_box($question, $state, $attempt, $CFG->wwwroot . '/mod/quiz/comment.php');
print_footer();
Example #5
0
 /**
  * Displays the report.
  */
 function display($quiz, $cm, $course)
 {
     global $CFG, $QTYPES;
     $viewoptions = array('mode' => 'grading', 'q' => $quiz->id);
     if ($questionid = optional_param('questionid', 0, PARAM_INT)) {
         $viewoptions += array('questionid' => $questionid);
     }
     // grade question specific parameters
     $gradeungraded = optional_param('gradeungraded', 0, PARAM_INT);
     if ($userid = optional_param('userid', 0, PARAM_INT)) {
         $viewoptions += array('userid' => $userid);
     }
     if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) {
         $viewoptions += array('attemptid' => $attemptid);
     }
     if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) {
         $viewoptions += array('gradeall' => $gradeall);
     }
     if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) {
         $viewoptions += array('gradeungraded' => $gradeungraded);
     }
     if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) {
         $viewoptions += array('gradenextungraded' => $gradenextungraded);
     }
     $this->cm = $cm;
     $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading");
     // Check permissions
     $this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
     if (!has_capability('mod/quiz:grade', $this->context)) {
         notify(get_string('gradingnotallowed', 'quiz_grading'));
         return true;
     }
     $gradeableqs = quiz_report_load_questions($quiz);
     $questionsinuse = implode(',', array_keys($gradeableqs));
     foreach ($gradeableqs as $qid => $question) {
         if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) {
             unset($gradeableqs[$qid]);
         }
     }
     if (empty($gradeableqs)) {
         print_heading(get_string('noessayquestionsfound', 'quiz'));
         return true;
     } else {
         if (count($gradeableqs) == 1) {
             $questionid = array_shift(array_keys($gradeableqs));
         }
     }
     $currentgroup = groups_get_activity_group($this->cm, true);
     $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false);
     $this->userids = implode(',', array_keys($this->users));
     if (!empty($questionid)) {
         if (!isset($gradeableqs[$questionid])) {
             error("Gradeable question with id {$questionid} not found");
         } else {
             $question =& $gradeableqs[$questionid];
         }
         $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. The array key
         // must be the question id.
         $key = $question->id;
         $questions[$key] =& $question;
         // We need to add additional questiontype specific information to
         // the question objects.
         if (!get_question_options($questions)) {
             error("Unable to load questiontype specific question information");
         }
         // This will have extended the question object so that it now holds
         // all the information about the questions that may be needed later.
     }
     add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&amp;q={$quiz->id}", "{$quiz->id}", "{$cm->id}");
     echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>';
     // for overlib
     if ($data = data_submitted()) {
         // post data submitted, process it
         require_sesskey();
         // now go through all of the responses and save them.
         $allok = true;
         foreach ($data->manualgrades as $uniqueid => $response) {
             // get our attempt
             $uniqueid = clean_param($uniqueid, PARAM_INT);
             if (!($attempt = get_record_sql("SELECT * FROM {$CFG->prefix}quiz_attempts " . "WHERE uniqueid = {$uniqueid} AND " . "userid IN ({$this->userids}) AND " . "quiz=" . $quiz->id))) {
                 error('No such attempt ID exists');
             }
             // Load the state for this attempt (The questions array was created earlier)
             $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];
             // the following will update the state and attempt
             $error = question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']);
             if (is_string($error)) {
                 notify($error);
                 $allok = false;
             } else {
                 if ($state->changed) {
                     // If the state has changed save it and update the quiz grade
                     save_question_session($question, $state);
                     quiz_save_best_grade($quiz, $attempt->userid);
                 }
             }
         }
         if ($allok) {
             notify(get_string('changessaved', 'quiz'), 'notifysuccess');
         } else {
             notify(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess');
         }
     }
     $this->viewurl = new moodle_url($CFG->wwwroot . '/mod/quiz/report.php', $viewoptions);
     /// find out current groups mode
     if ($groupmode = groups_get_activity_groupmode($this->cm)) {
         // Groups are being used
         groups_print_activity_menu($this->cm, $this->viewurl->out(false, array('userid' => 0, 'attemptid' => 0)));
     }
     echo '<div class="quizattemptcounts">' . quiz_num_attempt_summary($quiz, $cm, true, $currentgroup) . '</div>';
     if (empty($this->users)) {
         if ($currentgroup) {
             notify(get_string('nostudentsingroup'));
         } else {
             notify(get_string('nostudentsyet'));
         }
         return true;
     }
     $gradeablequestionids = implode(',', array_keys($gradeableqs));
     $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, $gradeablequestionids, $this->userids);
     if (empty($qattempts)) {
         notify(get_string('noattemptstoshow', 'quiz'));
         return true;
     }
     $qmenu = array();
     foreach ($gradeableqs as $qid => $questionformenu) {
         $a = new object();
         $a->number = $gradeableqs[$qid]->number;
         $a->name = $gradeableqs[$qid]->name;
         $a->gradedattempts = $qattempts[$qid]->gradedattempts;
         $a->totalattempts = $qattempts[$qid]->totalattempts;
         $a->openspan = '';
         $a->closespan = '';
         $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a);
     }
     if (count($gradeableqs) != 1) {
         $qurl = fullclone($this->viewurl);
         $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded');
         $menu = popup_form($qurl->out() . '&amp;questionid=', $qmenu, 'questionid', $questionid, 'choose', '', '', true);
         echo '<div class="mdl-align">' . $menu . '</div>';
     }
     if (!$questionid) {
         return true;
     }
     $a = new object();
     $a->number = $question->number;
     $a->name = $question->name;
     $a->gradedattempts = $qattempts[$question->id]->gradedattempts;
     $a->totalattempts = $qattempts[$question->id]->totalattempts;
     $a->openspan = '<span class="highlightgraded">';
     $a->closespan = '</span>';
     print_heading(get_string('questiontitle', 'quiz_grading', $a));
     // our 3 different views
     // the first one displays all of the manually graded questions in the quiz
     // with the number of ungraded attempts for each question
     // the second view displays the users who have answered the essay question
     // and all of their attempts at answering the question
     // the third prints the question with a comment
     // and grade form underneath it
     $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts;
     if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) {
         $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded);
     } else {
         $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded);
     }
     return true;
 }
Example #6
0
/// Now load the state of every question, reloading the ones we messed around
/// with above.
$attemptobj->preload_question_states();
$attemptobj->load_question_states();
/// Move each question to the closed state.
$success = true;
$attempt = $attemptobj->get_attempt();
foreach ($attemptobj->get_questions() as $id => $question) {
    $state = $attemptobj->get_question_state($id);
    $action = new stdClass();
    $action->event = QUESTION_EVENTCLOSE;
    $action->responses = $state->responses;
    $action->responses['_flagged'] = $state->flagged;
    $action->timestamp = $state->timestamp;
    if (question_process_responses($attemptobj->get_question($id), $state, $action, $attemptobj->get_quiz(), $attempt)) {
        save_question_session($attemptobj->get_question($id), $state);
    } else {
        $success = false;
    }
}
if (!$success) {
    print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
}
/// Log the end of this attempt.
add_to_log($attemptobj->get_courseid(), 'quiz', 'close attempt', 'review.php?attempt=' . $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
/// Update the quiz attempt record.
$attempt->timemodified = $timenow;
$attempt->timefinish = $timenow;
$DB->update_record('quiz_attempts', $attempt);
if (!$attempt->preview) {
    /// Record this user's best grade (if this is not a preview).
Example #7
0
 /**
  * Displays the report.
  */
 function display($quiz, $cm, $course)
 {
     global $CFG, $SESSION, $USER, $db, $QTYPES;
     $action = optional_param('action', 'viewquestions', PARAM_ALPHA);
     $questionid = optional_param('questionid', 0, PARAM_INT);
     $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading");
     // Check permissions
     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
     if (!has_capability('mod/quiz:grade', $context)) {
         notify(get_string('gradingnotallowed', 'quiz_grading'));
         return true;
     }
     if (!empty($questionid)) {
         if (!($question = get_record('question', 'id', $questionid))) {
             error("Question with id {$questionid} not found");
         }
         $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. The array key
         // must be the question id.
         $key = $question->id;
         $questions[$key] =& $question;
         // We need to add additional questiontype specific information to
         // the question objects.
         if (!get_question_options($questions)) {
             error("Unable to load questiontype specific question information");
         }
         // This will have extended the question object so that it now holds
         // all the information about the questions that may be needed later.
     }
     add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&amp;q={$quiz->id}", "{$quiz->id}", "{$cm->id}");
     echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>';
     // for overlib
     if ($data = data_submitted()) {
         // post data submitted, process it
         confirm_sesskey();
         // now go through all of the responses and save them.
         foreach ($data->manualgrades as $uniqueid => $response) {
             // get our attempt
             if (!($attempt = get_record('quiz_attempts', 'uniqueid', $uniqueid))) {
                 error('No such attempt ID exists');
             }
             // Load the state for this attempt (The questions array was created earlier)
             $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];
             // the following will update the state and attempt
             question_process_comment($question, $state, $attempt, $response['comment'], $response['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, $attempt->userid);
             }
         }
         notify(get_string('changessaved', 'quiz'), 'notifysuccess');
     }
     // our 3 different views
     // the first one displays all of the manually graded questions in the quiz
     // with the number of ungraded attempts for each question
     // the second view displays the users who have answered the essay question
     // and all of their attempts at answering the question
     // the third prints the question with a comment
     // and grade form underneath it
     switch ($action) {
         case 'viewquestions':
             $this->view_questions($quiz);
             break;
         case 'viewquestion':
             $this->view_question($quiz, $question);
             break;
         case 'grade':
             $this->print_questions_and_form($quiz, $question);
             break;
     }
     return true;
 }
Example #8
0
 /**
  * Process a manual comment for a question in this attempt.
  * @param $questionid
  * @param integer $questionid the question id
  * @param string $comment the new comment from the teacher.
  * @param mixed $grade the grade the teacher assigned, or '' to not change the grade.
  * @return mixed true on success, a string error message if a problem is detected
  *         (for example score out of range).
  */
 public function process_comment($questionid, $comment, $commentformat, $grade)
 {
     // I am not sure it is a good idea to have update methods here - this
     // class is only about getting data out of the question engine, and
     // helping to display it, apart from this.
     $this->ensure_question_loaded($questionid);
     $this->ensure_state_loaded($questionid);
     $state = $this->states[$questionid];
     $error = question_process_comment($this->questions[$questionid], $state, $this->attempt, $comment, $commentformat, $grade);
     // If the state was update (successfully), save the changes.
     if (!is_string($error) && $state->changed) {
         if (!save_question_session($this->questions[$questionid], $state)) {
             $error = get_string('errorudpatingquestionsession', 'quiz');
         }
         if (!quiz_save_best_grade($this->quiz, $this->attempt->userid)) {
             $error = get_string('errorudpatingbestgrade', 'quiz');
         }
     }
     return $error;
 }
 public function process_comment($questionid, $comment, $grade)
 {
     $this->ensure_question_loaded($questionid);
     $this->ensure_state_loaded($questionid);
     $state = $this->states[$questionid];
     $error = question_process_comment($this->questions[$questionid], $state, $this->attempt, $comment, $grade);
     // If the state was update (successfully), save the changes.
     if (!is_string($error) && $state->changed) {
         if (!save_question_session($this->questions[$questionid], $state)) {
             $error = get_string('errorudpatingquestionsession', 'quiz');
         }
         if (!quiz_save_best_grade($this->quiz, $this->attempt->userid)) {
             $error = get_string('errorudpatingbestgrade', 'quiz');
         }
     }
     return $error;
 }
function evaluate_quiz($acode, $jobid, $newattempt, $blended)
{
    global $USER;
    global $CFG;
    mtrace("Evaluation QUIZ Processing..." . "<BR><BR>");
    try {
        print "New Attempt is: " . $newattempt . "<BR/>";
        $detected_userid = find_userid($acode, $jobid);
        if ($detected_userid == null or $detected_userid == '') {
            throw new EvaluationError(get_string('ErrorUserIDEmpty', 'blended'), EvaluationError::USERID_IS_EMPTY);
        }
        $user_reg = blended_get_user($detected_userid, $blended);
        if ($user_reg == null) {
            throw new EvaluationError(get_string('ErrorUserNotInCourse', 'blended'), EvaluationError::USER_NOT_IN_THIS_COURSE);
        }
        $userid = $user_reg->id;
        mtrace('Obtained USERID value: ' . $userid . " OK. <BR/>");
        $quiz = get_quiz($acode);
        $attempts = quiz_get_user_attempts($quiz->id, $userid, 'all', true);
        mtrace("Searching quiz... Success." . "<BR/>");
        $uniqueid = get_uniqueid($acode);
        mtrace('Obtained uniqueid: OK. <BR/>');
        $timestamp = get_timestamp($acode);
        mtrace('Obtained timestamp: OK. <BR/>');
        if (!get_record('quiz_attempts', 'uniqueid', $uniqueid)) {
            $newattempt = true;
        } else {
            $newattempt = false;
            mtrace("User {$userid} had opened this attempt already.");
        }
        $attemptnumber = 1;
        if ($newattempt == false) {
            mtrace('Obtaining user attempt...<BR/>');
            set_attempt_unfinished($uniqueid);
            $attempt = quiz_get_user_attempt_unfinished($quiz->id, $userid);
        } elseif ($newattempt == true) {
            mtrace('Creating new attempt...<BR/>');
            $attempt = create_new_attempt($quiz, $attemptnumber, $userid, $acode, $uniqueid, $timestamp);
            // Save the attempt
            if (!insert_record('quiz_attempts', $attempt)) {
                throw new EvaluationError(get_string('ErrorCouldNotCreateAttempt', 'blended'), EvaluationError::CREATE_QUIZ_ATTEMPT_ERROR);
            }
            // Actualizamos el estado de las imágenes para indicar que ya está creado un nuevo attempt
            update_images_status($acode, $jobid);
        }
        update_question_attempts($uniqueid);
        // /*
        mtrace('<BR>Getting questions and question options... ');
        $questions = get_questions($attempt, $quiz);
        if (!get_question_options($questions)) {
            error('Could not load question options');
        }
        mtrace('Success! <BR>');
        //	print ("<BR>He obtenido questions: ");
        //print_object($questions);
        $lastattemptid = false;
        //	 if ($attempt->attempt > 1 and $quiz->attemptonlast and !$attempt->preview) {
        // Find the previous attempt
        //      if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) {
        //        error('Could not find previous attempt to build on');
        //  }
        //}
        //print ('He obtenido lastattemptid');
        mtrace('Getting question states... ');
        if (!($states = get_question_states($questions, $quiz, $attempt, $lastattemptid))) {
            error('Could not restore question sessions');
        }
        mtrace('Success! <BR>');
        mtrace('Getting responses... <BR>');
        $responses = get_responses($acode, $jobid, $attempt);
        //print('Estas son las responses:');
        //print_object($responses);
        //$timestamp=time();
        $event = 8;
        $actions = question_extract_responses($questions, $responses, $event);
        $questionids = get_questionids($acode);
        //	print $questionids;
        $questionidarray = explode(',', $questionids);
        $success = true;
        mtrace('<BR> Processing responses and saving session... ');
        foreach ($questionidarray as $i) {
            if (!isset($actions[$i])) {
                $actions[$i]->responses = array('' => '');
                $actions[$i]->event = QUESTION_EVENTOPEN;
            }
            $actions[$i]->timestamp = $timestamp;
            if (question_process_responses($questions[$i], $states[$i], $actions[$i], $quiz, $attempt)) {
                save_question_session($questions[$i], $states[$i]);
            } else {
                $success = false;
            }
        }
        mtrace('Success! <BR>');
        // Set the attempt to be finished
        $timestamp = time();
        //$attempt->timefinish = $timestamp;
        // Update the quiz attempt and the overall grade for the quiz
        mtrace('<BR> Finishing the attempt... ');
        // print_object ($attempt);
        if (set_field('quiz_attempts', 'timefinish', $timestamp, 'uniqueid', $uniqueid) == false) {
            throw new EvaluationError('Unable to finish the quiz attempt!', EvaluationError::FINISH_QUIZ_ATTEMPT_ERROR);
        }
        mtrace('Success! <BR>');
        if ($attempt->attempt > 1 || $attempt->timefinish > 0 and !$attempt->preview) {
            mtrace('<BR> Saving quiz grade... ');
            quiz_save_best_grade($quiz, $userid);
        }
        mtrace('Success! <BR>');
        // */
        mtrace("Process Done. <BR><BR>");
        mtrace("<center> Your quiz has been succesfully evaluated!! </center>");
    } catch (EvaluationError $e) {
        throw $e;
    }
    return;
}
Example #11
0
 /**
  * Displays the report.
  */
 function display($quiz, $cm, $course)
 {
     global $CFG, $QTYPES, $DB, $OUTPUT, $PAGE;
     $viewoptions = array('mode' => 'grading', 'q' => $quiz->id);
     if ($questionid = optional_param('questionid', 0, PARAM_INT)) {
         $viewoptions += array('questionid' => $questionid);
     }
     // grade question specific parameters
     if ($userid = optional_param('userid', 0, PARAM_INT)) {
         $viewoptions += array('userid' => $userid);
     }
     if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) {
         $viewoptions += array('attemptid' => $attemptid);
     }
     if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) {
         $viewoptions += array('gradeall' => $gradeall);
     }
     if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) {
         $viewoptions += array('gradeungraded' => $gradeungraded);
     }
     if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) {
         $viewoptions += array('gradenextungraded' => $gradenextungraded);
     }
     $this->cm = $cm;
     $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading");
     // Check permissions
     $this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
     if (!has_capability('mod/quiz:grade', $this->context)) {
         echo $OUTPUT->notification(get_string('gradingnotallowed', 'quiz_grading'));
         return true;
     }
     $gradeableqs = quiz_report_load_questions($quiz);
     $questionsinuse = implode(',', array_keys($gradeableqs));
     foreach ($gradeableqs as $qid => $question) {
         if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) {
             unset($gradeableqs[$qid]);
         }
     }
     if (empty($gradeableqs)) {
         echo $OUTPUT->heading(get_string('noessayquestionsfound', 'quiz'));
         return true;
     } else {
         if (count($gradeableqs) == 1) {
             $questionid = array_shift(array_keys($gradeableqs));
         }
     }
     $currentgroup = groups_get_activity_group($this->cm, true);
     $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false);
     if (!empty($questionid)) {
         if (!isset($gradeableqs[$questionid])) {
             print_error('invalidquestionid', 'quiz_grading', '', $questionid);
         } else {
             $question =& $gradeableqs[$questionid];
         }
         // Some of the questions code is optimised to work with several questions
         // at once so it wants the question to be in an array. The array key
         // must be the question id.
         $key = $question->id;
         $questions[$key] =& $question;
         // We need to add additional questiontype specific information to
         // the question objects.
         if (!get_question_options($questions)) {
             print_error('cannotloadquestioninfo', 'quiz_grading');
         }
         // This will have extended the question object so that it now holds
         // all the information about the questions that may be needed later.
     }
     add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&amp;q={$quiz->id}", "{$quiz->id}", "{$cm->id}");
     if ($data = data_submitted()) {
         // post data submitted, process it
         if (confirm_sesskey() && $this->users) {
             // now go through all of the responses and save them.
             $allok = true;
             foreach ($data->manualgrades as $uniqueid => $response) {
                 // get our attempt
                 $uniqueid = clean_param($uniqueid, PARAM_INT);
                 list($usql, $params) = $DB->get_in_or_equal(array_keys($this->users));
                 if (!($attempt = $DB->get_record_sql("SELECT * FROM {quiz_attempts} " . "WHERE uniqueid = ? AND " . "userid {$usql} AND " . "quiz=?", array_merge(array($uniqueid), $params, array($quiz->id))))) {
                     print_error('invalidattemptid', 'quiz_grading');
                 }
                 // Load the state for this attempt (The questions array was created earlier)
                 $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];
                 // the following will update the state and attempt
                 $error = question_process_comment($question, $state, $attempt, $response['comment'], FORMAT_HTML, $response['grade']);
                 if (is_string($error)) {
                     echo $OUTPUT->notification($error);
                     $allok = false;
                 } else {
                     if ($state->changed) {
                         // If the state has changed save it and update the quiz grade
                         save_question_session($question, $state);
                         quiz_save_best_grade($quiz, $attempt->userid);
                     }
                 }
             }
             if ($allok) {
                 echo $OUTPUT->notification(get_string('changessaved', 'quiz'), 'notifysuccess');
             } else {
                 echo $OUTPUT->notification(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess');
             }
         }
     }
     $this->viewurl = new moodle_url('/mod/quiz/report.php', $viewoptions);
     /// find out current groups mode
     if ($groupmode = groups_get_activity_groupmode($this->cm)) {
         // Groups are being used
         groups_print_activity_menu($this->cm, $this->viewurl->out(true, array('userid' => 0, 'attemptid' => 0)));
     }
     if (empty($this->users)) {
         if ($currentgroup) {
             echo $OUTPUT->notification(get_string('nostudentsingroup'));
         } else {
             echo $OUTPUT->notification(get_string('nostudentsyet'));
         }
         return true;
     }
     $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, array_keys($gradeableqs), array_keys($this->users));
     if (empty($qattempts)) {
         echo $OUTPUT->notification(get_string('noattemptstoshow', 'quiz'));
         return true;
     }
     $qmenu = array();
     foreach ($gradeableqs as $qid => $questionformenu) {
         $a = new stdClass();
         $a->number = $gradeableqs[$qid]->number;
         $a->name = $gradeableqs[$qid]->name;
         $a->gradedattempts = $qattempts[$qid]->gradedattempts;
         $a->totalattempts = $qattempts[$qid]->totalattempts;
         $a->openspan = '';
         $a->closespan = '';
         $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a);
     }
     if (count($gradeableqs) != 1) {
         $qurl = fullclone($this->viewurl);
         $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded');
         $menu = $OUTPUT->single_select($qurl, 'questionid', $qmenu, $questionid, array('' => 'choosedots'), 'questionid');
         echo '<div class="mdl-align">' . $menu . '</div>';
     }
     if (!$questionid) {
         return true;
     }
     $a = new stdClass();
     $a->number = $question->number;
     $a->name = $question->name;
     $a->gradedattempts = $qattempts[$question->id]->gradedattempts;
     $a->totalattempts = $qattempts[$question->id]->totalattempts;
     $a->openspan = '<span class="highlightgraded">';
     $a->closespan = '</span>';
     echo $OUTPUT->heading(get_string('questiontitle', 'quiz_grading', $a));
     // our 2 different views
     // the first view allows a user to select a question and
     // displays the users who have answered the essay question
     // and all of their attempts at answering the question
     // the second prints selected attempt answer(s) with a comment
     // and grade form underneath them
     $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts;
     if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) {
         $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded);
     } else {
         $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded);
     }
     return true;
 }
Example #12
0
/**
 * 
 * Creates a new PDF with the questions of a quiz's attempt.
 * 
 * @param unknown_type $attempt record with the attempt info
 * @param unknown_type $quiz
 * @param unknown_type $pdf1
 * @param unknown_type $blended
 * @param unknown_type $options array with formatting options
 * @param unknown_type $pdfFile
 * @throws PDFError
 */
function blended_generate_quiz($attempt, $quiz, $pdf1, $blended, $options, $pdfFile)
{
    global $QTYPES;
    global $CFG;
    global $COURSE;
    $uniqueid = $attempt->id;
    $activity_code = $uniqueid;
    $identifyLabel = $options['identifyLabel'];
    /*
     	switch($identifyLabel)
    	{
    		case 'id':
    			$idText= $activity_code;
    			break;
    		case 'none': $idText='';
    		break;
    	}
    */
    $markSize = 3;
    $quizname = $quiz->name;
    $images->ok_marks = '<img src="' . $CFG->wwwroot . '/mod/blended/images/ok_marks.png" height="10" />';
    $images->ko_marks = '<img src="' . $CFG->wwwroot . '/mod/blended/images/ko_marks.png" height="10" />';
    //	$images->ok_marks='<img src="mod/blended/images/ok_marks.png" height="10" />';
    //	$images->ko_marks='<img src="mod/blended/images/ko_marks.png"  height="10"/>';
    $howToMark = get_string('howToMarkInstructions', 'blended', $images);
    $instructions = $quiz->intro . ' ' . $howToMark;
    $fullname = "nombre persona";
    $style = array('position' => 'S', 'border' => false, 'padding' => 1, 'fgcolor' => array(0, 0, 0), 'bgcolor' => false, 'text' => true, 'font' => 'courier', 'fontsize' => $options['fontsize'], 'stretchtext' => 4);
    $headeroptions = new stdClass();
    $headeroptions->rowHeight = 6;
    $headeroptions->logoWidth = 30;
    $headeroptions->codebarWidth = 40;
    $headeroptions->textStyle = $style;
    if (isset($options['logourl'])) {
        $headeroptions->logo_url = $options['logourl'];
    } else {
        $headeroptions->logo_url = $CFG->dirroot . '/mod/blended/pix/UVa_logo.jpg';
    }
    $headeroptions->cellHtmlText = get_string('modulename', 'quiz') . ':' . $quizname;
    //Nombre:
    $headeroptions->cellHtmlDate = '';
    $headeroptions->cellHtmlUser = get_string('Student', 'blended') . ':';
    // Alumno:
    $headeroptions->cellCourseName = $COURSE->fullname;
    $headeroptions->evaluationmarksize = 3;
    // if null evaluation marks are not included in header
    $headeroptions->marksName = 'EVAL';
    $headeroptions->codebarType = $blended->codebartype;
    $headeroptions->identifyLabel = $identifyLabel;
    // show readable text for codebars 'none' if not to be shown
    $headeroptions->instructions = $instructions;
    /**
     * Give precedence to the selected number of columns in the $options
     */
    if (isset($options['columns'])) {
        $numcols = $options['columns'];
    } else {
        if (!isset($blended->numcols) || $blended->numcols == 0) {
            $numcols = 2;
        } else {
            $numcols = $blended->numcols;
        }
    }
    unset($quiz->questionsinuse);
    unset($QTYPES["random"]->catrandoms);
    $pagelist = quiz_questions_on_page($attempt->layout, 0);
    $pagequestions = explode(',', $pagelist);
    $questionlist = quiz_questions_in_quiz($attempt->layout);
    if (!$questionlist) {
        throw new PDFError("Quiz layout is empty", PDFError::QUIZ_IS_EMPTY);
    }
    $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance" . "  FROM {$CFG->prefix}question q," . "       {$CFG->prefix}quiz_question_instances i" . " WHERE i.quiz = '{$quiz->id}' AND q.id = i.question" . "   AND q.id IN ({$questionlist})";
    if (!($questions = get_records_sql($sql))) {
        throw new PDFError("Questions not found. ", PDFError::QUESTIONS_NOT_FOUND);
    }
    //Carga las preguntas con sus opciones
    if (!get_question_options($questions)) {
        throw new PDFError("Could not load question options", PDFError::COULD_NOT_LOAD_QUESTION_OPTIONS);
    }
    $quiz_userid = 4;
    $acode = $attempt->id;
    if (!($attemptnumber = (int) get_field_sql('SELECT MAX(attempt)+1 FROM ' . "{$CFG->prefix}quiz_attempts WHERE quiz = '{$quiz->id}' AND " . "userid = '{$quiz_userid}' AND timefinish > 0 AND preview != 1"))) {
        $attemptnumber = 1;
    }
    $quiz_uniqueid = $attempt->attempt;
    $timenow = time();
    $quiz_attempt = create_new_attempt($quiz, $attemptnumber, $quiz_userid, $acode, $quiz_uniqueid, $timenow);
    // Save the attempt
    // if (!insert_record('quiz_attempts', $quiz_attempt)) {
    //     error('Could not create new attempt');
    //}
    if (!($states = get_question_states($questions, $quiz, $quiz_attempt, false))) {
        throw new PDFError("Could not restore question sessions", PDFError::COULD_NOT_RESTORE_QUESTION_SESSIONS);
    }
    // TODO save question states someway in question_states
    foreach ($questions as $i => $question) {
        save_question_session($questions[$i], $states[$i]);
    }
    //	global $QTYPES;
    //$question = reset($questions);
    //print("state answer question $question->id: ".$QTYPES[$question->qtype]->get_question_options($question));
    $quests = new stdClass();
    $quests = array();
    foreach ($pagequestions as $i) {
        $options = quiz_get_renderoptions($quiz->review, $states[$i]);
        $quest = blended_get_question_formulation_and_controls($questions[$i], $states[$i], $quiz, $options);
        if (isset($quest->answers)) {
            foreach ($quest->answers as $c => $v) {
                $text = $v->answer;
                $quest->answers[$c]->answer = blended_image_path($text);
            }
        }
        $quests[] = $quest;
    }
    $idText = '';
    $original = new stdClass();
    $original->question = array();
    $question = $quests;
    $num = 0;
    $a = 0;
    //foreach ($questions as $m)//esto es redundante: crea un array igual
    foreach ($pagequestions as $i) {
        $questions[$i]->questiontext = $quests[$a]->questiontext;
        if (isset($questions[$i]->questiontext)) {
            $questions[$i]->questiontext = blended_image_path($questions[$i]->questiontext);
        }
        $question[$a] = $questions[$i];
        if ($question[$a]->qtype == "multichoice") {
            $question[$a]->anss = array();
            foreach ($states[$questions[$i]->id]->options->order as $j => $aid) {
                $question[$a]->anss[$j] = $questions[$i]->options->answers[$aid];
                $num++;
            }
        }
        $question[$a]->id = "q" . $question[$a]->id;
        $a++;
    }
    foreach ($question as $quest) {
        $original->question[] = $quest;
    }
    /**
     * Start PDF printing
     */
    // create new PDF document
    $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
    blended_new_page($pdf);
    $margins = $pdf->getMargins();
    $columnsWidth = ($pdf->getPageWidth() - $margins['left'] - $margins['right']) / $numcols;
    /**
     * Print all questions with TPDF to calculate exact dimensions of each block
     */
    $pdf->SetFont($headeroptions->textStyle['font'], '', $headeroptions->textStyle['fontsize']);
    for ($key = 0; $key < count($original->question); $key++) {
        $dims_borrador = new stdClass();
        $dims_borrador->coords = array();
        $original->question[$key]->height = blended_print_draft($pdf, $key, $original, $dims_borrador, $columnsWidth, $markSize, $headeroptions);
    }
    /**
     * Print final PDF
     */
    // Array to hold sizes of elements
    $dimsn = array();
    // Print content starting at abscisa $y
    $pdf1 = blended_print_quiz($pdf1, $numcols, $columnsWidth, $margins, $original, $dimsn, $markSize, $headeroptions, $uniqueid, $pdfFile);
    unset($pdf);
    return $pdf1;
}