/** * Load the question data necessary in the reports. * - Remove description questions. * - Order questions in order that they are in the quiz * - Add question numbers. * - Add grade from quiz_questions_instance */ function quiz_report_load_questions($quiz) { global $CFG; $questionlist = quiz_questions_in_quiz($quiz->questions); //In fact in most cases the id IN $questionlist below is redundant //since we are also doing a JOIN on the qqi table. But will leave it in //since this double check will probably do no harm. if (!($questions = get_records_sql("SELECT q.*, qqi.grade " . "FROM {$CFG->prefix}question q, " . "{$CFG->prefix}quiz_question_instances qqi " . "WHERE q.id IN ({$questionlist}) AND " . "qqi.question = q.id AND " . "qqi.quiz =" . $quiz->id))) { print_error('No questions found'); } //Now we have an array of questions from a quiz we work out there question nos and remove //questions with zero length ie. description questions etc. //also put questions in order. $number = 1; $realquestions = array(); $questionids = explode(',', $questionlist); foreach ($questionids as $id) { if ($questions[$id]->length) { // Ignore questions of zero length $realquestions[$id] = $questions[$id]; $realquestions[$id]->number = $number; $number += $questions[$id]->length; } } return $realquestions; }
/** * Get the slots of real questions (not descriptions) in this quiz, in order. * @param object $quiz the quiz. * @return array of slot => $question object with fields * ->slot, ->id, ->maxmark, ->number, ->length. */ function quiz_report_get_significant_questions($quiz) { global $DB; $questionids = quiz_questions_in_quiz($quiz->questions); if (empty($questionids)) { return array(); } list($usql, $params) = $DB->get_in_or_equal(explode(',', $questionids)); $params[] = $quiz->id; $questions = $DB->get_records_sql("\nSELECT\n q.id,\n q.length,\n qqi.grade AS maxmark\n\nFROM {question} q\nJOIN {quiz_question_instances} qqi ON qqi.question = q.id\n\nWHERE\n q.id {$usql} AND\n qqi.quiz = ? AND\n length > 0", $params); $qsbyslot = array(); $number = 1; foreach (explode(',', $questionids) as $key => $id) { if (!array_key_exists($id, $questions)) { continue; } $slot = $key + 1; $question = $questions[$id]; $question->slot = $slot; $question->number = $number; $qsbyslot[$slot] = $question; $number += $question->length; } return $qsbyslot; }
public function test_quiz_questions_in_quiz() { $this->assertEquals(quiz_questions_in_quiz(''), ''); $this->assertEquals(quiz_questions_in_quiz('0'), ''); $this->assertEquals(quiz_questions_in_quiz('0,0'), ''); $this->assertEquals(quiz_questions_in_quiz('0,0,0'), ''); $this->assertEquals(quiz_questions_in_quiz('1'), '1'); $this->assertEquals(quiz_questions_in_quiz('1,2'), '1,2'); $this->assertEquals(quiz_questions_in_quiz('1,0,2'), '1,2'); $this->assertEquals(quiz_questions_in_quiz('0,1,0,0,2,0'), '1,2'); }
public function display($quiz, $cm, $course) { global $CFG, $DB, $PAGE; $this->quiz = $quiz; $this->cm = $cm; $this->course = $course; // Get the URL options. $slot = optional_param('slot', null, PARAM_INT); $questionid = optional_param('qid', null, PARAM_INT); $grade = optional_param('grade', null, PARAM_ALPHA); $includeauto = optional_param('includeauto', false, PARAM_BOOL); if (!in_array($grade, array('all', 'needsgrading', 'autograded', 'manuallygraded'))) { $grade = null; } $pagesize = optional_param('pagesize', self::DEFAULT_PAGE_SIZE, PARAM_INT); $page = optional_param('page', 0, PARAM_INT); $order = optional_param('order', self::DEFAULT_ORDER, PARAM_ALPHA); // Assemble the options requried to reload this page. $optparams = array('includeauto', 'page'); foreach ($optparams as $param) { if (${$param}) { $this->viewoptions[$param] = ${$param}; } } if ($pagesize != self::DEFAULT_PAGE_SIZE) { $this->viewoptions['pagesize'] = $pagesize; } if ($order != self::DEFAULT_ORDER) { $this->viewoptions['order'] = $order; } // Check permissions. $this->context = context_module::instance($cm->id); require_capability('mod/quiz:grade', $this->context); $shownames = has_capability('quiz/grading:viewstudentnames', $this->context); $showidnumbers = has_capability('quiz/grading:viewidnumber', $this->context); // Validate order. if (!in_array($order, array('random', 'date', 'studentfirstname', 'studentlastname', 'idnumber'))) { $order = self::DEFAULT_ORDER; } else { if (!$shownames && ($order == 'studentfirstname' || $order == 'studentlastname')) { $order = self::DEFAULT_ORDER; } else { if (!$showidnumbers && $order == 'idnumber') { $order = self::DEFAULT_ORDER; } } } if ($order == 'random') { $page = 0; } // Get the list of questions in this quiz. $this->questions = quiz_report_get_significant_questions($quiz); if ($slot && !array_key_exists($slot, $this->questions)) { throw new moodle_exception('unknownquestion', 'quiz_grading'); } // Process any submitted data. if ($data = data_submitted() && confirm_sesskey() && $this->validate_submitted_marks()) { $this->process_submitted_data(); redirect($this->grade_question_url($slot, $questionid, $grade, $page + 1)); } // Get the group, and the list of significant users. $this->currentgroup = $this->get_current_group($cm, $course, $this->context); if ($this->currentgroup == self::NO_GROUPS_ALLOWED) { $this->users = array(); } else { $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $this->currentgroup, '', false); } $questionsinquiz = quiz_questions_in_quiz($quiz->questions); $counts = null; if ($slot && $questionsinquiz) { // Make sure there is something to do. $statecounts = $this->get_question_state_summary(array($slot)); foreach ($statecounts as $record) { if ($record->questionid == $questionid) { $counts = $record; break; } } // If not, redirect back to the list. if (!$counts || $counts->{$grade} == 0) { redirect($this->list_questions_url(), get_string('alldoneredirecting', 'quiz_grading')); } } // Start output. $this->print_header_and_tabs($cm, $course, $quiz, 'grading'); // What sort of page to display? if (!$questionsinquiz) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else { if (!$slot) { $this->display_index($includeauto); } else { $this->display_grading_interface($slot, $questionid, $grade, $pagesize, $page, $shownames, $showidnumbers, $order, $counts); } } return true; }
/// Print heading and tabs if this is part of a preview if (has_capability('mod/quiz:preview', $context)) { if ($attempt->userid == $USER->id) { // this is the report on a preview $currenttab = 'preview'; } else { $currenttab = 'reports'; $mode = ''; } include 'tabs.php'; } else { print_heading(format_string($quiz->name)); } /// Load all the questions and states needed by this script // load the questions needed by page $pagelist = $showall ? quiz_questions_in_quiz($attempt->layout) : quiz_questions_on_page($attempt->layout, $page); $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 ({$pagelist})"; if (!($questions = get_records_sql($sql))) { error('No questions found'); } // Load the question type specific information if (!get_question_options($questions)) { 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($questions, $quiz, $attempt))) { error('Could not restore question sessions'); } /// Print infobox $timelimit = (int) $quiz->timelimit * 60;
/** * Upgrade states for an attempt to Moodle 1.5 model * * Any state that does not yet have its timestamp set to nonzero has not yet been upgraded from Moodle 1.4 * The reason these are still around is that for large sites it would have taken too long to * upgrade all states at once. This function sets the timestamp field and creates an entry in the * question_sessions table. * @param object $attempt The attempt whose states need upgrading */ function quiz_upgrade_states($attempt) { global $CFG; // The old quiz model only allowed a single response per quiz attempt so that there will be // only one state record per question for this attempt. // We set the timestamp of all states to the timemodified field of the attempt. execute_sql("UPDATE {$CFG->prefix}question_states SET timestamp = '{$attempt->timemodified}' WHERE attempt = '{$attempt->uniqueid}'", false); // For each state we create an entry in the question_sessions table, with both newest and // newgraded pointing to this state. // Actually we only do this for states whose question is actually listed in $attempt->layout. // We do not do it for states associated to wrapped questions like for example the questions // used by a RANDOM question $session = new stdClass(); $session->attemptid = $attempt->uniqueid; $questionlist = quiz_questions_in_quiz($attempt->layout); if ($questionlist and $states = get_records_select('question_states', "attempt = '{$attempt->uniqueid}' AND question IN ({$questionlist})")) { foreach ($states as $state) { $session->newgraded = $state->id; $session->newest = $state->id; $session->questionid = $state->question; insert_record('question_sessions', $session, false); } } }
if (!$success) { $pagebit = ''; if ($page) { $pagebit = '&page=' . $page; } print_error('errorprocessingresponses', 'question', $CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id . $pagebit); } $attempt->timemodified = $timestamp; // We have now finished processing form data } /// 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;
/** * Display the report. */ public function display($quiz, $cm, $course) { global $CFG, $DB, $OUTPUT, $PAGE; $this->context = context_module::instance($cm->id); // Work out the display options. $download = optional_param('download', '', PARAM_ALPHA); $everything = optional_param('everything', 0, PARAM_BOOL); $recalculate = optional_param('recalculate', 0, PARAM_BOOL); // A qid paramter indicates we should display the detailed analysis of a question. $qid = optional_param('qid', 0, PARAM_INT); $slot = optional_param('slot', 0, PARAM_INT); $pageoptions = array(); $pageoptions['id'] = $cm->id; $pageoptions['mode'] = 'statistics'; $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions); $mform = new quiz_statistics_settings_form($reporturl); if ($fromform = $mform->get_data()) { $useallattempts = $fromform->useallattempts; if ($fromform->useallattempts) { set_user_preference('quiz_report_statistics_useallattempts', $fromform->useallattempts); } else { unset_user_preference('quiz_report_statistics_useallattempts'); } } else { $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0); } // Find out current groups mode. $currentgroup = $this->get_current_group($cm, $course, $this->context); $nostudentsingroup = false; // True if a group is selected and there is no one in it. if (empty($currentgroup)) { $currentgroup = 0; $groupstudents = array(); } else if ($currentgroup == self::NO_GROUPS_ALLOWED) { $groupstudents = array(); $nostudentsingroup = true; } else { // All users who can attempt quizzes and who are in the currently selected group. $groupstudents = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); if (!$groupstudents) { $nostudentsingroup = true; } } // If recalculate was requested, handle that. if ($recalculate && confirm_sesskey()) { $this->clear_cached_data($quiz->id, $currentgroup, $useallattempts); redirect($reporturl); } // Set up the main table. $this->table = new quiz_statistics_table(); if ($everything) { $report = get_string('completestatsfilename', 'quiz_statistics'); } else { $report = get_string('questionstatsfilename', 'quiz_statistics'); } $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); $filename = quiz_report_download_filename($report, $courseshortname, $quiz->name); $this->table->is_downloading($download, $filename, get_string('quizstructureanalysis', 'quiz_statistics')); // Load the questions. $questions = quiz_report_get_significant_questions($quiz); $questionids = array(); foreach ($questions as $question) { $questionids[] = $question->id; } $fullquestions = question_load_questions($questionids); foreach ($questions as $qno => $question) { $q = $fullquestions[$question->id]; $q->maxmark = $question->maxmark; $q->slot = $qno; $q->number = $question->number; $questions[$qno] = $q; } // Get the data to be displayed. list($quizstats, $questions, $subquestions, $s) = $this->get_quiz_and_questions_stats($quiz, $currentgroup, $nostudentsingroup, $useallattempts, $groupstudents, $questions); $quizinfo = $this->get_formatted_quiz_info_data($course, $cm, $quiz, $quizstats); // Set up the table, if there is data. if ($s) { $this->table->statistics_setup($quiz, $cm->id, $reporturl, $s); } // Print the page header stuff (if not downloading. if (!$this->table->is_downloading()) { $this->print_header_and_tabs($cm, $course, $quiz, 'statistics'); if (groups_get_activity_groupmode($cm)) { groups_print_activity_menu($cm, $reporturl->out()); if ($currentgroup && !$groupstudents) { $OUTPUT->notification(get_string('nostudentsingroup', 'quiz_statistics')); } } if (!quiz_questions_in_quiz($quiz->questions)) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else if (!$this->table->is_downloading() && $s == 0) { echo $OUTPUT->notification(get_string('noattempts', 'quiz')); } // Print display options form. $mform->set_data(array('useallattempts' => $useallattempts)); $mform->display(); } if ($everything) { // Implies is downloading. // Overall report, then the analysis of each question. $this->download_quiz_info_table($quizinfo); if ($s) { $this->output_quiz_structure_analysis_table($s, $questions, $subquestions); if ($this->table->is_downloading() == 'xhtml') { $this->output_statistics_graph($quizstats->id, $s); } foreach ($questions as $question) { if (question_bank::get_qtype( $question->qtype, false)->can_analyse_responses()) { $this->output_individual_question_response_analysis( $question, $reporturl, $quizstats); } else if (!empty($question->_stats->subquestions)) { $subitemstodisplay = explode(',', $question->_stats->subquestions); foreach ($subitemstodisplay as $subitemid) { $this->output_individual_question_response_analysis( $subquestions[$subitemid], $reporturl, $quizstats); } } } } $this->table->export_class_instance()->finish_document(); } else if ($slot) { // Report on an individual question indexed by position. if (!isset($questions[$slot])) { print_error('questiondoesnotexist', 'question'); } $this->output_individual_question_data($quiz, $questions[$slot]); $this->output_individual_question_response_analysis( $questions[$slot], $reporturl, $quizstats); // Back to overview link. echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' . get_string('backtoquizreport', 'quiz_statistics') . '</a>', 'backtomainstats boxaligncenter generalbox boxwidthnormal mdl-align'); } else if ($qid) { // Report on an individual sub-question indexed questionid. if (!isset($subquestions[$qid])) { print_error('questiondoesnotexist', 'question'); } $this->output_individual_question_data($quiz, $subquestions[$qid]); $this->output_individual_question_response_analysis( $subquestions[$qid], $reporturl, $quizstats); // Back to overview link. echo $OUTPUT->box('<a href="' . $reporturl->out() . '">' . get_string('backtoquizreport', 'quiz_statistics') . '</a>', 'boxaligncenter generalbox boxwidthnormal mdl-align'); } else if ($this->table->is_downloading()) { // Downloading overview report. $this->download_quiz_info_table($quizinfo); $this->output_quiz_structure_analysis_table($s, $questions, $subquestions); $this->table->finish_output(); } else { // On-screen display of overview report. echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics')); echo $this->output_caching_info($quizstats, $quiz->id, $currentgroup, $groupstudents, $useallattempts, $reporturl); echo $this->everything_download_options(); echo $this->output_quiz_info_table($quizinfo); if ($s) { echo $OUTPUT->heading(get_string('quizstructureanalysis', 'quiz_statistics')); $this->output_quiz_structure_analysis_table($s, $questions, $subquestions); $this->output_statistics_graph($quizstats->id, $s); } } return true; }
function regrade_selected_attempts($quiz, $attemptids, $groupstudents) { global $DB; require_capability('mod/quiz:regrade', $this->context); if ($groupstudents) { list($usql, $params) = $DB->get_in_or_equal($groupstudents); $where = "qa.userid $usql AND "; } else { $params = array(); $where = ''; } list($asql, $aparams) = $DB->get_in_or_equal($attemptids); $where = "qa.id $asql AND "; $params = array_merge($params, $aparams); $where .= "qa.quiz = ? AND qa.preview = 0"; $params[] = $quiz->id; if (!$attempts = $DB->get_records_sql('SELECT qa.* FROM {quiz_attempts} qa WHERE '. $where, $params)) { print_error('noattemptstoregrade', 'quiz_overview'); } // Fetch all questions $questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance', '{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question'); $updateoverallgrades = array(); foreach($attempts as $attempt) { foreach ($questions as $question) { $changed = regrade_question_in_attempt($question, $attempt, $quiz, true); } $updateoverallgrades[] = $attempt->uniqueid; } $this->check_overall_grades($quiz, array(), $updateoverallgrades); }
/** * Prints a table containing all manually graded questions * * @param object $quiz Quiz object of the currrent quiz * @param object $course Course object of the current course * @param string $userids Comma-separated list of userids in this course * @return boolean * @todo Look for the TODO in this code to see what still needs to be done **/ function view_questions($quiz) { global $CFG, $QTYPE_MANUAL; $users = get_course_students($quiz->course); if (empty($users)) { print_heading(get_string("noattempts", "quiz")); return true; } // setup the table $table = new stdClass(); $table->head = array(get_string("essayquestions", "quiz"), get_string("ungraded", "quiz")); $table->align = array("left", "left"); $table->wrap = array("wrap", "wrap"); $table->width = "20%"; $table->size = array("*", "*"); $table->data = array(); // get the essay questions $questionlist = quiz_questions_in_quiz($quiz->questions); $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})" . " AND q.qtype IN ({$QTYPE_MANUAL})" . " ORDER BY q.name"; if (empty($questionlist) or !($questions = get_records_sql($sql))) { print_heading(get_string('noessayquestionsfound', 'quiz')); return false; } notify(get_string('essayonly', 'quiz_grading')); // get all the finished attempts by the users $userids = implode(', ', array_keys($users)); if ($attempts = get_records_select('quiz_attempts', "quiz = {$quiz->id} and timefinish > 0 AND userid IN ({$userids}) AND preview = 0", 'userid, attempt')) { foreach ($questions as $question) { $link = "<a href=\"report.php?mode=grading&q={$quiz->id}&action=viewquestion&questionid={$question->id}\">" . $question->name . "</a>"; // determine the number of ungraded attempts $ungraded = 0; foreach ($attempts as $attempt) { if (!$this->is_graded($question, $attempt)) { $ungraded++; } } $table->data[] = array($link, $ungraded); } print_table($table); } else { print_heading(get_string('noattempts', 'quiz')); } return true; }
/** * Display the report. */ function display($quiz, $cm, $course) { global $CFG, $db; // Define some strings $strreallydel = addslashes(get_string('deleteattemptcheck', 'quiz')); $strtimeformat = get_string('strftimedatetime'); $strreviewquestion = get_string('reviewresponse', 'quiz'); $context = get_context_instance(CONTEXT_MODULE, $cm->id); // Only print headers if not asked to download data if (!($download = optional_param('download', NULL))) { $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "overview"); } // Deal with actions $action = optional_param('action', '', PARAM_ACTION); switch ($action) { case 'delete': // Some attempts need to be deleted require_capability('mod/quiz:deleteattempts', $context); $attemptids = optional_param('attemptid', array(), PARAM_INT); foreach ($attemptids as $attemptid) { add_to_log($course->id, 'quiz', 'delete attempt', 'report.php?id=' . $cm->id, $attemptid, $cm->id); quiz_delete_attempt($attemptid, $quiz); } break; } // Set of format options for teacher-created content, for example overall feedback. $nocleanformatoptions = new stdClass(); $nocleanformatoptions->noclean = true; // Prepare list of available actions to perform on attempts - we only want to show the checkbox. // Column on the table if there are options. $attemptactions = array(); if (has_capability('mod/quiz:deleteattempts', $context)) { $attemptactions['delete'] = get_string('delete'); } // Work out some display options - whether there is feedback, and whether scores should be shown. $hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.0E-7 && $quiz->sumgrades > 1.0E-7; $fakeattempt = new stdClass(); $fakeattempt->preview = false; $fakeattempt->timefinish = $quiz->timeopen; $reviewoptions = quiz_get_reviewoptions($quiz, $fakeattempt, $context); $showgrades = $quiz->grade && $quiz->sumgrades && $reviewoptions->scores; // Set table options $noattempts = optional_param('noattempts', 0, PARAM_INT); $detailedmarks = optional_param('detailedmarks', 0, PARAM_INT); $pagesize = optional_param('pagesize', 10, PARAM_INT); $reporturl = $CFG->wwwroot . '/mod/quiz/report.php?mode=overview'; if ($pagesize < 1) { $pagesize = 10; } if (!$reviewoptions->scores) { $detailedmarks = 0; } $reporturlwithoptions = $reporturl . '&id=' . $cm->id . '&noattempts=' . $noattempts . '&detailedmarks=' . $detailedmarks . '&pagesize=' . $pagesize; /// find out current groups mode $currentgroup = groups_get_activity_group($cm, true); if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used if (!$download) { groups_print_activity_menu($cm, $reporturlwithoptions); } } // Print information on the number of existing attempts if (!$download) { //do not print notices when downloading if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, false, $currentgroup)) { echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; } } // Now check if asked download of data if ($download) { $filename = clean_filename("{$course->shortname} " . format_string($quiz->name, true)); $sort = ''; } // Define table columns $tablecolumns = array('picture', 'fullname', 'timestart', 'timefinish', 'duration'); $tableheaders = array('', get_string('name'), get_string('startedon', 'quiz'), get_string('timecompleted', 'quiz'), get_string('attemptduration', 'quiz')); if (!empty($attemptactions)) { array_unshift($tablecolumns, 'checkbox'); array_unshift($tableheaders, NULL); } if ($showgrades) { $tablecolumns[] = 'sumgrades'; $tableheaders[] = get_string('grade', 'quiz') . '/' . $quiz->grade; } if ($detailedmarks) { // we want to display marks for all questions // Start by getting all questions $questionlist = quiz_questions_in_quiz($quiz->questions); $questionids = explode(',', $questionlist); $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))) { error('No questions found'); } $number = 1; foreach ($questionids as $key => $id) { if ($questions[$id]->length) { // Only print questions of non-zero length $tablecolumns[] = '$' . $id; $tableheaders[] = '#' . $number; $questions[$id]->number = $number; $number += $questions[$id]->length; } else { // get rid of zero length questions unset($questions[$id]); unset($questionids[$key]); } } } if ($hasfeedback) { $tablecolumns[] = 'feedbacktext'; $tableheaders[] = get_string('feedback', 'quiz'); } if (!$download) { // Set up the table $table = new flexible_table('mod-quiz-report-overview-report'); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($reporturlwithoptions); $table->sortable(true); $table->collapsible(true); $table->column_suppress('picture'); $table->column_suppress('fullname'); $table->column_class('picture', 'picture'); $table->set_attribute('cellspacing', '0'); $table->set_attribute('id', 'attempts'); $table->set_attribute('class', 'generaltable generalbox'); // Start working -- this is necessary as soon as the niceties are over $table->setup(); } else { if ($download == 'ODS') { require_once "{$CFG->libdir}/odslib.class.php"; $filename .= ".ods"; // Creating a workbook $workbook = new MoodleODSWorkbook("-"); // Sending HTTP headers $workbook->send($filename); // Creating the first worksheet $sheettitle = get_string('reportoverview', 'quiz'); $myxls =& $workbook->add_worksheet($sheettitle); // format types $format =& $workbook->add_format(); $format->set_bold(0); $formatbc =& $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb =& $workbook->add_format(); $formatb->set_bold(1); $formaty =& $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc =& $workbook->add_format(); $formatc->set_align('center'); $formatr =& $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg =& $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Here starts workshhet headers $headers = array(get_string('name'), get_string('startedon', 'quiz'), get_string('timecompleted', 'quiz'), get_string('attemptduration', 'quiz')); if ($showgrades) { $headers[] = get_string('grade', 'quiz') . '/' . $quiz->grade; } if ($detailedmarks) { foreach ($questionids as $id) { $headers[] = '#' . $questions[$id]->number; } } if ($hasfeedback) { $headers[] = get_string('feedback', 'quiz'); } $colnum = 0; foreach ($headers as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($download == 'Excel') { require_once "{$CFG->libdir}/excellib.class.php"; $filename .= ".xls"; // Creating a workbook $workbook = new MoodleExcelWorkbook("-"); // Sending HTTP headers $workbook->send($filename); // Creating the first worksheet $sheettitle = get_string('reportoverview', 'quiz'); $myxls =& $workbook->add_worksheet($sheettitle); // format types $format =& $workbook->add_format(); $format->set_bold(0); $formatbc =& $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb =& $workbook->add_format(); $formatb->set_bold(1); $formaty =& $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc =& $workbook->add_format(); $formatc->set_align('center'); $formatr =& $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg =& $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Here starts workshhet headers $headers = array(get_string('name'), get_string('startedon', 'quiz'), get_string('timecompleted', 'quiz'), get_string('attemptduration', 'quiz')); if ($showgrades) { $headers[] = get_string('grade', 'quiz') . '/' . $quiz->grade; } if ($detailedmarks) { foreach ($questionids as $id) { $headers[] = '#' . $questions[$id]->number; } } if ($hasfeedback) { $headers[] = get_string('feedback', 'quiz'); } $colnum = 0; foreach ($headers as $item) { $myxls->write(0, $colnum, $item, $formatbc); $colnum++; } $rownum = 1; } else { if ($download == 'CSV') { $filename .= ".txt"; header("Content-Type: application/download\n"); header("Content-Disposition: attachment; filename=\"{$filename}\""); header("Expires: 0"); header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); header("Pragma: public"); $headers = get_string('name') . "\t" . get_string('startedon', 'quiz') . "\t" . get_string('timecompleted', 'quiz') . "\t" . get_string('attemptduration', 'quiz'); if ($showgrades) { $headers .= "\t" . get_string('grade', 'quiz') . "/" . $quiz->grade; } if ($detailedmarks) { foreach ($questionids as $id) { $headers .= "\t#" . $questions[$id]->number; } } if ($hasfeedback) { $headers .= "\t" . get_string('feedback', 'quiz'); } echo $headers . " \n"; } } } } $contextlists = get_related_contexts_string(get_context_instance(CONTEXT_COURSE, $course->id)); // Construct the SQL $select = 'SELECT ' . sql_concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')) . ' AS uniqueid, ' . 'qa.uniqueid as attemptuniqueid, qa.id AS attempt, u.id AS userid, u.firstname, u.lastname, u.picture, ' . 'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration '; if ($course->id != SITEID) { // this is too complicated, so just do it for each of the four cases. if (!empty($currentgroup) && empty($noattempts)) { // we want a particular group and we only want to see students WITH attempts. // So join on groups_members and do an inner join on attempts. $from = 'FROM ' . $CFG->prefix . 'user u JOIN ' . $CFG->prefix . 'role_assignments ra ON ra.userid = u.id ' . 'JOIN ' . $CFG->prefix . 'groups_members gm ON u.id = gm.userid ' . 'JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = ' . $quiz->id; $where = ' WHERE ra.contextid ' . $contextlists . ' AND gm.groupid = ' . $currentgroup . ' AND qa.preview = 0'; } else { if (!empty($currentgroup) && !empty($noattempts)) { // We want a particular group and we want to do something funky with attempts // So join on groups_members and left join on attempts... $from = 'FROM ' . $CFG->prefix . 'user u JOIN ' . $CFG->prefix . 'role_assignments ra ON ra.userid = u.id ' . 'JOIN ' . $CFG->prefix . 'groups_members gm ON u.id = gm.userid ' . 'LEFT JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = ' . $quiz->id; $where = ' WHERE ra.contextid ' . $contextlists . ' AND gm.groupid = ' . $currentgroup; if ($noattempts == 1) { // noattempts = 1 means only no attempts, so make the left join ask for only records where the right is null (no attempts) $where .= ' AND qa.userid IS NULL'; // show ONLY no attempts; } else { // We are including attempts, so exclude previews. $where .= ' AND qa.preview = 0'; } } else { if (empty($currentgroup)) { // We don't care about group, and we to do something funky with attempts // So do a left join on attempts $from = 'FROM ' . $CFG->prefix . 'user u JOIN ' . $CFG->prefix . 'role_assignments ra ON ra.userid = u.id LEFT JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = ' . $quiz->id; $where = " WHERE ra.contextid {$contextlists}"; if (empty($noattempts)) { $where .= ' AND qa.userid IS NOT NULL AND qa.preview = 0'; // show ONLY students with attempts; } else { if ($noattempts == 1) { // noattempts = 1 means only no attempts, so make the left join ask for only records where the right is null (no attempts) $where .= ' AND qa.userid IS NULL'; // show ONLY students without attempts; } else { if ($noattempts == 3) { // we want all attempts $from = 'FROM ' . $CFG->prefix . 'user u JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid '; $where = ' WHERE qa.quiz = ' . $quiz->id . ' AND qa.preview = 0'; } } } // noattempts = 2 means we want all students, with or without attempts } } } $countsql = 'SELECT COUNT(DISTINCT(' . sql_concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')) . ')) ' . $from . $where; } else { if (empty($noattempts)) { $from = 'FROM ' . $CFG->prefix . 'user u JOIN ' . $CFG->prefix . 'quiz_attempts qa ON u.id = qa.userid '; $where = ' WHERE qa.quiz = ' . $quiz->id . ' AND qa.preview = 0'; $countsql = 'SELECT COUNT(DISTINCT(' . sql_concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')) . ')) ' . $from . $where; } } if (!$download) { // Add extra limits due to initials bar if ($table->get_sql_where()) { $where .= ' AND ' . $table->get_sql_where(); } // Count the records NOW, before funky question grade sorting messes up $from if (!empty($countsql)) { $totalinitials = count_records_sql($countsql); if ($table->get_sql_where()) { $countsql .= ' AND ' . $table->get_sql_where(); } $total = count_records_sql($countsql); } // Add extra limits due to sorting by question grade if ($sort = $table->get_sql_sort()) { $sortparts = explode(',', $sort); $newsort = array(); $questionsort = false; foreach ($sortparts as $sortpart) { $sortpart = trim($sortpart); if (substr($sortpart, 0, 1) == '$') { if (!$questionsort) { $qid = intval(substr($sortpart, 1)); $select .= ', grade '; $from .= ' LEFT JOIN ' . $CFG->prefix . 'question_sessions qns ON qns.attemptid = qa.id ' . 'LEFT JOIN ' . $CFG->prefix . 'question_states qs ON qs.id = qns.newgraded '; $where .= ' AND (' . sql_isnull('qns.questionid') . ' OR qns.questionid = ' . $qid . ')'; $newsort[] = 'grade ' . (strpos($sortpart, 'ASC') ? 'ASC' : 'DESC'); $questionsort = true; } } else { $newsort[] = $sortpart; } } // Reconstruct the sort string $sort = ' ORDER BY ' . implode(', ', $newsort); } // Fix some wired sorting if (empty($sort)) { $sort = ' ORDER BY uniqueid'; } $table->pagesize($pagesize, $total); } // If there is feedback, include it in the query. if ($hasfeedback) { $factor = $quiz->grade / $quiz->sumgrades; $select .= ', qf.feedbacktext '; $from .= " JOIN {$CFG->prefix}quiz_feedback qf ON " . "qf.quizid = {$quiz->id} AND qf.mingrade <= qa.sumgrades * {$factor} AND qa.sumgrades * {$factor} < qf.maxgrade"; } // Fetch the attempts if (!empty($from)) { // if we're in the site course and displaying no attempts, it makes no sense to do the query. if (!$download) { $attempts = get_records_sql($select . $from . $where . $sort, $table->get_page_start(), $table->get_page_size()); } else { $attempts = get_records_sql($select . $from . $where . $sort); } } else { $attempts = array(); } // Build table rows if (!$download) { $table->initialbars($totalinitials > 20); } if (!empty($attempts) || !empty($noattempts)) { if ($attempts) { foreach ($attempts as $attempt) { $picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true); // uncomment the commented lines below if you are choosing to show unenrolled users and // have uncommented the corresponding lines earlier in this script //if (in_array($attempt->userid, $unenrolledusers)) { // $userlink = '<a class="dimmed" href="'.$CFG->wwwroot.'/user/view.php?id='. // $attempt->userid.'&course='.$course->id.'">'.fullname($attempt).'</a>'; //} //else { $userlink = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $attempt->userid . '&course=' . $course->id . '">' . fullname($attempt) . '</a>'; //} // Username columns. $row = array(); if (!$download) { if (!empty($attemptactions)) { $row[] = '<input type="checkbox" name="attemptid[]" value="' . $attempt->attempt . '" />'; } $row[] = $picture; $row[] = $userlink; } else { $row[] = fullname($attempt); } // Timing columns. if ($attempt->attempt) { $startdate = userdate($attempt->timestart, $strtimeformat); if (!$download) { $row[] = '<a href="review.php?q=' . $quiz->id . '&attempt=' . $attempt->attempt . '">' . $startdate . '</a>'; } else { $row[] = $startdate; } if ($attempt->timefinish) { $timefinish = userdate($attempt->timefinish, $strtimeformat); $duration = format_time($attempt->duration); if (!$download) { $row[] = '<a href="review.php?q=' . $quiz->id . '&attempt=' . $attempt->attempt . '">' . $startdate . '</a>'; } else { $row[] = '<a href="review.php?q=' . $quiz->id . '&attempt=' . $attempt->attempt . '">' . $timefinish . '</a>'; } $row[] = $duration; } else { $row[] = '-'; $row[] = get_string('unfinished', 'quiz'); } } else { $row[] = '-'; $row[] = '-'; $row[] = '-'; } // Grades columns. if ($showgrades) { if ($attempt->timefinish) { $grade = quiz_rescale_grade($attempt->sumgrades, $quiz); if (!$download) { $row[] = '<a href="review.php?q=' . $quiz->id . '&attempt=' . $attempt->attempt . '">' . $grade . '</a>'; } else { $row[] = $grade; } } else { $row[] = '-'; } } if ($detailedmarks) { if (empty($attempt->attempt)) { foreach ($questionids as $questionid) { $row[] = '-'; } } else { foreach ($questionids as $questionid) { $gradedstateid = get_field('question_sessions', 'newgraded', 'attemptid', $attempt->attemptuniqueid, 'questionid', $questionid); if ($gradedstateid) { $grade = round(get_field('question_states', 'grade', 'id', $gradedstateid), $quiz->decimalpoints); } else { $grade = '--'; } if (!$download) { $row[] = link_to_popup_window('/mod/quiz/reviewquestion.php?state=' . $gradedstateid . '&number=' . $questions[$questionid]->number, 'reviewquestion', $grade, 450, 650, $strreviewquestion, 'none', true); } else { $row[] = $grade; } } } } // Feedback column. if ($hasfeedback) { if ($attempt->timefinish) { $row[] = format_text($attempt->feedbacktext, FORMAT_MOODLE, $nocleanformatoptions); } else { $row[] = '-'; } } if (!$download) { $table->add_data($row); } else { if ($download == 'Excel' or $download == 'ODS') { $colnum = 0; foreach ($row as $item) { $myxls->write($rownum, $colnum, $item, $format); $colnum++; } $rownum++; } else { if ($download == 'CSV') { $text = implode("\t", $row); echo $text . " \n"; } } } } } if (!$download) { // Start form echo '<div id="tablecontainer">'; echo '<form id="attemptsform" method="post" action="' . $reporturlwithoptions . '" onsubmit="var menu = document.getElementById(\'menuaction\'); ' . 'return (menu.options[menu.selectedIndex].value == \'delete\' ? confirm(\'' . $strreallydel . '\') : true);">'; echo '<div>'; // Print table $table->print_html(); // Print "Select all" etc. if (!empty($attempts) && !empty($attemptactions)) { echo '<table id="commands">'; echo '<tr><td>'; echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">' . get_string('selectall', 'quiz') . '</a> / '; echo '<a href="javascript:deselect_all_in(\'DIV\',null,\'tablecontainer\');">' . get_string('selectnone', 'quiz') . '</a> '; echo ' '; choose_from_menu($attemptactions, 'action', '', get_string('withselected', 'quiz'), 'if(this.selectedIndex > 0) submitFormById(\'attemptsform\');'); echo '<noscript id="noscriptmenuaction" style="display: inline;"><div>'; echo '<input type="submit" value="' . get_string('go') . '" /></div></noscript>'; echo '<script type="text/javascript"> <!-- document.getElementById("noscriptmenuaction").style.display = "none"; --> </script>'; echo '</td></tr></table>'; } // Close form echo '</div>'; echo '</form></div>'; if (!empty($attempts)) { echo '<table class="boxaligncenter"><tr>'; $options = array(); $options["id"] = $cm->id; $options["q"] = $quiz->id; $options['sesskey'] = sesskey(); $options["noheader"] = "yes"; $options['noattempts'] = $noattempts; $options['detailedmarks'] = $detailedmarks; echo '<td>'; $options["download"] = "ODS"; print_single_button($reporturl, $options, get_string("downloadods")); echo "</td>\n"; echo '<td>'; $options["download"] = "Excel"; print_single_button($reporturl, $options, get_string("downloadexcel")); echo "</td>\n"; echo '<td>'; $options["download"] = "CSV"; print_single_button($reporturl, $options, get_string("downloadtext")); echo "</td>\n"; echo "<td>"; helpbutton('overviewdownload', get_string('overviewdownload', 'quiz_overview'), 'quiz'); echo "</td>\n"; echo '</tr></table>'; } } else { if ($download == 'Excel' or $download == 'ODS') { $workbook->close(); exit; } else { if ($download == 'CSV') { exit; } } } } else { if (!$download) { $table->print_html(); } } // Print display options echo '<div class="controls">'; echo '<form id="options" action="' . $reporturl . '" method="get">'; echo '<div>'; echo '<p>' . get_string('displayoptions', 'quiz') . ': </p>'; echo '<input type="hidden" name="id" value="' . $cm->id . '" />'; echo '<input type="hidden" name="q" value="' . $quiz->id . '" />'; echo '<input type="hidden" name="noattempts" value="0" />'; echo '<input type="hidden" name="detailedmarks" value="0" />'; echo '<table id="overview-options" class="boxaligncenter">'; echo '<tr align="left">'; echo '<td><label for="pagesize">' . get_string('pagesize', 'quiz') . '</label></td>'; echo '<td><input type="text" id="pagesize" name="pagesize" size="3" value="' . $pagesize . '" /></td>'; echo '</tr>'; echo '<tr align="left">'; echo '<td colspan="2">'; $options = array(0 => get_string('attemptsonly', 'quiz_overview', $course->students)); if ($course->id != SITEID) { $options[1] = get_string('noattemptsonly', 'quiz_overview', $course->students); $options[2] = get_string('allstudents', 'quiz_overview', $course->students); $options[3] = get_string('allattempts', 'quiz_overview'); } choose_from_menu($options, 'noattempts', $noattempts, ''); echo '</td></tr>'; echo '<tr align="left">'; echo '<td colspan="2">'; echo '<input type="checkbox" id="checkdetailedmarks" name="detailedmarks" ' . ($detailedmarks ? 'checked="checked" ' : '') . 'value="1" /> '; echo '<label for="checkdetailedmarks">' . get_string('showdetailedmarks', 'quiz') . '</label> '; echo '</td></tr>'; echo '<tr><td colspan="2" align="center">'; echo '<input type="submit" value="' . get_string('go') . '" />'; echo '</td></tr></table>'; echo '</div>'; echo '</form>'; echo '</div>'; echo "\n"; return true; }
/** * Constructor, assuming we already have the necessary data loaded. * * @param object $quiz the row from the quiz table. * @param object $cm the course_module object for this quiz. * @param object $course the row from the course table for the course we belong to. * @param bool $getcontext intended for testing - stops the constructor getting the context. */ public function __construct($quiz, $cm, $course, $getcontext = true) { $this->quiz = $quiz; $this->cm = $cm; $this->quiz->cmid = $this->cm->id; $this->course = $course; if ($getcontext && !empty($cm->id)) { $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); } $this->questionids = explode(',', quiz_questions_in_quiz($this->quiz->questions)); }
/** * Load the question data necessary in the reports. * - Remove description questions. * - Order questions in order that they are in the quiz * - Add question numbers. * - Add grade from quiz_questions_instance */ function quiz_report_load_questions($quiz) { global $CFG, $DB; $questionlist = quiz_questions_in_quiz($quiz->questions); //In fact in most cases the id IN $questionlist below is redundant //since we are also doing a JOIN on the qqi table. But will leave it in //since this double check will probably do no harm. list($usql, $params) = $DB->get_in_or_equal(explode(',', $questionlist)); $params[] = $quiz->id; if (!($questions = $DB->get_records_sql("SELECT q.*, qqi.grade AS maxgrade\n FROM {question} q,\n {quiz_question_instances} qqi\n WHERE q.id {$usql} AND\n qqi.question = q.id AND\n qqi.quiz = ?", $params))) { print_error('noquestionsfound', 'quiz'); } //Now we have an array of questions from a quiz we work out there question nos and remove //questions with zero length ie. description questions etc. //also put questions in order. $number = 1; $realquestions = array(); $questionids = explode(',', $questionlist); foreach ($questionids as $id) { if ($questions[$id]->length) { // Ignore questions of zero length $realquestions[$id] = $questions[$id]; $realquestions[$id]->number = $number; $number += $questions[$id]->length; } } return $realquestions; }
/** * Get the slot for a question with a particular id. * @param object $quiz the quiz settings. * @param int $questionid the of a question in the quiz. * @return int the corresponding slot. Null if the question is not in the quiz. */ function quiz_get_slot_for_question($quiz, $questionid) { $questionids = quiz_questions_in_quiz($quiz->questions); foreach (explode(',', $questionids) as $key => $id) { if ($id == $questionid) { return $key + 1; } } return null; }
/** * Upgrade states for an attempt to Moodle 1.5 model * * Any state that does not yet have its timestamp set to nonzero has not yet been upgraded from Moodle 1.4 * The reason these are still around is that for large sites it would have taken too long to * upgrade all states at once. This function sets the timestamp field and creates an entry in the * question_sessions table. * @param object $attempt The attempt whose states need upgrading */ function quiz_upgrade_states($attempt) { global $DB; global $CFG; // The old quiz model only allowed a single response per quiz attempt so that there will be // only one state record per question for this attempt. // We set the timestamp of all states to the timemodified field of the attempt. $DB->execute("UPDATE {question_states} SET timestamp = ? WHERE attempt = ?", array($attempt->timemodified, $attempt->uniqueid)); // For each state we create an entry in the question_sessions table, with both newest and // newgraded pointing to this state. // Actually we only do this for states whose question is actually listed in $attempt->layout. // We do not do it for states associated to wrapped questions like for example the questions // used by a RANDOM question $session = new stdClass(); $session->attemptid = $attempt->uniqueid; $questionlist = quiz_questions_in_quiz($attempt->layout); $params = array($attempt->uniqueid); list($usql, $question_params) = $DB->get_in_or_equal(explode(',', $questionlist)); $params = array_merge($params, $question_params); if ($questionlist and $states = $DB->get_records_select('question_states', "attempt = ? AND question {$usql}", $params)) { foreach ($states as $state) { $session->newgraded = $state->id; $session->newest = $state->id; $session->questionid = $state->question; $DB->insert_record('question_sessions', $session, false); } } }
/** * Constructor, assuming we already have the necessary data loaded. * * @param object $quiz the row from the quiz table. * @param object $cm the course_module object for this quiz. * @param object $course the row from the course table for the course we belong to. * @param bool $getcontext intended for testing - stops the constructor getting the context. */ public function __construct($quiz, $cm, $course, $getcontext = true) { $this->quiz = $quiz; $this->cm = $cm; $this->quiz->cmid = $this->cm->id; $this->course = $course; if ($getcontext && !empty($cm->id)) { $this->context = context_module::instance($cm->id); } $questionids = quiz_questions_in_quiz($this->quiz->questions); if ($questionids) { $this->questionids = explode(',', quiz_questions_in_quiz($this->quiz->questions)); } else { $this->questionids = array(); // Which idiot made explode(',', '') = array('')? } }
public function display($quiz, $cm, $course) { global $CFG, $COURSE, $DB, $PAGE, $OUTPUT; $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); $download = optional_param('download', '', PARAM_ALPHA); list($currentgroup, $students, $groupstudents, $allowed) = $this->load_relevant_students($cm); $pageoptions = array(); $pageoptions['id'] = $cm->id; $pageoptions['mode'] = 'responses'; $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions); $qmsubselect = quiz_report_qm_filter_select($quiz); $mform = new mod_quiz_report_responses_settings($reporturl, array('qmsubselect' => $qmsubselect, 'quiz' => $quiz, 'currentgroup' => $currentgroup, 'context' => $this->context)); if ($fromform = $mform->get_data()) { $attemptsmode = $fromform->attemptsmode; if ($qmsubselect) { $qmfilter = $fromform->qmfilter; } else { $qmfilter = 0; } set_user_preference('quiz_report_responses_qtext', $fromform->qtext); set_user_preference('quiz_report_responses_resp', $fromform->resp); set_user_preference('quiz_report_responses_right', $fromform->right); set_user_preference('quiz_report_pagesize', $fromform->pagesize); $includeqtext = $fromform->qtext; $includeresp = $fromform->resp; $includeright = $fromform->right; $pagesize = $fromform->pagesize; } else { $attemptsmode = optional_param('attemptsmode', null, PARAM_INT); if ($qmsubselect) { $qmfilter = optional_param('qmfilter', 0, PARAM_INT); } else { $qmfilter = 0; } $includeqtext = get_user_preferences('quiz_report_responses_qtext', 0); $includeresp = get_user_preferences('quiz_report_responses_resp', 1); $includeright = get_user_preferences('quiz_report_responses_right', 0); $pagesize = get_user_preferences('quiz_report_pagesize', 0); } $this->validate_common_options($attemptsmode, $pagesize, $course, $currentgroup); if (!$includeqtext && !$includeresp && !$includeright) { $includeresp = 1; set_user_preference('quiz_report_responses_resp', 1); } // We only want to show the checkbox to delete attempts // if the user has permissions and if the report mode is showing attempts. $candelete = has_capability('mod/quiz:deleteattempts', $this->context) && $attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO; $displayoptions = array(); $displayoptions['attemptsmode'] = $attemptsmode; $displayoptions['qmfilter'] = $qmfilter; $displayoptions['qtext'] = $includeqtext; $displayoptions['resp'] = $includeresp; $displayoptions['right'] = $includeright; $mform->set_data($displayoptions + array('pagesize' => $pagesize)); if ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) { // This option is only available to users who can access all groups in // groups mode, so setting allowed to empty (which means all quiz attempts // are accessible, is not a security porblem. $allowed = array(); } $attemptids = optional_param_array('attemptid', array(), PARAM_INT); if ($attemptids && confirm_sesskey()) { require_capability('mod/quiz:deleteattempts', $this->context); $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed); redirect($reporturl->out(false, $displayoptions)); } // Load the required questions. $questions = quiz_report_get_significant_questions($quiz); $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); $displaycoursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id); $displaycourseshortname = format_string($COURSE->shortname, true, array('context' => $displaycoursecontext)); $table = new quiz_report_responses_table($quiz, $this->context, $qmsubselect, $groupstudents, $students, $questions, $candelete, $reporturl, $displayoptions); $filename = quiz_report_download_filename(get_string('responsesfilename', 'quiz_responses'), $courseshortname, $quiz->name); $table->is_downloading($download, $filename, $displaycourseshortname . ' ' . format_string($quiz->name, true)); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); } if (!$table->is_downloading()) { // Only print headers if not asked to download data $this->print_header_and_tabs($cm, $course, $quiz, 'responses'); } if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used if (!$table->is_downloading()) { groups_print_activity_menu($cm, $reporturl->out(true, $displayoptions)); } } // Print information on the number of existing attempts if (!$table->is_downloading()) { //do not print notices when downloading if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; } } $hasquestions = quiz_questions_in_quiz($quiz->questions); if (!$table->is_downloading()) { if (!$hasquestions) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else { if (!$students) { echo $OUTPUT->notification(get_string('nostudentsyet')); } else { if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } } } // Print display options $mform->display(); } $hasstudents = $students && (!$currentgroup || $groupstudents); if ($hasquestions && ($hasstudents || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) { // Print information on the grading method and whether we are displaying if (!$table->is_downloading()) { //do not print notices when downloading if ($strattempthighlight = quiz_report_highlighting_grading_method($quiz, $qmsubselect, $qmfilter)) { echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>'; } } list($fields, $from, $where, $params) = $this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed); $table->set_count_sql("SELECT COUNT(1) FROM {$from} WHERE {$where}", $params); $table->set_sql($fields, $from, $where, $params); // Define table columns $columns = array(); $headers = array(); if (!$table->is_downloading() && $candelete) { $columns[] = 'checkbox'; $headers[] = null; } $this->add_user_columns($table, $columns, $headers); if ($table->is_downloading()) { $this->add_time_columns($columns, $headers); } $this->add_grade_columns($quiz, $columns, $headers); foreach ($questions as $id => $question) { if ($displayoptions['qtext']) { $columns[] = 'question' . $id; $headers[] = get_string('questionx', 'question', $question->number); } if ($displayoptions['resp']) { $columns[] = 'response' . $id; $headers[] = get_string('responsex', 'quiz_responses', $question->number); } if ($displayoptions['right']) { $columns[] = 'right' . $id; $headers[] = get_string('rightanswerx', 'quiz_responses', $question->number); } } $table->define_columns($columns); $table->define_headers($headers); $table->sortable(true, 'uniqueid'); // Set up the table $table->define_baseurl($reporturl->out(true, $displayoptions)); $this->configure_user_columns($table); $table->no_sorting('feedbacktext'); $table->column_class('sumgrades', 'bold'); $table->set_attribute('id', 'attempts'); $table->collapsible(true); $table->out($pagesize, true); } return true; }
public function display($quiz, $cm, $course) { global $CFG, $DB, $OUTPUT; list($currentgroup, $students, $groupstudents, $allowed) = $this->init('responses', 'quiz_responses_settings_form', $quiz, $cm, $course); $options = new quiz_responses_options('responses', $quiz, $cm, $course); if ($fromform = $this->form->get_data()) { $options->process_settings_from_form($fromform); } else { $options->process_settings_from_params(); } $this->form->set_data($options->get_initial_form_data()); if ($options->attempts == self::ALL_WITH) { // This option is only available to users who can access all groups in // groups mode, so setting allowed to empty (which means all quiz attempts // are accessible, is not a security porblem. $allowed = array(); } // Load the required questions. $questions = quiz_report_get_significant_questions($quiz); // Prepare for downloading, if applicable. $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); $table = new quiz_responses_table($quiz, $this->context, $this->qmsubselect, $options, $groupstudents, $students, $questions, $this->get_base_url()); $filename = quiz_report_download_filename(get_string('responsesfilename', 'quiz_responses'), $courseshortname, $quiz->name); $table->is_downloading($options->download, $filename, $courseshortname . ' ' . format_string($quiz->name, true)); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); } $this->process_actions($quiz, $cm, $currentgroup, $groupstudents, $allowed, $options->get_url()); // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. $this->print_header_and_tabs($cm, $course, $quiz, $this->mode); } if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used, so output the group selector if we are not downloading. if (!$table->is_downloading()) { groups_print_activity_menu($cm, $options->get_url()); } } // Print information on the number of existing attempts. if (!$table->is_downloading()) { // Do not print notices when downloading. if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; } } $hasquestions = quiz_questions_in_quiz($quiz->questions); if (!$table->is_downloading()) { if (!$hasquestions) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else if (!$students) { echo $OUTPUT->notification(get_string('nostudentsyet')); } else if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } // Print the display options. $this->form->display(); } $hasstudents = $students && (!$currentgroup || $groupstudents); if ($hasquestions && ($hasstudents || $options->attempts == self::ALL_WITH)) { list($fields, $from, $where, $params) = $table->base_sql($allowed); $table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params); $table->set_sql($fields, $from, $where, $params); if (!$table->is_downloading()) { // Print information on the grading method. if ($strattempthighlight = quiz_report_highlighting_grading_method( $quiz, $this->qmsubselect, $options->onlygraded)) { echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>'; } } // Define table columns. $columns = array(); $headers = array(); if (!$table->is_downloading() && $options->checkboxcolumn) { $columns[] = 'checkbox'; $headers[] = null; } $this->add_user_columns($table, $columns, $headers); $this->add_state_column($columns, $headers); if ($table->is_downloading()) { $this->add_time_columns($columns, $headers); } $this->add_grade_columns($quiz, $options->usercanseegrades, $columns, $headers); foreach ($questions as $id => $question) { if ($options->showqtext) { $columns[] = 'question' . $id; $headers[] = get_string('questionx', 'question', $question->number); } if ($options->showresponses) { $columns[] = 'response' . $id; $headers[] = get_string('responsex', 'quiz_responses', $question->number); } if ($options->showright) { $columns[] = 'right' . $id; $headers[] = get_string('rightanswerx', 'quiz_responses', $question->number); } } $table->define_columns($columns); $table->define_headers($headers); $table->sortable(true, 'uniqueid'); // Set up the table. $table->define_baseurl($options->get_url()); $this->configure_user_columns($table); $table->no_sorting('feedbacktext'); $table->column_class('sumgrades', 'bold'); $table->set_attribute('id', 'attempts'); $table->collapsible(true); $table->out($options->pagesize, true); } return true; }
function display($quiz, $cm, $course) { global $CFG; // Print header $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "regrade"); // Check permissions $context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $context)) { notify(get_string('regradenotallowed', 'quiz')); return true; } // Fetch all attempts if (!($attempts = get_records_select('quiz_attempts', "quiz = '{$quiz->id}' AND preview = 0"))) { print_heading(get_string('noattempts', 'quiz')); return true; } // Fetch all questions $sql = "SELECT q.*, i.grade AS maxgrade FROM {$CFG->prefix}question q,\n {$CFG->prefix}quiz_question_instances i\n WHERE i.quiz = {$quiz->id}\n AND i.question = q.id"; if (!($questions = get_records_sql($sql))) { error("Failed to get questions for regrading!"); } get_question_options($questions); // Print heading print_heading(get_string('regradingquiz', 'quiz', format_string($quiz->name))); echo '<div class="boxaligncenter">'; print_string('regradedisplayexplanation', 'quiz'); echo '</div>'; // Loop through all questions and all attempts and regrade while printing progress info foreach ($questions as $question) { echo '<strong>' . get_string('regradingquestion', 'quiz', $question->name) . '</strong> ' . get_string('attempts', 'quiz') . ": \n"; foreach ($attempts as $attempt) { set_time_limit(30); $changed = regrade_question_in_attempt($question, $attempt, $quiz, true); if ($changed) { link_to_popup_window('/mod/quiz/reviewquestion.php?attempt=' . $attempt->id . '&question=' . $question->id, 'reviewquestion', ' #' . $attempt->id, 450, 550, get_string('reviewresponse', 'quiz')); } else { echo ' #' . $attempt->id; } } echo '<br />'; // the following makes sure that the output is sent immediately. @flush(); @ob_flush(); } // Loop through all questions and recalculate $attempt->sumgrade $attemptschanged = 0; foreach ($attempts as $attempt) { $sumgrades = 0; $questionids = explode(',', quiz_questions_in_quiz($attempt->layout)); foreach ($questionids as $questionid) { $lastgradedid = get_field('question_sessions', 'newgraded', 'attemptid', $attempt->uniqueid, 'questionid', $questionid); $sumgrades += get_field('question_states', 'grade', 'id', $lastgradedid); } if ($attempt->sumgrades != $sumgrades) { $attemptschanged++; set_field('quiz_attempts', 'sumgrades', $sumgrades, 'id', $attempt->id); } } // Update the overall quiz grades if ($grades = get_records('quiz_grades', 'quiz', $quiz->id)) { foreach ($grades as $grade) { quiz_save_best_grade($quiz, $grade->userid); } } return true; }
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; }
public function display($quiz, $cm, $course) { global $CFG, $DB, $OUTPUT, $PAGE; list($currentgroup, $students, $groupstudents, $allowed) = $this->init('overview', 'quiz_overview_settings_form', $quiz, $cm, $course); $options = new quiz_overview_options('overview', $quiz, $cm, $course); if ($fromform = $this->form->get_data()) { $options->process_settings_from_form($fromform); } else { $options->process_settings_from_params(); } $this->form->set_data($options->get_initial_form_data()); if ($options->attempts == self::ALL_WITH) { // This option is only available to users who can access all groups in // groups mode, so setting allowed to empty (which means all quiz attempts // are accessible, is not a security porblem. $allowed = array(); } // Load the required questions. $questions = quiz_report_get_significant_questions($quiz); // Prepare for downloading, if applicable. $courseshortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id))); $table = new quiz_overview_table($quiz, $this->context, $this->qmsubselect, $options, $groupstudents, $students, $questions, $options->get_url()); $filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'), $courseshortname, $quiz->name); $table->is_downloading($options->download, $filename, $courseshortname . ' ' . format_string($quiz->name, true)); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); } $this->course = $course; // Hack to make this available in process_actions. $this->process_actions($quiz, $cm, $currentgroup, $groupstudents, $allowed, $options->get_url()); // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. $this->print_header_and_tabs($cm, $course, $quiz, $this->mode); } if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used, so output the group selector if we are not downloading. if (!$table->is_downloading()) { groups_print_activity_menu($cm, $options->get_url()); } } // Print information on the number of existing attempts. if (!$table->is_downloading()) { // Do not print notices when downloading. if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; } } $hasquestions = quiz_questions_in_quiz($quiz->questions); if (!$table->is_downloading()) { if (!$hasquestions) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else { if (!$students) { echo $OUTPUT->notification(get_string('nostudentsyet')); } else { if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } } } // Print the display options. $this->form->display(); } $hasstudents = $students && (!$currentgroup || $groupstudents); if ($hasquestions && ($hasstudents || $options->attempts == self::ALL_WITH)) { // Construct the SQL. $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid, '; if ($this->qmsubselect) { $fields .= "(CASE " . " WHEN {$this->qmsubselect} THEN 1" . " ELSE 0 " . "END) AS gradedattempt, "; } list($fields, $from, $where, $params) = $table->base_sql($allowed); $table->set_count_sql("SELECT COUNT(1) FROM {$from} WHERE {$where}", $params); // Test to see if there are any regraded attempts to be listed. $fields .= ", COALESCE((\n SELECT MAX(qqr.regraded)\n FROM {quiz_overview_regrades} qqr\n WHERE qqr.questionusageid = quiza.uniqueid\n ), -1) AS regraded"; if ($options->onlyregraded) { $where .= " AND COALESCE((\n SELECT MAX(qqr.regraded)\n FROM {quiz_overview_regrades} qqr\n WHERE qqr.questionusageid = quiza.uniqueid\n ), -1) <> -1"; } $table->set_sql($fields, $from, $where, $params); if (!$table->is_downloading()) { // Output the regrade buttons. if (has_capability('mod/quiz:regrade', $this->context)) { $regradesneeded = $this->count_question_attempts_needing_regrade($quiz, $groupstudents); if ($currentgroup) { $a = new stdClass(); $a->groupname = groups_get_group_name($currentgroup); $a->coursestudents = get_string('participants'); $a->countregradeneeded = $regradesneeded; $regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a); $regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a); $regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a); } else { $regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $regradesneeded); $regradealldrylabel = get_string('regradealldry', 'quiz_overview'); $regradealllabel = get_string('regradeall', 'quiz_overview'); } $displayurl = new moodle_url($options->get_url(), array('sesskey' => sesskey())); echo '<div class="mdl-align">'; echo '<form action="' . $displayurl->out_omit_querystring() . '">'; echo '<div>'; echo html_writer::input_hidden_params($displayurl); echo '<input type="submit" name="regradeall" value="' . $regradealllabel . '"/>'; echo '<input type="submit" name="regradealldry" value="' . $regradealldrylabel . '"/>'; if ($regradesneeded) { echo '<input type="submit" name="regradealldrydo" value="' . $regradealldrydolabel . '"/>'; } echo '</div>'; echo '</form>'; echo '</div>'; } // Print information on the grading method. if ($strattempthighlight = quiz_report_highlighting_grading_method($quiz, $this->qmsubselect, $options->onlygraded)) { echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>'; } } // Define table columns. $columns = array(); $headers = array(); if (!$table->is_downloading() && $options->checkboxcolumn) { $columns[] = 'checkbox'; $headers[] = null; } $this->add_user_columns($table, $columns, $headers); $this->add_state_column($columns, $headers); $this->add_time_columns($columns, $headers); $this->add_grade_columns($quiz, $options->usercanseegrades, $columns, $headers, false); if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) && $this->has_regraded_questions($from, $where, $params)) { $columns[] = 'regraded'; $headers[] = get_string('regrade', 'quiz_overview'); } if ($options->slotmarks) { foreach ($questions as $slot => $question) { // Ignore questions of zero length. $columns[] = 'qsgrade' . $slot; $header = get_string('qbrief', 'quiz', $question->number); if (!$table->is_downloading()) { $header .= '<br />'; } else { $header .= ' '; } $header .= '/' . quiz_rescale_grade($question->maxmark, $quiz, 'question'); $headers[] = $header; } } $this->set_up_table_columns($table, $columns, $headers, $this->get_base_url(), $options, false); $table->set_attribute('class', 'generaltable generalbox grades'); $table->out($options->pagesize, true); } if (!$table->is_downloading() && $options->usercanseegrades) { $output = $PAGE->get_renderer('mod_quiz'); if ($currentgroup && $groupstudents) { list($usql, $params) = $DB->get_in_or_equal($groupstudents); $params[] = $quiz->id; if ($DB->record_exists_select('quiz_grades', "userid {$usql} AND quiz = ?", $params)) { $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php', array('id' => $quiz->id, 'groupid' => $currentgroup)); $graphname = get_string('overviewreportgraphgroup', 'quiz_overview', groups_get_group_name($currentgroup)); echo $output->graph($imageurl, $graphname); } } if ($DB->record_exists('quiz_grades', array('quiz' => $quiz->id))) { $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php', array('id' => $quiz->id)); $graphname = get_string('overviewreportgraph', 'quiz_overview'); echo $output->graph($imageurl, $graphname); } } return true; }
public function display($quiz, $cm, $course) { global $CFG, $COURSE, $DB, $OUTPUT; $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); $download = optional_param('download', '', PARAM_ALPHA); list($currentgroup, $students, $groupstudents, $allowed) = $this->load_relevant_students($cm); $pageoptions = array(); $pageoptions['id'] = $cm->id; $pageoptions['mode'] = 'overview'; $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions); $qmsubselect = quiz_report_qm_filter_select($quiz); $mform = new mod_quiz_report_overview_settings($reporturl, array('qmsubselect' => $qmsubselect, 'quiz' => $quiz, 'currentgroup' => $currentgroup, 'context' => $this->context)); if ($fromform = $mform->get_data()) { $regradeall = false; $regradealldry = false; $regradealldrydo = false; $attemptsmode = $fromform->attemptsmode; if ($qmsubselect) { $qmfilter = $fromform->qmfilter; } else { $qmfilter = 0; } $regradefilter = !empty($fromform->regradefilter); set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks); set_user_preference('quiz_report_pagesize', $fromform->pagesize); $detailedmarks = $fromform->detailedmarks; $pagesize = $fromform->pagesize; } else { $regradeall = optional_param('regradeall', 0, PARAM_BOOL); $regradealldry = optional_param('regradealldry', 0, PARAM_BOOL); $regradealldrydo = optional_param('regradealldrydo', 0, PARAM_BOOL); $attemptsmode = optional_param('attemptsmode', null, PARAM_INT); if ($qmsubselect) { $qmfilter = optional_param('qmfilter', 0, PARAM_INT); } else { $qmfilter = 0; } $regradefilter = optional_param('regradefilter', 0, PARAM_INT); $detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1); $pagesize = get_user_preferences('quiz_report_pagesize', 0); } $this->validate_common_options($attemptsmode, $pagesize, $course, $currentgroup); $displayoptions = array(); $displayoptions['attemptsmode'] = $attemptsmode; $displayoptions['qmfilter'] = $qmfilter; $displayoptions['regradefilter'] = $regradefilter; $mform->set_data($displayoptions + array('detailedmarks' => $detailedmarks, 'pagesize' => $pagesize)); if (!$this->should_show_grades($quiz)) { $detailedmarks = 0; } // We only want to show the checkbox to delete attempts // if the user has permissions and if the report mode is showing attempts. $candelete = has_capability('mod/quiz:deleteattempts', $this->context) && $attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO; if ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) { // This option is only available to users who can access all groups in // groups mode, so setting allowed to empty (which means all quiz attempts // are accessible, is not a security porblem. $allowed = array(); } $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); $displaycoursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id); $displaycourseshortname = format_string($COURSE->shortname, true, array('context' => $displaycoursecontext)); // Load the required questions. $questions = quiz_report_get_significant_questions($quiz); $table = new quiz_report_overview_table($quiz, $this->context, $qmsubselect, $groupstudents, $students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions); $filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'), $courseshortname, $quiz->name); $table->is_downloading($download, $filename, $displaycourseshortname . ' ' . format_string($quiz->name, true)); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); } // Process actions. if (empty($currentgroup) || $groupstudents) { if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) { if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) { require_capability('mod/quiz:deleteattempts', $this->context); $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed); redirect($reporturl->out(false, $displayoptions)); } } else { if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) { if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) { require_capability('mod/quiz:regrade', $this->context); $this->regrade_attempts($quiz, false, $groupstudents, $attemptids); redirect($reporturl->out(false, $displayoptions)); } } } } if ($regradeall && confirm_sesskey()) { require_capability('mod/quiz:regrade', $this->context); $this->regrade_attempts($quiz, false, $groupstudents); redirect($reporturl->out(false, $displayoptions), '', 5); } else { if ($regradealldry && confirm_sesskey()) { require_capability('mod/quiz:regrade', $this->context); $this->regrade_attempts($quiz, true, $groupstudents); redirect($reporturl->out(false, $displayoptions), '', 5); } else { if ($regradealldrydo && confirm_sesskey()) { require_capability('mod/quiz:regrade', $this->context); $this->regrade_attempts_needing_it($quiz, $groupstudents); redirect($reporturl->out(false, $displayoptions), '', 5); } } } // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data $this->print_header_and_tabs($cm, $course, $quiz, 'overview'); } if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used if (!$table->is_downloading()) { groups_print_activity_menu($cm, $reporturl->out(true, $displayoptions)); } } // Print information on the number of existing attempts if (!$table->is_downloading()) { //do not print notices when downloading if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) { echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>'; } } $hasquestions = quiz_questions_in_quiz($quiz->questions); if (!$table->is_downloading()) { if (!$hasquestions) { echo quiz_no_questions_message($quiz, $cm, $this->context); } else { if (!$students) { echo $OUTPUT->notification(get_string('nostudentsyet')); } else { if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } } } // Print display options $mform->display(); } $hasstudents = $students && (!$currentgroup || $groupstudents); if ($hasquestions && ($hasstudents || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) { // Construct the SQL $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid, '; if ($qmsubselect) { $fields .= "(CASE " . " WHEN {$qmsubselect} THEN 1" . " ELSE 0 " . "END) AS gradedattempt, "; } list($fields, $from, $where, $params) = $this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed); $table->set_count_sql("SELECT COUNT(1) FROM {$from} WHERE {$where}", $params); // Test to see if there are any regraded attempts to be listed. $fields .= ", COALESCE((\n SELECT MAX(qqr.regraded)\n FROM {quiz_overview_regrades} qqr\n WHERE qqr.questionusageid = quiza.uniqueid\n ), -1) AS regraded"; if ($regradefilter) { $where .= " AND COALESCE((\n SELECT MAX(qqr.regraded)\n FROM {quiz_overview_regrades} qqr\n WHERE qqr.questionusageid = quiza.uniqueid\n ), -1) <> -1"; } $table->set_sql($fields, $from, $where, $params); if (!$table->is_downloading()) { // Regrade buttons if (has_capability('mod/quiz:regrade', $this->context)) { $regradesneeded = $this->count_question_attempts_needing_regrade($quiz, $groupstudents); if ($currentgroup) { $a = new stdClass(); $a->groupname = groups_get_group_name($currentgroup); $a->coursestudents = get_string('participants'); $a->countregradeneeded = $regradesneeded; $regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a); $regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a); $regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a); } else { $regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $regradesneeded); $regradealldrylabel = get_string('regradealldry', 'quiz_overview'); $regradealllabel = get_string('regradeall', 'quiz_overview'); } $displayurl = new moodle_url($reporturl, $displayoptions + array('sesskey' => sesskey())); echo '<div class="mdl-align">'; echo '<form action="' . $displayurl->out_omit_querystring() . '">'; echo '<div>'; echo html_writer::input_hidden_params($displayurl); echo '<input type="submit" name="regradeall" value="' . $regradealllabel . '"/>'; echo '<input type="submit" name="regradealldry" value="' . $regradealldrylabel . '"/>'; if ($regradesneeded) { echo '<input type="submit" name="regradealldrydo" value="' . $regradealldrydolabel . '"/>'; } echo '</div>'; echo '</form>'; echo '</div>'; } // Print information on the grading method if ($strattempthighlight = quiz_report_highlighting_grading_method($quiz, $qmsubselect, $qmfilter)) { echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>'; } } // Define table columns $columns = array(); $headers = array(); if (!$table->is_downloading() && $candelete) { $columns[] = 'checkbox'; $headers[] = null; } $this->add_user_columns($table, $columns, $headers); $this->add_time_columns($columns, $headers); if ($detailedmarks) { foreach ($questions as $slot => $question) { // Ignore questions of zero length $columns[] = 'qsgrade' . $slot; $header = get_string('qbrief', 'quiz', $question->number); if (!$table->is_downloading()) { $header .= '<br />'; } else { $header .= ' '; } $header .= '/' . quiz_rescale_grade($question->maxmark, $quiz, 'question'); $headers[] = $header; } } if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) && $this->has_regraded_questions($from, $where, $params)) { $columns[] = 'regraded'; $headers[] = get_string('regrade', 'quiz_overview'); } $this->add_grade_columns($quiz, $columns, $headers); $this->set_up_table_columns($table, $columns, $headers, $reporturl, $displayoptions, false); $table->set_attribute('class', 'generaltable generalbox grades'); $table->out($pagesize, true); } if (!$table->is_downloading() && $this->should_show_grades($quiz)) { if ($currentgroup && $groupstudents) { list($usql, $params) = $DB->get_in_or_equal($groupstudents); $params[] = $quiz->id; if ($DB->record_exists_select('quiz_grades', "userid {$usql} AND quiz = ?", $params)) { $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php', array('id' => $quiz->id, 'groupid' => $currentgroup)); $graphname = get_string('overviewreportgraphgroup', 'quiz_overview', groups_get_group_name($currentgroup)); echo $OUTPUT->heading($graphname); echo html_writer::tag('div', html_writer::empty_tag('img', array('src' => $imageurl, 'alt' => $graphname)), array('class' => 'graph')); } } if ($DB->record_exists('quiz_grades', array('quiz' => $quiz->id))) { $graphname = get_string('overviewreportgraph', 'quiz_overview'); $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php', array('id' => $quiz->id)); echo $OUTPUT->heading($graphname); echo html_writer::tag('div', html_writer::empty_tag('img', array('src' => $imageurl, 'alt' => $graphname)), array('class' => 'graph')); } } return true; }
/** * * 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; }