Example #1
0
        error('Questions missing');
    }
    // Load the question type specific information
    if (!get_question_options($closequestions)) {
        error('Could not load question options');
    }
    // Restore the question sessions
    if (!($closestates = get_question_states($closequestions, $quiz, $attempt))) {
        error('Could not restore question sessions');
    }
    $success = true;
    foreach ($closequestions as $key => $question) {
        $action->event = QUESTION_EVENTCLOSE;
        $action->responses = $closestates[$key]->responses;
        $action->timestamp = $closestates[$key]->timestamp;
        if (question_process_responses($question, $closestates[$key], $action, $quiz, $attempt)) {
            save_question_session($question, $closestates[$key]);
        } else {
            $success = false;
        }
    }
    if (!$success) {
        $pagebit = '';
        if ($page) {
            $pagebit = '&page=' . $page;
        }
        print_error('errorprocessingresponses', 'question', $CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id . $pagebit);
    }
    add_to_log($course->id, 'quiz', 'close attempt', 'review.php?attempt=' . $attempt->id, $quiz->id, $cm->id);
}
/// Update the quiz attempt and the overall grade for the quiz
Example #2
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;
}
/**
* 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 #4
0
    $historylength++;
    $curstate =& $states[$historylength][$id];
    $curstate->changed = false;
    // Process the responses
    unset($form['id']);
    unset($form['quizid']);
    unset($form['continue']);
    unset($form['markall']);
    unset($form['finishattempt']);
    unset($form['back']);
    unset($form['startagain']);
    $event = $finishattempt ? QUESTION_EVENTCLOSE : QUESTION_EVENTSUBMIT;
    if ($actions = question_extract_responses($questions, $form, $event)) {
        $actions[$id]->timestamp = 0;
        // We do not care about timelimits here
        if (!question_process_responses($questions[$id], $curstate, $actions[$id], $quiz, $attempt)) {
            unset($SESSION->quizpreview);
            print_error('errorprocessingresponses', 'question', $url->out());
        }
        if (!$curstate->changed) {
            // Update the current state rather than creating a new one
            $historylength--;
            unset($states[$historylength]);
            $states = array_values($states);
            $curstate =& $states[$historylength][$id];
        }
    }
} else {
    $submitted = false;
    $curstate =& $states[$historylength][$id];
}
Example #5
0
/// We have been asked to finish attempt, so do that //////////////////////
/// 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) {
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;
}