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));
function display($quiz, $cm, $course) { /// This function just displays the report global $CFG, $SESSION, $db, $QTYPES; $strnoquiz = get_string('noquiz', 'quiz'); $strnoattempts = get_string('noattempts', 'quiz'); /// Only print headers if not asked to download data $download = optional_param('download', NULL); if (!$download) { $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "analysis"); } /// Construct the table for this particular report if (!$quiz->questions) { print_heading($strnoattempts); return true; } /// Check to see if groups are being used in this quiz if ($groupmode = groupmode($course, $cm)) { // Groups are being used if (!$download) { $currentgroup = setup_and_print_groups($course, $groupmode, "report.php?id={$cm->id}&mode=analysis"); } else { $currentgroup = get_and_set_current_group($course, $groupmode); } } else { $currentgroup = get_and_set_current_group($course, $groupmode); } // set Table and Analysis stats options if (!isset($SESSION->quiz_analysis_table)) { $SESSION->quiz_analysis_table = array('attemptselection' => 0, 'lowmarklimit' => 0, 'pagesize' => 10); } foreach ($SESSION->quiz_analysis_table as $option => $value) { $urlparam = optional_param($option, NULL); if ($urlparam === NULL) { ${$option} = $value; } else { ${$option} = $SESSION->quiz_analysis_table[$option] = $urlparam; } } $scorelimit = $quiz->sumgrades * $lowmarklimit / 100; // ULPGC ecastro DEBUG this is here to allow for different SQL to select attempts switch ($attemptselection) { case QUIZ_ALLATTEMPTS: $limit = ''; $group = ''; break; case QUIZ_HIGHESTATTEMPT: $limit = ', max(qa.sumgrades) '; $group = ' GROUP BY qa.userid '; break; case QUIZ_FIRSTATTEMPT: $limit = ', min(qa.timemodified) '; $group = ' GROUP BY qa.userid '; break; case QUIZ_LASTATTEMPT: $limit = ', max(qa.timemodified) '; $group = ' GROUP BY qa.userid '; break; } if ($attemptselection != QUIZ_ALLATTEMPTS) { $sql = 'SELECT qa.userid ' . $limit . 'FROM ' . $CFG->prefix . 'user u LEFT JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid ' . 'WHERE qa.quiz = ' . $quiz->id . ' AND qa.preview = 0 ' . $group; $usermax = get_records_sql_menu($sql); } $groupmembers = ''; $groupwhere = ''; //Add this to the SQL to show only group users if ($currentgroup) { $groupmembers = ', ' . groups_members_from_sql(); $groupwhere = ' AND ' . groups_members_where_sql($currentgroup, 'u.id'); } $sql = 'SELECT qa.* FROM ' . $CFG->prefix . 'quiz_attempts qa, ' . $CFG->prefix . 'user u ' . $groupmembers . 'WHERE u.id = qa.userid AND qa.quiz = ' . $quiz->id . ' AND qa.preview = 0 AND ( qa.sumgrades >= ' . $scorelimit . ' ) ' . $groupwhere; // ^^^^^^ es posible seleccionar aqu TODOS los quizzes, como quiere Jussi, // pero habra que llevar la cuenta ed cada quiz para restaura las preguntas (quizquestions, states) /// Fetch the attempts $attempts = get_records_sql($sql); if (empty($attempts)) { print_heading(get_string('nothingtodisplay')); $this->print_options_form($quiz, $cm, $attemptselection, $lowmarklimit, $pagesize); return true; } /// Here we rewiew all attempts and record data to construct the table $questions = array(); $statstable = array(); $questionarray = array(); foreach ($attempts as $attempt) { $questionarray[] = quiz_questions_in_quiz($attempt->layout); } $questionlist = quiz_questions_in_quiz(implode(",", $questionarray)); $questionarray = array_unique(explode(",", $questionlist)); $questionlist = implode(",", $questionarray); unset($questionarray); foreach ($attempts as $attempt) { switch ($attemptselection) { case QUIZ_ALLATTEMPTS: $userscore = 0; // can be anything, not used break; case QUIZ_HIGHESTATTEMPT: $userscore = $attempt->sumgrades; break; case QUIZ_FIRSTATTEMPT: $userscore = $attempt->timemodified; break; case QUIZ_LASTATTEMPT: $userscore = $attempt->timemodified; break; } if ($attemptselection == QUIZ_ALLATTEMPTS || $userscore == $usermax[$attempt->userid]) { $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 (!($quizquestions = get_records_sql($sql))) { error('No questions found'); } // Load the question type specific information if (!get_question_options($quizquestions)) { error('Could not load question options'); } // Restore the question sessions to their most recent states // creating new sessions where required if (!($states = get_question_states($quizquestions, $quiz, $attempt))) { error('Could not restore question sessions'); } $numbers = explode(',', $questionlist); $statsrow = array(); foreach ($numbers as $i) { if (!isset($quizquestions[$i]) or !isset($states[$i])) { continue; } $qtype = $quizquestions[$i]->qtype == 'random' ? $states[$i]->options->question->qtype : $quizquestions[$i]->qtype; $q = get_question_responses($quizquestions[$i], $states[$i]); if (empty($q)) { continue; } $qid = $q->id; if (!isset($questions[$qid])) { $questions[$qid]['id'] = $qid; $questions[$qid]['qname'] = $quizquestions[$i]->name; foreach ($q->responses as $answer => $r) { $r->count = 0; $questions[$qid]['responses'][$answer] = $r->answer; $questions[$qid]['rcounts'][$answer] = 0; $questions[$qid]['credits'][$answer] = $r->credit; $statsrow[$qid] = 0; } } $responses = get_question_actual_response($quizquestions[$i], $states[$i]); foreach ($responses as $resp) { if ($resp) { if ($key = array_search($resp, $questions[$qid]['responses'])) { $questions[$qid]['rcounts'][$key]++; } else { $test = new stdClass(); $test->responses = $QTYPES[$quizquestions[$i]->qtype]->get_correct_responses($quizquestions[$i], $states[$i]); if ($key = $QTYPES[$quizquestions[$i]->qtype]->check_response($quizquestions[$i], $states[$i], $test)) { $questions[$qid]['rcounts'][$key]++; } else { $questions[$qid]['responses'][] = $resp; $questions[$qid]['rcounts'][] = 1; $questions[$qid]['credits'][] = 0; } } } } $statsrow[$qid] = get_question_fraction_grade($quizquestions[$i], $states[$i]); } $attemptscores[$attempt->id] = $attempt->sumgrades; $statstable[$attempt->id] = $statsrow; } } // Statistics Data table built unset($attempts); unset($quizquestions); unset($states); // now calculate statistics and set the values in the $questions array $top = max($attemptscores); $bottom = min($attemptscores); $gap = ($top - $bottom) / 3; $top -= $gap; $bottom += $gap; foreach ($questions as $qid => $q) { $questions[$qid] = $this->report_question_stats($q, $attemptscores, $statstable, $top, $bottom); } unset($attemptscores); unset($statstable); /// Now check if asked download of data if ($download = optional_param('download', NULL)) { $filename = clean_filename("{$course->shortname} " . format_string($quiz->name, true)); switch ($download) { case "Excel": $this->Export_Excel($questions, $filename); break; case "ODS": $this->Export_ODS($questions, $filename); break; case "CSV": $this->Export_CSV($questions, $filename); break; } } /// Construct the table for this particular report $tablecolumns = array('id', 'qname', 'responses', 'credits', 'rcounts', 'rpercent', 'facility', 'qsd', 'disc_index', 'disc_coeff'); $tableheaders = array(get_string('qidtitle', 'quiz_analysis'), get_string('qtexttitle', 'quiz_analysis'), get_string('responsestitle', 'quiz_analysis'), get_string('rfractiontitle', 'quiz_analysis'), get_string('rcounttitle', 'quiz_analysis'), get_string('rpercenttitle', 'quiz_analysis'), get_string('facilitytitle', 'quiz_analysis'), get_string('stddevtitle', 'quiz_analysis'), get_string('dicsindextitle', 'quiz_analysis'), get_string('disccoefftitle', 'quiz_analysis')); $table = new flexible_table('mod-quiz-report-itemanalysis'); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($CFG->wwwroot . '/mod/quiz/report.php?q=' . $quiz->id . '&mode=analysis'); $table->sortable(true); $table->no_sorting('rpercent'); $table->collapsible(true); $table->initialbars(false); $table->column_class('id', 'numcol'); $table->column_class('credits', 'numcol'); $table->column_class('rcounts', 'numcol'); $table->column_class('rpercent', 'numcol'); $table->column_class('facility', 'numcol'); $table->column_class('qsd', 'numcol'); $table->column_class('disc_index', 'numcol'); $table->column_class('disc_coeff', 'numcol'); $table->column_suppress('id'); $table->column_suppress('qname'); $table->column_suppress('facility'); $table->column_suppress('qsd'); $table->column_suppress('disc_index'); $table->column_suppress('disc_coeff'); $table->set_attribute('cellspacing', '0'); $table->set_attribute('id', 'itemanalysis'); $table->set_attribute('class', 'generaltable generalbox'); // Start working -- this is necessary as soon as the niceties are over $table->setup(); $tablesort = $table->get_sql_sort(); $sorts = explode(",", trim($tablesort)); if ($tablesort and is_array($sorts)) { $sortindex = array(); $sortorder = array(); foreach ($sorts as $sort) { $data = explode(" ", trim($sort)); $sortindex[] = trim($data[0]); $s = trim($data[1]); if ($s == "ASC") { $sortorder[] = SORT_ASC; } else { $sortorder[] = SORT_DESC; } } if (count($sortindex) > 0) { $sortindex[] = "id"; $sortorder[] = SORT_ASC; foreach ($questions as $qid => $row) { $index1[$qid] = $row[$sortindex[0]]; $index2[$qid] = $row[$sortindex[1]]; } array_multisort($index1, $sortorder[0], $index2, $sortorder[1], $questions); } } $format_options = new stdClass(); $format_options->para = false; $format_options->noclean = true; $format_options->newlines = false; // Now it is time to page the data, simply slice the keys in the array if (!isset($pagesize) || (int) $pagesize < 1) { $pagesize = 10; } $table->pagesize($pagesize, count($questions)); $start = $table->get_page_start(); $pagequestions = array_slice(array_keys($questions), $start, $pagesize); foreach ($pagequestions as $qnum) { $q = $questions[$qnum]; $qid = $q['id']; $question = get_record('question', 'id', $qid); if (has_capability('moodle/question:manage', get_context_instance(CONTEXT_COURSE, $course->id))) { $qnumber = " (" . link_to_popup_window('/question/question.php?id=' . $qid, '&cmid=' . $cm->id . 'editquestion', $qid, 450, 550, get_string('edit'), 'none', true) . ") "; } else { $qnumber = $qid; } $qname = '<div class="qname">' . format_text($question->name . " : ", $question->questiontextformat, $format_options, $quiz->course) . '</div>'; $qicon = print_question_icon($question, true); $qreview = quiz_question_preview_button($quiz, $question); $qtext = format_text($question->questiontext, $question->questiontextformat, $format_options, $quiz->course); $qquestion = $qname . "\n" . $qtext . "\n"; $responses = array(); foreach ($q['responses'] as $aid => $resp) { $response = new stdClass(); if ($q['credits'][$aid] <= 0) { $qclass = 'uncorrect'; } elseif ($q['credits'][$aid] == 1) { $qclass = 'correct'; } else { $qclass = 'partialcorrect'; } $response->credit = '<span class="' . $qclass . '">(' . format_float($q['credits'][$aid], 2) . ') </span>'; $response->text = '<span class="' . $qclass . '">' . format_text($resp, FORMAT_MOODLE, $format_options, $quiz->course) . ' </span>'; $count = $q['rcounts'][$aid] . '/' . $q['count']; $response->rcount = $count; $response->rpercent = '(' . format_float($q['rcounts'][$aid] / $q['count'] * 100, 0) . '%)'; $responses[] = $response; } $facility = format_float($q['facility'] * 100, 0) . "%"; $qsd = format_float($q['qsd'], 3); $di = format_float($q['disc_index'], 2); $dc = format_float($q['disc_coeff'], 2); $response = array_shift($responses); $table->add_data(array($qnumber . "\n<br />" . $qicon . "\n " . $qreview, $qquestion, $response->text, $response->credit, $response->rcount, $response->rpercent, $facility, $qsd, $di, $dc)); foreach ($responses as $response) { $table->add_data(array('', '', $response->text, $response->credit, $response->rcount, $response->rpercent, '', '', '', '')); } } print_heading_with_help(get_string("analysistitle", "quiz_analysis"), "itemanalysis", "quiz"); echo '<div id="tablecontainer">'; $table->print_html(); echo '</div>'; $this->print_options_form($quiz, $cm, $attemptselection, $lowmarklimit, $pagesize); return true; }
/** * Prints questions with comment and grade form underneath each question * * @return void * @todo Finish documenting this function **/ function print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded) { global $CFG; // TODO get the context, and put in proper roles an permissions checks. $context = NULL; $questions[$question->id] =& $question; $usehtmleditor = can_use_richtext_editor(); list($select, $from, $where) = $this->attempts_sql($quiz->id, false, $question->id, $userid, $attemptid, $gradeungraded, $gradenextungraded); $sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC'; if ($gradenextungraded) { $attempts = get_records_sql($select . $from . $where . $sort, 0, QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE); } else { $attempts = get_records_sql($select . $from . $where . $sort); } if ($attempts) { $firstattempt = current($attempts); $fullname = fullname($firstattempt); if ($gradeungraded) { // getting all ungraded attempts print_heading(get_string('gradingungraded', 'quiz_grading', $ungraded), '', 3); } else { if ($gradenextungraded) { // getting next ungraded attempts print_heading(get_string('gradingnextungraded', 'quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE), '', 3); } else { if ($userid) { print_heading(get_string('gradinguser', 'quiz_grading', $fullname), '', 3); } else { if ($attemptid) { $a = new object(); $a->fullname = $fullname; $a->attempt = $firstattempt->attempt; print_heading(get_string('gradingattempt', 'quiz_grading', $a), '', 3); } else { print_heading(get_string('gradingall', 'quiz_grading', count($attempts)), '', 3); } } } } // Display the form with one part for each selected attempt echo '<form method="post" action="report.php">' . '<input type="hidden" name="mode" value="grading" />' . '<input type="hidden" name="q" value="' . $quiz->id . '" />' . '<input type="hidden" name="sesskey" value="' . sesskey() . '" />' . '<input type="hidden" name="questionid" value="' . $question->id . '" />'; foreach ($attempts as $attempt) { // 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]; $options = quiz_get_reviewoptions($quiz, $attempt, $context); unset($options->questioncommentlink); $options->noeditlink = true; $copy = $state->manualcomment; $state->manualcomment = ''; $options->readonly = 1; $gradedclass = question_state_is_graded($state) ? ' class="highlightgraded" ' : ''; $gradedstring = question_state_is_graded($state) ? ' ' . get_string('graded', 'quiz_grading') : ''; $a = new object(); $a->fullname = fullname($attempt, true); $a->attempt = $attempt->attempt; // print the user name, attempt count, the question, and some more hidden fields echo '<div class="boxaligncenter" width="80%" style="clear:left;padding:15px;">'; echo "<span{$gradedclass}>" . get_string('gradingattempt', 'quiz_grading', $a); echo $gradedstring . "</span>"; print_question($question, $state, '', $quiz, $options); $prefix = "manualgrades[{$attempt->uniqueid}]"; if (!question_state_is_graded($state)) { $grade = ''; } else { $grade = round($state->last_graded->grade, 3); } $state->manualcomment = $copy; include $CFG->dirroot . '/question/comment.html'; echo '</div>'; } echo '<div class="boxaligncenter"><input type="submit" value="' . get_string('savechanges') . '" /></div>' . '</form>'; if ($usehtmleditor) { use_html_editor(); } } else { notify(get_string('noattemptstoshow', 'quiz')); } }
/// Finish attempt if requested if ($finishattempt) { // Set the attempt to be finished $attempt->timefinish = $timestamp; // load all the questions $closequestionlist = quiz_questions_in_quiz($attempt->layout); $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 ({$closequestionlist})"; if (!($closequestions = get_records_sql($sql))) { 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 = '';
require_capability('mod/quiz:grade', $context); // 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->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'));
// new sessions. Make $states a reference to the states array in the moodle // session. if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid == $id) { // Reload the question session history from the moodle session $states =& $SESSION->quizpreview->states; $historylength = count($states) - 1; if ($back && $historylength > 0) { // Go back one step in the history unset($states[$historylength]); $historylength--; } } else { // Record the question id in the moodle session $SESSION->quizpreview->questionid = $id; // Create an empty session for the question if (!($newstates = get_question_states($questions, $quiz, $attempt))) { print_error('newattemptfail', 'quiz'); } $SESSION->quizpreview->states = array($newstates); $states =& $SESSION->quizpreview->states; $historylength = 0; } if (!$fillcorrect && !$back && ($form = data_submitted())) { $form = (array) $form; $submitted = true; // Create a new item in the history of question states (don't simplify!) $states[$historylength + 1] = array(); $states[$historylength + 1][$id] = clone $states[$historylength][$id]; $historylength++; $curstate =& $states[$historylength][$id]; $curstate->changed = false;
/** * Prints questions with comment and grade form underneath each question * * @return void * @todo Finish documenting this function **/ function print_questions_and_form($quiz, $question) { global $CFG, $db; // grade question specific parameters $gradeall = optional_param('gradeall', 0, PARAM_INT); $userid = optional_param('userid', 0, PARAM_INT); $attemptid = optional_param('attemptid', 0, PARAM_INT); // TODO get the context, and put in proper roles an permissions checks. $context = NULL; $questions[$question->id] =& $question; $usehtmleditor = can_use_richtext_editor(); $users = get_course_students($quiz->course); $userids = implode(',', array_keys($users)); // this sql joins the attempts table and the user table $select = 'SELECT ' . sql_concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')) . ' AS userattemptid, qa.id AS attemptid, qa.uniqueid, qa.attempt, qa.timefinish, qa.preview, u.id AS userid, u.firstname, u.lastname, u.picture '; $from = 'FROM ' . $CFG->prefix . 'user u LEFT JOIN ' . $CFG->prefix . 'quiz_attempts qa ON (u.id = qa.userid AND qa.quiz = ' . $quiz->id . ') '; if ($gradeall) { // get all user attempts $where = 'WHERE u.id IN (' . $userids . ') '; } else { if ($userid) { // get all the attempts for a specific user $where = 'WHERE u.id=' . $userid . ' '; } else { // get a specific attempt $where = 'WHERE qa.id=' . $attemptid . ' '; } } // ignore previews $where .= ' AND preview = 0 '; $where .= 'AND ' . $db->IfNull('qa.attempt', '0') . ' != 0 '; $where .= 'AND ' . $db->IfNull('qa.timefinish', '0') . ' != 0 '; $sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC'; $attempts = get_records_sql($select . $from . $where . $sort); // Display the form with one part for each selected attempt echo '<form method="post" action="report.php">' . '<input type="hidden" name="mode" value="grading" />' . '<input type="hidden" name="q" value="' . $quiz->id . '" />' . '<input type="hidden" name="sesskey" value="' . sesskey() . '" />' . '<input type="hidden" name="action" value="viewquestion" />' . '<input type="hidden" name="questionid" value="' . $question->id . '" />'; foreach ($attempts as $attempt) { // 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]; $options = quiz_get_reviewoptions($quiz, $attempt, $context); unset($options->questioncommentlink); $copy = $state->manualcomment; $state->manualcomment = ''; $options->readonly = 1; // print the user name, attempt count, the question, and some more hidden fields echo '<div class="boxaligncenter" width="80%" style="padding:15px;">' . fullname($attempt, true) . ': ' . get_string('attempt', 'quiz') . $attempt->attempt; print_question($question, $state, '', $quiz, $options); $prefix = "manualgrades[{$attempt->uniqueid}]"; $grade = round($state->last_graded->grade, 3); $state->manualcomment = $copy; include $CFG->dirroot . '/question/comment.html'; echo '</div>'; } echo '<div class="boxaligncenter"><input type="submit" value="' . get_string('savechanges') . '" /></div>' . '</form>'; if ($usehtmleditor) { use_html_editor(); } }
/** * Prints questions with comment and grade form underneath each question * * @return void * @todo Finish documenting this function **/ function print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded) { global $CFG, $DB, $OUTPUT; $context = get_context_instance(CONTEXT_MODULE, $this->cm->id); $questions[$question->id] =& $question; $usehtmleditor = can_use_html_editor(); list($select, $from, $where, $params) = $this->attempts_sql($quiz->id, false, $question->id, $userid, $attemptid, $gradeungraded, $gradenextungraded); $sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC'; if ($gradenextungraded) { $attempts = $DB->get_records_sql($select . $from . $where . $sort, $params, 0, QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE); } else { $attempts = $DB->get_records_sql($select . $from . $where . $sort, $params); } if ($attempts) { $firstattempt = current($attempts); $fullname = fullname($firstattempt); if ($gradeungraded) { // getting all ungraded attempts echo $OUTPUT->heading(get_string('gradingungraded', 'quiz_grading', $ungraded), 3); } else { if ($gradenextungraded) { // getting next ungraded attempts echo $OUTPUT->heading(get_string('gradingnextungraded', 'quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE), 3); } else { if ($userid) { echo $OUTPUT->heading(get_string('gradinguser', 'quiz_grading', $fullname), 3); } else { if ($attemptid) { $a = new object(); $a->fullname = $fullname; $a->attempt = $firstattempt->attempt; echo $OUTPUT->heading(get_string('gradingattempt', 'quiz_grading', $a), 3); } else { echo $OUTPUT->heading(get_string('gradingall', 'quiz_grading', count($attempts)), 3); } } } } // Display the form with one part for each selected attempt echo '<form method="post" action="report.php" class="mform" id="manualgradingform">' . '<input type="hidden" name="mode" value="grading" />' . '<input type="hidden" name="q" value="' . $quiz->id . '" />' . '<input type="hidden" name="sesskey" value="' . sesskey() . '" />' . '<input type="hidden" name="questionid" value="' . $question->id . '" />'; foreach ($attempts as $attempt) { // 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]; $options = quiz_get_reviewoptions($quiz, $attempt, $context); unset($options->questioncommentlink); $options->readonly = 1; if (question_state_is_graded($state)) { $gradedclass = 'main highlightgraded'; $gradedstring = ' ' . get_string('graded', 'quiz_grading'); } else { $gradedclass = 'main'; $gradedstring = ''; } $a = new object(); $a->fullname = fullname($attempt, true); $a->attempt = $attempt->attempt; // print the user name, attempt count, the question, and some more hidden fields echo '<div class="boxaligncenter" width="80%" style="clear:left;padding:15px;">'; echo $OUTPUT->heading(get_string('gradingattempt', 'quiz_grading', $a) . $gradedstring, 3, $gradedclass); // Print the question, without showing any previous comment. $copy = $state->manualcomment; $state->manualcomment = ''; $options->noeditlink = true; print_question($question, $state, '', $quiz, $options); // The print the comment and grade fields, putting back the previous comment. $state->manualcomment = $copy; question_print_comment_fields($question, $state, 'manualgrades[' . $attempt->uniqueid . ']', $quiz, get_string('manualgrading', 'quiz')); echo '</div>'; } echo '<div class="boxaligncenter"><input type="submit" value="' . get_string('savechanges') . '" /></div>' . '</form>'; } else { echo $OUTPUT->notification(get_string('noattemptstoshow', 'quiz')); } }
$number = quiz_first_questionnumber($attempt->layout, $pagelist); $quests = new stdClass(); $quests = array(); /// Print all the questions // LA PAGINACION YA NO LA HACE MOODLE SINO BLENDED ASI QUE EL ARRAY PAGEQUESTIONS NO TIENE SENTIDO // foreach ($pagequestions as $i) { // $options = quiz_get_renderoptions($quiz->review, $states[$i]); // $questions[$i] // $quests[$i] = get_question_formulation_and_controls($questions[$i], $states[$i], $quiz, $options); // $number += $questions[$i]->length; // } //Carga las preguntas con sus opciones if (!get_question_options(&$questions)) { error('Could not load question options'); } $states = get_question_states(&$questions, $quiz, $attempt, $lastattemptid); foreach ($questions as $id => $question) { $options = quiz_get_renderoptions($quiz->review, $states[$id]); $quest = get_question_formulation_and_controls(&$question, &$states[$id], $quiz, $options); //$question->anss=$quest->anss; $quests[] = $quest; $number += $question->length; // NO ENTIENDO QUE ACUMULA ESTO } $item = new stdClass(); $descartados = new stdClass(); $descartados->question = array(); $borrado = new stdClass(); $borrado->question = array(); $original = new stdClass(); $original->question = array();
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; }
/** * * 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; }