/** * Called via pluginfile.php -> question_pluginfile to serve files belonging to * a question in a question_attempt when that attempt is a preview. * * @param object $course course settings object * @param object $context context object * @param string $component the name of the component we are serving files for. * @param string $filearea the name of the file area. * @param array $args the remaining bits of the file path. * @param bool $forcedownload whether the user must be forced to download the file. * @return bool false if file not found, does not return if found - justsend the file */ function question_preview_question_pluginfile($course, $context, $component, $filearea, $attemptid, $questionid, $args, $forcedownload) { global $USER, $SESSION, $DB, $CFG; require_once $CFG->dirroot . '/mod/quiz/locallib.php'; if (!($question = $DB->get_record('question', array('id' => $questionid)))) { return send_file_not_found(); } if (!question_has_capability_on($question, 'use', $question->category)) { send_file_not_found(); } if (!isset($SESSION->quizpreview->states) || $SESSION->quizpreview->questionid != $questionid) { send_file_not_found(); } $states = end($SESSION->quizpreview->states); if (!array_key_exists($question->id, $states)) { send_file_not_found(); } $state = $states[$question->id]; // Build fake cmoptions $quiz = new cmoptions(); $quiz->id = 0; $quiz->review = get_config('quiz', 'review'); if (empty($course->id)) { $quiz->course = SITEID; } else { $quiz->course = $course->id; } $quiz->decimalpoints = get_config('quiz', 'decimalpoints'); $questions[$question->id] = $question; get_question_options($questions); // Build fake attempt $timenow = time(); $attempt = new stdClass(); $attempt->quiz = $quiz->id; $attempt->userid = $USER->id; $attempt->attempt = 0; $attempt->sumgrades = 0; $attempt->timestart = $timenow; $attempt->timefinish = 0; $attempt->timemodified = $timenow; $attempt->uniqueid = 0; $attempt->id = 0; $attempt->layout = $question->id; $options = quiz_get_renderoptions($quiz, $attempt, $context, $state); $options->noeditlink = true; // XXX: mulitichoice type needs quiz id to get maxgrade $options->quizid = 0; if (!question_check_file_access($question, $state, $options, $context->id, $component, $filearea, $args, $forcedownload)) { send_file_not_found(); } $fs = get_file_storage(); $relativepath = implode('/', $args); $fullpath = "/{$context->id}/{$component}/{$filearea}/{$relativepath}"; if (!($file = $fs->get_file_by_hash(sha1($fullpath))) or $file->is_directory()) { send_file_not_found(); } send_stored_file($file, 0, 0, $forcedownload); }
/** * * 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; }
function offlinequiz_create_pdf_answer($maxanswers, $templateusage, $offlinequiz, $group, $courseid, $context) { global $CFG, $DB, $OUTPUT, $USER; $letterstr = ' abcdefghijklmnopqrstuvwxyz'; $groupletter = strtoupper($letterstr[$group->number]); $fm = new stdClass(); $fm->q = 0; $fm->a = 0; $texfilter = new filter_tex($context, array()); $pdf = new offlinequiz_answer_pdf('P', 'mm', 'A4'); $title = offlinequiz_str_html_pdf($offlinequiz->name); if (!empty($offlinequiz->time)) { $title = $title . ": " . offlinequiz_str_html_pdf(userdate($offlinequiz->time)); } $pdf->set_title($title); $pdf->group = $groupletter; $pdf->groupid = $group->number; $pdf->offlinequiz = $offlinequiz->id; $pdf->formtype = 4; $pdf->colwidth = 7 * 6.5; if ($maxanswers > 5) { $pdf->formtype = 3; $pdf->colwidth = 9 * 6.5; } if ($maxanswers > 7) { $pdf->formtype = 2; $pdf->colwidth = 14 * 6.5; } if ($maxanswers > 12) { $pdf->formtype = 1; $pdf->colwidth = 26 * 6.5; } if ($maxanswers > 26) { print_error('Too many answers in one question'); } $pdf->userid = $USER->id; $pdf->SetMargins(15, 20, 15); $pdf->SetAutoPageBreak(true, 20); $pdf->AddPage(); // Load all the questions and quba slots needed by this script. $slots = $templateusage->get_slots(); $sql = "SELECT q.*, c.contextid, ogq.page, ogq.slot, ogq.maxmark \n FROM {offlinequiz_group_questions} ogq,\n {question} q,\n {question_categories} c\n WHERE ogq.offlinequizid = :offlinequizid\n AND ogq.offlinegroupid = :offlinegroupid\n AND q.id = ogq.questionid\n AND q.category = c.id\n ORDER BY ogq.slot ASC "; $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id); if (!($questions = $DB->get_records_sql($sql, $params))) { echo $OUTPUT->box_start(); echo $OUTPUT->error_text(get_string('noquestionsfound', 'offlinequiz', $groupletter)); echo $OUTPUT->box_end(); return; } // Load the question type specific information. if (!get_question_options($questions)) { print_error('Could not load question options'); } // Counting the total number of multichoice questions in the question usage. $totalnumber = offlinequiz_count_multichoice_questions($templateusage); $number = 0; $col = 1; $offsety = 105.5; $offsetx = 17.3; $page = 1; $pdf->SetY($offsety); $pdf->SetFont('FreeSans', 'B', 10); foreach ($slots as $key => $slot) { set_time_limit(120); $slotquestion = $templateusage->get_question($slot); $currentquestionid = $slotquestion->id; $attempt = $templateusage->get_question_attempt($slot); $order = $slotquestion->get_order($attempt); // Order of the answers. // Get the question data. $question = $questions[$currentquestionid]; // Only look at multichoice questions. if ($question->qtype != 'multichoice' && $question->qtype != 'multichoiceset') { continue; } // Print the answer letters every 8 questions. if ($number % 8 == 0) { $pdf->SetFont('FreeSans', '', 8); $pdf->SetX(($col - 1) * $pdf->colwidth + $offsetx + 5); for ($i = 0; $i < $maxanswers; $i++) { $pdf->Cell(3.5, 3.5, number_in_style($i, $question->options->answernumbering), 0, 0, 'C'); $pdf->Cell(3, 3.5, '', 0, 0, 'C'); } $pdf->Ln(4.5); $pdf->SetFont('FreeSans', 'B', 10); } $pdf->SetX(($col - 1) * $pdf->colwidth + $offsetx); $pdf->Cell(5, 1, $number + 1 . ") ", 0, 0, 'R'); // Print one empty box for each answer. $x = $pdf->GetX(); $y = $pdf->GetY(); for ($i = 1; $i <= count($order); $i++) { // Move the boxes slightly down to align with question number. $pdf->Rect($x, $y + 0.6, 3.5, 3.5, '', array('all' => array('width' => 0.2))); $x += 6.5; } $pdf->SetX($x); $pdf->Ln(6.5); // // Save the answer page number in the group questions table. // $DB->set_field('offlinequiz_group_questions', 'pagenumber', $page, array('offlinequizid' => $offlinequiz->id, // 'offlinegroupid' => $group->id, 'questionid' => $question->id)); // Switch to next column if necessary. if (($number + 1) % 24 == 0) { $pdf->SetY($offsety); $col++; // Do a pagebreak if necessary. if ($col > $pdf->formtype and $number + 1 < $totalnumber) { $col = 1; $pdf->AddPage(); $page++; $pdf->SetY($offsety); } } $number++; } // Save the number of pages in the group questions table. $DB->set_field('offlinequiz_groups', 'numberofpages', $page, array('id' => $group->id)); $fs = get_file_storage(); // Prepare file record object. $timestamp = date('Ymd_His', time()); $fileinfo = array('contextid' => $context->id, 'component' => 'mod_offlinequiz', 'filearea' => 'pdfs', 'filepath' => '/', 'itemid' => 0, 'filename' => 'answer-' . strtolower($groupletter) . '_' . $timestamp . '.pdf'); if ($oldfile = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename'])) { $oldfile->delete(); } $pdfstring = $pdf->Output('', 'S'); $file = $fs->create_file_from_string($fileinfo, $pdfstring); return $file; }
/** * Display the report. */ function display($quiz, $cm, $course) { global $CFG, $COURSE, $DB, $PAGE, $OUTPUT; $context = get_context_instance(CONTEXT_MODULE, $cm->id); // Work out some display options - whether there is feedback, and whether scores should be shown. $hasfeedback = quiz_has_feedback($quiz); $fakeattempt = new stdClass(); $fakeattempt->preview = false; $fakeattempt->timefinish = $quiz->timeopen; $fakeattempt->userid = 0; $reviewoptions = quiz_get_reviewoptions($quiz, $fakeattempt, $context); $showgrades = quiz_has_grades($quiz) && $reviewoptions->scores; $download = optional_param('download', '', PARAM_ALPHA); if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) { //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); } //No need for a redirect, any attemptids that do not exist are ignored. //So no problem if the user refreshes and tries to delete the same attempts //twice. } $pageoptions = array(); $pageoptions['id'] = $cm->id; $pageoptions['q'] = $quiz->id; $pageoptions['mode'] = 'responses'; $reporturl = new moodle_url($CFG->wwwroot . '/mod/quiz/report.php', $pageoptions); $qmsubselect = quiz_report_qm_filter_select($quiz); /// find out current groups mode $currentgroup = groups_get_activity_group($cm, true); $mform = new mod_quiz_report_responses_settings($reporturl, array('qmsubselect' => $qmsubselect, 'quiz' => $quiz, 'currentgroup' => $currentgroup)); if ($fromform = $mform->get_data()) { $attemptsmode = $fromform->attemptsmode; if ($qmsubselect) { //control is not on the form if //the grading method is not set //to grade one attempt per user eg. for average attempt grade. $qmfilter = $fromform->qmfilter; } else { $qmfilter = 0; } set_user_preference('quiz_report_pagesize', $fromform->pagesize); $pagesize = $fromform->pagesize; } else { $qmfilter = optional_param('qmfilter', 0, PARAM_INT); $attemptsmode = optional_param('attemptsmode', null, PARAM_INT); if ($attemptsmode === null) { //default $attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL; } else { if ($currentgroup) { //default for when a group is selected if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) { $attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH; } } else { if (!$currentgroup && $course->id == SITEID) { //force report on front page to show all, unless a group is selected. $attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL; } } } $pagesize = get_user_preferences('quiz_report_pagesize', 0); } if ($pagesize < 1) { $pagesize = QUIZ_REPORT_DEFAULT_PAGE_SIZE; } // 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', $context) && $attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO; $displayoptions = array(); $displayoptions['attemptsmode'] = $attemptsmode; $displayoptions['qmfilter'] = $qmfilter; //work out the sql for this table. if (!($students = get_users_by_capability($context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', '', '', false))) { $students = array(); } else { $students = array_keys($students); } if (empty($currentgroup)) { // all users who can attempt quizzes $allowed = $students; $groupstudents = array(); } else { // all users who can attempt quizzes and who are in the currently selected group if (!($groupstudents = get_users_by_capability($context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false))) { $groupstudents = array(); } else { $groupstudents = array_keys($groupstudents); } $allowed = $groupstudents; } $questions = quiz_report_load_questions($quiz); $table = new quiz_report_responses_table($quiz, $qmsubselect, $groupstudents, $students, $questions, $candelete, $reporturl, $displayoptions); $table->is_downloading($download, get_string('reportresponses', 'quiz_responses'), "{$COURSE->shortname} " . format_string($quiz->name, true)); if (!$table->is_downloading()) { // Only print headers if not asked to download data $PAGE->requires->css('mod/quiz/report/responses/styles.css'); $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(false, $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>'; } } $nostudents = false; if (!$students) { echo $OUTPUT->notification(get_string('nostudentsyet')); $nostudents = true; } else { if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup')); $nostudents = true; } } if (!$table->is_downloading()) { // Print display options $mform->set_data($displayoptions + compact('pagesize')); $mform->display(); } if (!$nostudents || $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>'; } } $showgrades = quiz_has_grades($quiz) && $reviewoptions->scores; $hasfeedback = quiz_has_feedback($quiz); // Construct the SQL $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')') . ' AS concattedid, '; if ($qmsubselect) { $fields .= "(CASE " . " WHEN {$qmsubselect} THEN 1" . " ELSE 0 " . "END) AS gradedattempt, "; } $fields .= 'qa.uniqueid, qa.id AS attempt, u.id AS userid, u.idnumber, u.firstname,' . ' u.lastname, u.institution, u.department, u.email, u.picture, u.imagealt, ' . 'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration, ' . 'qa.layout '; // This part is the same for all cases - join users and quiz_attempts tables $from = '{user} u '; $from .= 'LEFT JOIN {quiz_attempts} qa ON qa.userid = u.id AND qa.quiz = :quizid'; $params = array('quizid' => $quiz->id); if ($qmsubselect && $qmfilter) { $from .= ' AND ' . $qmsubselect; } switch ($attemptsmode) { case QUIZ_REPORT_ATTEMPTS_ALL: // Show all attempts, including students who are no longer in the course $where = 'qa.id IS NOT NULL AND qa.preview = 0'; break; case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH: // Show only students with attempts list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); $params += $allowed_params; $where = "u.id {$allowed_usql} AND qa.preview = 0 AND qa.id IS NOT NULL"; break; case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO: // Show only students without attempts list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); $params += $allowed_params; $where = "u.id {$allowed_usql} AND qa.id IS NULL"; break; case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS: // Show all students with or without attempts list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000'); $params += $allowed_params; $where = "u.id {$allowed_usql} AND (qa.preview = 0 OR qa.preview IS NULL)"; break; } $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; } if (!$table->is_downloading() && $CFG->grade_report_showuserimage) { $columns[] = 'picture'; $headers[] = ''; } if (!$table->is_downloading()) { $columns[] = 'fullname'; $headers[] = get_string('name'); } else { $columns[] = 'lastname'; $headers[] = get_string('lastname'); $columns[] = 'firstname'; $headers[] = get_string('firstname'); } if ($CFG->grade_report_showuseridnumber) { $columns[] = 'idnumber'; $headers[] = get_string('idnumber'); } if ($table->is_downloading()) { $columns[] = 'institution'; $headers[] = get_string('institution'); $columns[] = 'department'; $headers[] = get_string('department'); $columns[] = 'email'; $headers[] = get_string('email'); $columns[] = 'timestart'; $headers[] = get_string('startedon', 'quiz'); $columns[] = 'timefinish'; $headers[] = get_string('timecompleted', 'quiz'); $columns[] = 'duration'; $headers[] = get_string('attemptduration', 'quiz'); } if ($showgrades) { $columns[] = 'sumgrades'; $headers[] = get_string('grade', 'quiz') . '/' . quiz_format_grade($quiz, $quiz->grade); } if ($hasfeedback) { $columns[] = 'feedbacktext'; $headers[] = get_string('feedback', 'quiz'); } // we want to display responses for all questions foreach ($questions as $id => $question) { // Ignore questions of zero length $columns[] = 'qsanswer' . $id; $headers[] = '#' . $question->number; $question->formattedname = strip_tags(format_string($question->name)); } // Load the question type specific information if (!get_question_options($questions)) { print_error('cannotloadoptions', 'quiz_responses'); } $table->define_columns($columns); $table->define_headers($headers); $table->sortable(true, 'concattedid'); // Set up the table $table->define_baseurl($reporturl->out(false, $displayoptions)); $table->collapsible(true); $table->column_suppress('picture'); $table->column_suppress('fullname'); $table->column_suppress('idnumber'); $table->no_sorting('feedbacktext'); $table->column_class('picture', 'picture'); $table->column_class('lastname', 'bold'); $table->column_class('firstname', 'bold'); $table->column_class('fullname', 'bold'); $table->column_class('sumgrades', 'bold'); $table->set_attribute('id', 'attempts'); $table->out($pagesize, true); } return true; }
} $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; $action->responses = $closestates[$key]->responses; $action->timestamp = $closestates[$key]->timestamp; if (question_process_responses($question, $closestates[$key], $action, $quiz, $attempt)) { save_question_session($question, $closestates[$key]); } else { $success = false;
/** * Load a set of questions, given a list of ids. The $join and $extrafields arguments can be used * together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and * read the code below to see how the SQL is assembled. Throws exceptions on error. * * @param array $questionids array of question ids. * @param string $extrafields extra SQL code to be added to the query. * @param string $join extra SQL code to be added to the query. * @param array $extraparams values for any placeholders in $join. * You are strongly recommended to use named placeholder. * * @return array question objects. */ function question_load_questions($questionids, $extrafields = '', $join = '') { $questions = question_preload_questions($questionids, $extrafields, $join); // Load the question type specific information if (!get_question_options($questions)) { return 'Could not load the question options'; } return $questions; }
/** * Fully load some or all of the questions for this quiz. You must call * {@link preload_questions()} first. * * @param array $questionids question ids of the questions to load. null for all. */ public function load_questions($questionids = null) { if (is_null($questionids)) { $questionids = $this->questionids; } $questionstoprocess = array(); foreach ($questionids as $id) { if (array_key_exists($id, $this->questions)) { $questionstoprocess[$id] = $this->questions[$id]; } } get_question_options($questionstoprocess); }
/** * Display the report. */ function display($quiz, $cm, $course) { global $CFG, $DB, $QTYPES, $OUTPUT; $context = get_context_instance(CONTEXT_MODULE, $cm->id); $download = optional_param('download', '', PARAM_ALPHA); $everything = optional_param('everything', 0, PARAM_BOOL); $recalculate = optional_param('recalculate', 0, PARAM_BOOL); //pass the question id for detailed analysis question $qid = optional_param('qid', 0, PARAM_INT); $pageoptions = array(); $pageoptions['id'] = $cm->id; $pageoptions['q'] = $quiz->id; $pageoptions['mode'] = 'statistics'; $questions = quiz_report_load_questions($quiz); // Load the question type specific information if (!get_question_options($questions)) { print_error('cannotloadquestion', 'question'); } $reporturl = new moodle_url($CFG->wwwroot . '/mod/quiz/report.php', $pageoptions); $mform = new mod_quiz_report_statistics($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 = groups_get_activity_group($cm, true); $nostudentsingroup = false; //true if a group is selected and their is noeone in it. if (!empty($currentgroup)) { // all users who can attempt quizzes and who are in the currently selected group $groupstudents = get_users_by_capability($context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); if (!$groupstudents) { $nostudentsingroup = true; } } else { $groupstudents = array(); } if ($recalculate) { if ($todelete = $DB->get_records_menu('quiz_statistics', array('quizid' => $quiz->id, 'groupid' => (int) $currentgroup, 'allattempts' => $useallattempts))) { list($todeletesql, $todeleteparams) = $DB->get_in_or_equal(array_keys($todelete)); if (!$DB->delete_records_select('quiz_statistics', "id {$todeletesql}", $todeleteparams)) { print_error('errordeleting', 'quiz_statistics', '', 'quiz_statistics'); } if (!$DB->delete_records_select('quiz_question_statistics', "quizstatisticsid {$todeletesql}", $todeleteparams)) { print_error('errordeleting', 'quiz_statistics', '', 'quiz_question_statistics'); } if (!$DB->delete_records_select('quiz_question_response_stats', "quizstatisticsid {$todeletesql}", $todeleteparams)) { print_error('errordeleting', 'quiz_statistics', '', 'quiz_question_response_stats'); } } redirect($reporturl); } $this->table = new quiz_report_statistics_table(); $filename = "{$course->shortname}-" . format_string($quiz->name, true); $this->table->is_downloading($download, $filename, get_string('quizstructureanalysis', 'quiz_statistics')); if (!$this->table->is_downloading()) { // Only print headers if not asked to download data $this->print_header_and_tabs($cm, $course, $quiz, "statistics"); } if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used if (!$this->table->is_downloading()) { groups_print_activity_menu($cm, $reporturl->out()); echo '<br />'; if ($currentgroup && !$groupstudents) { echo $OUTPUT->notification(get_string('nostudentsingroup', 'quiz_statistics')); } } } if (!$this->table->is_downloading()) { // Print display options $mform->set_data(array('useallattempts' => $useallattempts)); $mform->display(); } list($quizstats, $questions, $subquestions, $s, $usingattemptsstring) = $this->quiz_questions_stats($quiz, $currentgroup, $nostudentsingroup, $useallattempts, $groupstudents, $questions); if (!$this->table->is_downloading()) { if ($s == 0) { echo $OUTPUT->heading(get_string('noattempts', 'quiz')); } } if ($s) { $this->table->setup($quiz, $cm->id, $reporturl, $s); } if (!$qid) { //main page $this->output_quiz_info_table($course, $cm, $quiz, $quizstats, $usingattemptsstring, $currentgroup, $groupstudents, $useallattempts, $download, $reporturl, $everything); $this->output_quiz_structure_analysis_table($s, $questions, $subquestions); if (!$this->table->is_downloading() || $everything && $this->table->is_downloading() == 'xhtml') { if ($s > 1) { $imageurl = $CFG->wwwroot . '/mod/quiz/report/statistics/statistics_graph.php?id=' . $quizstats->id; echo $OUTPUT->heading(get_string('statisticsreportgraph', 'quiz_statistics')); echo '<div class="mdl-align"><img src="' . $imageurl . '" alt="' . get_string('statisticsreportgraph', 'quiz_statistics') . '" /></div>'; } } if ($this->table->is_downloading()) { if ($everything) { foreach ($questions as $question) { if ($question->qtype != 'random' && $QTYPES[$question->qtype]->show_analysis_of_responses()) { $this->output_individual_question_data($quiz, $question, $reporturl, $quizstats); } elseif (!empty($question->_stats->subquestions)) { $subitemstodisplay = explode(',', $question->_stats->subquestions); foreach ($subitemstodisplay as $subitemid) { $this->output_individual_question_data($quiz, $subquestions[$subitemid], $reporturl, $quizstats); } } } $exportclassinstance =& $this->table->export_class_instance(); } else { $this->table->finish_output(); } } if ($this->table->is_downloading() && $everything) { $exportclassinstance->finish_document(); } } else { //individual question page $thisquestion = false; if (isset($questions[$qid])) { $thisquestion = $questions[$qid]; } else { if (isset($subquestions[$qid])) { $thisquestion = $subquestions[$qid]; } else { print_error('questiondoesnotexist', 'question'); } } $this->output_individual_question_data($quiz, $thisquestion, $reporturl, $quizstats); } return true; }
/** * Display analysis of a particular question in this quiz. * @param object $question the row from the question table for the question to analyse. */ public function display_analysis($question) { get_question_options($question); $this->display_question_information($question); $dm = new question_engine_data_mapper(); $this->attempts = $dm->load_attempts_at_question($question->id, $this->qubaids); // Setup useful internal arrays for report generation. $this->inputs = array_keys($question->inputs); $this->prts = array_keys($question->prts); // TODO: change this to be a list of all *deployed* notes, not just those *used*. $qnotes = array(); foreach ($this->attempts as $qa) { $q = $qa->get_question(); $qnotes[$q->get_question_summary()] = true; } $this->qnotes = array_keys($qnotes); // Compute results. list($results, $answernoteresults, $answernoteresultsraw) = $this->input_report(); list($validresults, $invalidresults) = $this->input_report_separate(); // Display the results. // Overall results. $i = 0; $list = ''; $tablehead = array(); foreach ($this->qnotes as $qnote) { $list .= html_writer::tag('li', stack_ouput_castext($qnote)); $i++; $tablehead[] = $i; } $tablehead[] = format_string(get_string('questionreportingtotal', 'quiz_stack')); $tablehead = array_merge(array(''), $tablehead, $tablehead); echo html_writer::tag('p', get_string('notesused', 'quiz_stack')); echo html_writer::tag('ol', $list); // Complete anwernotes. $inputstable = new html_table(); $inputstable->head = $tablehead; $data = array(); foreach ($answernoteresults as $prt => $anotedata) { if (count($answernoteresults) > 1) { $inputstable->data[] = array(html_writer::tag('b', $this->prts[$prt])); } $cstats = $this->column_stats($anotedata); foreach ($anotedata as $anote => $a) { $inputstable->data[] = array_merge(array($anote), $a, array(array_sum($a)), $cstats[$anote]); } } echo html_writer::tag('p', get_string('completenotes', 'quiz_stack')); echo html_writer::table($inputstable); // Split anwernotes. $inputstable = new html_table(); $inputstable->head = $tablehead; foreach ($answernoteresultsraw as $prt => $anotedata) { if (count($answernoteresultsraw) > 1) { $inputstable->data[] = array(html_writer::tag('b', $this->prts[$prt])); } $cstats = $this->column_stats($anotedata); foreach ($anotedata as $anote => $a) { $inputstable->data[] = array_merge(array($anote), $a, array(array_sum($a)), $cstats[$anote]); } } echo html_writer::tag('p', get_string('splitnotes', 'quiz_stack')); echo html_writer::table($inputstable); // Maxima analysis. $maxheader = array(); $maxheader[] = "STACK input data for the question '" . $question->name . "'"; $maxheader[] = new moodle_url($this->get_base_url(), array('questionid' => $question->id)); $maxheader[] = "Data generated: " . date("Y-m-d H:i:s"); $maximacode = $this->maxima_comment($maxheader); $maximacode .= "\ndisplay2d:true\$\nload(\"stackreporting\")\$\n"; $maximacode .= "stackdata:[]\$\n"; $variants = array(); foreach ($this->qnotes as $qnote) { $variants[] = '"' . $qnote . '"'; } $inputs = array(); foreach ($this->inputs as $input) { $inputs[] = $input; } $anymaximadata = false; // Results for each question note. foreach ($this->qnotes as $qnote) { echo html_writer::tag('h2', get_string('variantx', 'quiz_stack') . stack_ouput_castext($qnote)); $inputstable = new html_table(); $inputstable->attributes['class'] = 'generaltable stacktestsuite'; $inputstable->head = array_merge(array(get_string('questionreportingsummary', 'quiz_stack'), '', get_string('questionreportingscore', 'quiz_stack')), $this->prts); foreach ($results[$qnote] as $dsummary => $summary) { foreach ($summary as $key => $res) { $inputstable->data[] = array_merge(array($dsummary, $res['count'], $res['fraction']), $res['answernotes']); } } echo html_writer::table($inputstable); // Separate out inputs and look at validity. $validresultsdata = array(); foreach ($this->inputs as $input) { $inputstable = new html_table(); $inputstable->attributes['class'] = 'generaltable stacktestsuite'; $inputstable->head = array($input, '', '', ''); foreach ($validresults[$qnote][$input] as $key => $res) { $validresultsdata[$input][] = $key; $inputstable->data[] = array($key, $res, get_string('inputstatusnamevalid', 'qtype_stack'), ''); $inputstable->rowclasses[] = 'pass'; } foreach ($invalidresults[$qnote][$input] as $key => $res) { $inputstable->data[] = array($key, $res[0], get_string('inputstatusnameinvalid', 'qtype_stack'), $res[1]); $inputstable->rowclasses[] = 'fail'; } echo html_writer::table($inputstable); } // Maxima analysis. $maximacode .= "\n/* " . $qnote . ' */ ' . "\n"; foreach ($this->inputs as $input) { if (array_key_exists($input, $validresultsdata)) { $maximacode .= $this->maxima_list_create($validresultsdata[$input], $input); $anymaximadata = true; } } $maximacode .= "stackdata:append(stackdata,[[" . implode(',', $inputs) . "]])\$\n"; } // Maxima analysis at the end. if ($anymaximadata) { $maximacode .= "\n/* Reset input names */\nkill(" . implode(',', $inputs) . ")\$\n"; $maximacode .= $this->maxima_list_create($variants, 'variants'); $maximacode .= $this->maxima_list_create($inputs, 'inputs'); $maximacode .= "\n/* Perform the analysis. */\nstack_analysis(stackdata)\$\n"; echo html_writer::tag('h3', get_string('maximacode', 'quiz_stack')); echo html_writer::tag('p', get_string('offlineanalysis', 'quiz_stack')); $rows = count(explode("\n", $maximacode)) + 2; echo html_writer::tag('textarea', $maximacode, array('readonly' => 'readonly', 'wrap' => 'virtual', 'rows' => $rows, 'cols' => '160')); } }
function quiz_get_questions($quiz_id) { global $USER, $CFG, $QTYPES, $_SESSION, $DB; include_once $CFG->dirroot . '/mod/quiz/locallib.php'; // Check to see we haven't already gotten these questions. If we have return from the session if (!empty($_SESSION['retrived_question_ids'][$quiz_id])) { $question_ids = $_SESSION['retrived_question_ids'][$quiz_id]; } else { $question_ids = $DB->get_records('quiz_question_instances', array('quiz' => $quiz_id)); } $questions = array(); if (empty($question_ids)) { return false; } $_SESSION['retrived_question_ids'][$quiz_id] = $question_ids; if (!empty($_SESSION['retrived_questions'][$quiz_id])) { $questions = $_SESSION['retrived_questions'][$quiz_id]; } else { foreach ($question_ids as $index => $question) { $db_question = $DB->get_record('question', array('id' => $question->question)); $questions[$db_question->id] = $db_question; } get_question_options($questions); $_SESSION['retrived_questions'][$quiz_id] = $questions; } return $questions; }
/** * Load a question definition from the database. The object returned * will actually be of an appropriate {@link question_definition} subclass. * @param int $questionid the id of the question to load. * @param bool $allowshuffle if false, then any shuffle option on the selected * quetsion is disabled. * @return question_definition loaded from the database. */ public static function load_question($questionid, $allowshuffle = true) { global $DB; if (self::$testmode) { // Evil, test code in production, but now way round it. return self::return_test_question_data($questionid); } $questiondata = $DB->get_record_sql(' SELECT q.*, qc.contextid FROM {question} q JOIN {question_categories} qc ON q.category = qc.id WHERE q.id = :id', array('id' => $questionid), MUST_EXIST); get_question_options($questiondata); if (!$allowshuffle) { $questiondata->options->shuffleanswers = false; } return self::make_question($questiondata); }
// along with Stack. If not, see <http://www.gnu.org/licenses/>. /** * Script to download the export of a single STACK question. * * @copyright 2015 the Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once __DIR__ . '/../../../config.php'; require_once $CFG->libdir . '/questionlib.php'; require_once $CFG->dirroot . '/question/format/xml/format.php'; require_once __DIR__ . '/locallib.php'; // Get the parameters from the URL. $questionid = required_param('questionid', PARAM_INT); // Load the necessary data. $questiondata = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST); get_question_options($questiondata); $question = question_bank::load_question($questionid); // Process any other URL parameters, and do require_login. list($context, $seed, $urlparams) = qtype_stack_setup_question_test_page($question); $contexts = new question_edit_contexts($context); // Check permissions. question_require_capability_on($questiondata, 'edit'); require_sesskey(); // Initialise $PAGE. $nexturl = new moodle_url('/question/type/stack/questiontestrun.php', $urlparams); $PAGE->set_url($nexturl); // Since this script always ends in a redirect. $PAGE->set_heading($COURSE->fullname); $PAGE->set_pagelayout('admin'); // Set up the export format. $qformat = new qformat_xml();
function evaluate_quiz($acode, $jobid, $newattempt, $blended) { global $USER; global $CFG; mtrace("Evaluation QUIZ Processing..." . "<BR><BR>"); try { print "New Attempt is: " . $newattempt . "<BR/>"; $detected_userid = find_userid($acode, $jobid); if ($detected_userid == null or $detected_userid == '') { throw new EvaluationError(get_string('ErrorUserIDEmpty', 'blended'), EvaluationError::USERID_IS_EMPTY); } $user_reg = blended_get_user($detected_userid, $blended); if ($user_reg == null) { throw new EvaluationError(get_string('ErrorUserNotInCourse', 'blended'), EvaluationError::USER_NOT_IN_THIS_COURSE); } $userid = $user_reg->id; mtrace('Obtained USERID value: ' . $userid . " OK. <BR/>"); $quiz = get_quiz($acode); $attempts = quiz_get_user_attempts($quiz->id, $userid, 'all', true); mtrace("Searching quiz... Success." . "<BR/>"); $uniqueid = get_uniqueid($acode); mtrace('Obtained uniqueid: OK. <BR/>'); $timestamp = get_timestamp($acode); mtrace('Obtained timestamp: OK. <BR/>'); if (!get_record('quiz_attempts', 'uniqueid', $uniqueid)) { $newattempt = true; } else { $newattempt = false; mtrace("User {$userid} had opened this attempt already."); } $attemptnumber = 1; if ($newattempt == false) { mtrace('Obtaining user attempt...<BR/>'); set_attempt_unfinished($uniqueid); $attempt = quiz_get_user_attempt_unfinished($quiz->id, $userid); } elseif ($newattempt == true) { mtrace('Creating new attempt...<BR/>'); $attempt = create_new_attempt($quiz, $attemptnumber, $userid, $acode, $uniqueid, $timestamp); // Save the attempt if (!insert_record('quiz_attempts', $attempt)) { throw new EvaluationError(get_string('ErrorCouldNotCreateAttempt', 'blended'), EvaluationError::CREATE_QUIZ_ATTEMPT_ERROR); } // Actualizamos el estado de las imágenes para indicar que ya está creado un nuevo attempt update_images_status($acode, $jobid); } update_question_attempts($uniqueid); // /* mtrace('<BR>Getting questions and question options... '); $questions = get_questions($attempt, $quiz); if (!get_question_options($questions)) { error('Could not load question options'); } mtrace('Success! <BR>'); // print ("<BR>He obtenido questions: "); //print_object($questions); $lastattemptid = false; // if ($attempt->attempt > 1 and $quiz->attemptonlast and !$attempt->preview) { // Find the previous attempt // if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) { // error('Could not find previous attempt to build on'); // } //} //print ('He obtenido lastattemptid'); mtrace('Getting question states... '); if (!($states = get_question_states($questions, $quiz, $attempt, $lastattemptid))) { error('Could not restore question sessions'); } mtrace('Success! <BR>'); mtrace('Getting responses... <BR>'); $responses = get_responses($acode, $jobid, $attempt); //print('Estas son las responses:'); //print_object($responses); //$timestamp=time(); $event = 8; $actions = question_extract_responses($questions, $responses, $event); $questionids = get_questionids($acode); // print $questionids; $questionidarray = explode(',', $questionids); $success = true; mtrace('<BR> Processing responses and saving session... '); foreach ($questionidarray as $i) { if (!isset($actions[$i])) { $actions[$i]->responses = array('' => ''); $actions[$i]->event = QUESTION_EVENTOPEN; } $actions[$i]->timestamp = $timestamp; if (question_process_responses($questions[$i], $states[$i], $actions[$i], $quiz, $attempt)) { save_question_session($questions[$i], $states[$i]); } else { $success = false; } } mtrace('Success! <BR>'); // Set the attempt to be finished $timestamp = time(); //$attempt->timefinish = $timestamp; // Update the quiz attempt and the overall grade for the quiz mtrace('<BR> Finishing the attempt... '); // print_object ($attempt); if (set_field('quiz_attempts', 'timefinish', $timestamp, 'uniqueid', $uniqueid) == false) { throw new EvaluationError('Unable to finish the quiz attempt!', EvaluationError::FINISH_QUIZ_ATTEMPT_ERROR); } mtrace('Success! <BR>'); if ($attempt->attempt > 1 || $attempt->timefinish > 0 and !$attempt->preview) { mtrace('<BR> Saving quiz grade... '); quiz_save_best_grade($quiz, $userid); } mtrace('Success! <BR>'); // */ mtrace("Process Done. <BR><BR>"); mtrace("<center> Your quiz has been succesfully evaluated!! </center>"); } catch (EvaluationError $e) { throw $e; } return; }
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $QTYPES, $DB, $OUTPUT, $PAGE; $viewoptions = array('mode' => 'grading', 'q' => $quiz->id); if ($questionid = optional_param('questionid', 0, PARAM_INT)) { $viewoptions += array('questionid' => $questionid); } // grade question specific parameters if ($userid = optional_param('userid', 0, PARAM_INT)) { $viewoptions += array('userid' => $userid); } if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) { $viewoptions += array('attemptid' => $attemptid); } if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) { $viewoptions += array('gradeall' => $gradeall); } if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) { $viewoptions += array('gradeungraded' => $gradeungraded); } if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) { $viewoptions += array('gradenextungraded' => $gradenextungraded); } $this->cm = $cm; $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $this->context)) { echo $OUTPUT->notification(get_string('gradingnotallowed', 'quiz_grading')); return true; } $gradeableqs = quiz_report_load_questions($quiz); $questionsinuse = implode(',', array_keys($gradeableqs)); foreach ($gradeableqs as $qid => $question) { if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) { unset($gradeableqs[$qid]); } } if (empty($gradeableqs)) { echo $OUTPUT->heading(get_string('noessayquestionsfound', 'quiz')); return true; } else { if (count($gradeableqs) == 1) { $questionid = array_shift(array_keys($gradeableqs)); } } $currentgroup = groups_get_activity_group($this->cm, true); $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); if (!empty($questionid)) { if (!isset($gradeableqs[$questionid])) { print_error('invalidquestionid', 'quiz_grading', '', $questionid); } else { $question =& $gradeableqs[$questionid]; } // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { print_error('cannotloadquestioninfo', 'quiz_grading'); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); if ($data = data_submitted()) { // post data submitted, process it if (confirm_sesskey() && $this->users) { // now go through all of the responses and save them. $allok = true; foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt $uniqueid = clean_param($uniqueid, PARAM_INT); list($usql, $params) = $DB->get_in_or_equal(array_keys($this->users)); if (!($attempt = $DB->get_record_sql("SELECT * FROM {quiz_attempts} " . "WHERE uniqueid = ? AND " . "userid {$usql} AND " . "quiz=?", array_merge(array($uniqueid), $params, array($quiz->id))))) { print_error('invalidattemptid', 'quiz_grading'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt $error = question_process_comment($question, $state, $attempt, $response['comment'], FORMAT_HTML, $response['grade']); if (is_string($error)) { echo $OUTPUT->notification($error); $allok = false; } else { if ($state->changed) { // If the state has changed save it and update the quiz grade save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } } if ($allok) { echo $OUTPUT->notification(get_string('changessaved', 'quiz'), 'notifysuccess'); } else { echo $OUTPUT->notification(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess'); } } } $this->viewurl = new moodle_url('/mod/quiz/report.php', $viewoptions); /// find out current groups mode if ($groupmode = groups_get_activity_groupmode($this->cm)) { // Groups are being used groups_print_activity_menu($this->cm, $this->viewurl->out(true, array('userid' => 0, 'attemptid' => 0))); } if (empty($this->users)) { if ($currentgroup) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } else { echo $OUTPUT->notification(get_string('nostudentsyet')); } return true; } $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, array_keys($gradeableqs), array_keys($this->users)); if (empty($qattempts)) { echo $OUTPUT->notification(get_string('noattemptstoshow', 'quiz')); return true; } $qmenu = array(); foreach ($gradeableqs as $qid => $questionformenu) { $a = new stdClass(); $a->number = $gradeableqs[$qid]->number; $a->name = $gradeableqs[$qid]->name; $a->gradedattempts = $qattempts[$qid]->gradedattempts; $a->totalattempts = $qattempts[$qid]->totalattempts; $a->openspan = ''; $a->closespan = ''; $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a); } if (count($gradeableqs) != 1) { $qurl = fullclone($this->viewurl); $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded'); $menu = $OUTPUT->single_select($qurl, 'questionid', $qmenu, $questionid, array('' => 'choosedots'), 'questionid'); echo '<div class="mdl-align">' . $menu . '</div>'; } if (!$questionid) { return true; } $a = new stdClass(); $a->number = $question->number; $a->name = $question->name; $a->gradedattempts = $qattempts[$question->id]->gradedattempts; $a->totalattempts = $qattempts[$question->id]->totalattempts; $a->openspan = '<span class="highlightgraded">'; $a->closespan = '</span>'; echo $OUTPUT->heading(get_string('questiontitle', 'quiz_grading', $a)); // our 2 different views // the first view allows a user to select a question and // displays the users who have answered the essay question // and all of their attempts at answering the question // the second prints selected attempt answer(s) with a comment // and grade form underneath them $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts; if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) { $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded); } else { $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded); } return true; }
public function load_many_for_cache(array $questionids) { global $DB; list($idcondition, $params) = $DB->get_in_or_equal($questionids); $questiondata = $DB->get_records_sql(' SELECT q.*, qc.contextid FROM {question} q JOIN {question_categories} qc ON q.category = qc.id WHERE q.id ' . $idcondition, $params); foreach ($questionids as $id) { if (!array_key_exists($id, $questionids)) { throw new dml_missing_record_exception('question', '', array('id' => $id)); } get_question_options($questiondata[$id]); } return $questiondata; }
/** * Fully load some or all of the questions for this quiz. You must call {@link preload_questions()} first. * * @param array $questionids question ids of the questions to load. null for all. */ public function load_questions($questionids = null) { if (is_null($questionids)) { $questionids = $this->questionids; } $questionstoprocess = array(); foreach ($questionids as $id) { $questionstoprocess[$id] = $this->questions[$id]; } if (!get_question_options($questionstoprocess)) { throw new moodle_quiz_exception($this, 'loadingquestionsfailed', implode(', ', $questionids)); } }
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $QTYPES; $viewoptions = array('mode' => 'grading', 'q' => $quiz->id); if ($questionid = optional_param('questionid', 0, PARAM_INT)) { $viewoptions += array('questionid' => $questionid); } // grade question specific parameters $gradeungraded = optional_param('gradeungraded', 0, PARAM_INT); if ($userid = optional_param('userid', 0, PARAM_INT)) { $viewoptions += array('userid' => $userid); } if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) { $viewoptions += array('attemptid' => $attemptid); } if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) { $viewoptions += array('gradeall' => $gradeall); } if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) { $viewoptions += array('gradeungraded' => $gradeungraded); } if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) { $viewoptions += array('gradenextungraded' => $gradenextungraded); } $this->cm = $cm; $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $this->context)) { notify(get_string('gradingnotallowed', 'quiz_grading')); return true; } $gradeableqs = quiz_report_load_questions($quiz); $questionsinuse = implode(',', array_keys($gradeableqs)); foreach ($gradeableqs as $qid => $question) { if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) { unset($gradeableqs[$qid]); } } if (empty($gradeableqs)) { print_heading(get_string('noessayquestionsfound', 'quiz')); return true; } else { if (count($gradeableqs) == 1) { $questionid = array_shift(array_keys($gradeableqs)); } } $currentgroup = groups_get_activity_group($this->cm, true); $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); $this->userids = implode(',', array_keys($this->users)); if (!empty($questionid)) { if (!isset($gradeableqs[$questionid])) { error("Gradeable question with id {$questionid} not found"); } else { $question =& $gradeableqs[$questionid]; } $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id); // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { error("Unable to load questiontype specific question information"); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib if ($data = data_submitted()) { // post data submitted, process it require_sesskey(); // now go through all of the responses and save them. $allok = true; foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt $uniqueid = clean_param($uniqueid, PARAM_INT); if (!($attempt = get_record_sql("SELECT * FROM {$CFG->prefix}quiz_attempts " . "WHERE uniqueid = {$uniqueid} AND " . "userid IN ({$this->userids}) AND " . "quiz=" . $quiz->id))) { error('No such attempt ID exists'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt $error = question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']); if (is_string($error)) { notify($error); $allok = false; } else { if ($state->changed) { // If the state has changed save it and update the quiz grade save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } } if ($allok) { notify(get_string('changessaved', 'quiz'), 'notifysuccess'); } else { notify(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess'); } } $this->viewurl = new moodle_url($CFG->wwwroot . '/mod/quiz/report.php', $viewoptions); /// find out current groups mode if ($groupmode = groups_get_activity_groupmode($this->cm)) { // Groups are being used groups_print_activity_menu($this->cm, $this->viewurl->out(false, array('userid' => 0, 'attemptid' => 0))); } echo '<div class="quizattemptcounts">' . quiz_num_attempt_summary($quiz, $cm, true, $currentgroup) . '</div>'; if (empty($this->users)) { if ($currentgroup) { notify(get_string('nostudentsingroup')); } else { notify(get_string('nostudentsyet')); } return true; } $gradeablequestionids = implode(',', array_keys($gradeableqs)); $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, $gradeablequestionids, $this->userids); if (empty($qattempts)) { notify(get_string('noattemptstoshow', 'quiz')); return true; } $qmenu = array(); foreach ($gradeableqs as $qid => $questionformenu) { $a = new object(); $a->number = $gradeableqs[$qid]->number; $a->name = $gradeableqs[$qid]->name; $a->gradedattempts = $qattempts[$qid]->gradedattempts; $a->totalattempts = $qattempts[$qid]->totalattempts; $a->openspan = ''; $a->closespan = ''; $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a); } if (count($gradeableqs) != 1) { $qurl = fullclone($this->viewurl); $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded'); $menu = popup_form($qurl->out() . '&questionid=', $qmenu, 'questionid', $questionid, 'choose', '', '', true); echo '<div class="mdl-align">' . $menu . '</div>'; } if (!$questionid) { return true; } $a = new object(); $a->number = $question->number; $a->name = $question->name; $a->gradedattempts = $qattempts[$question->id]->gradedattempts; $a->totalattempts = $qattempts[$question->id]->totalattempts; $a->openspan = '<span class="highlightgraded">'; $a->closespan = '</span>'; print_heading(get_string('questiontitle', 'quiz_grading', $a)); // our 3 different views // the first one displays all of the manually graded questions in the quiz // with the number of ungraded attempts for each question // the second view displays the users who have answered the essay question // and all of their attempts at answering the question // the third prints the question with a comment // and grade form underneath it $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts; if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) { $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded); } else { $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded); } return true; }
public function get_quiz_questions($quiz) { $id = is_object($quiz) ? $quiz->id : $quiz; global $DB; global $CFG; $prefix = $CFG->prefix; $sql = 'SELECT q.* FROM {question} q, {quiz_question_instances} i WHERE i.quiz = ' . $id . ' AND q.id = i.question'; try { $result = $DB->get_records_sql($sql); if (is_array($result)) { foreach ($result as $question) { $question->export_process = true; //needed to export datasets } } get_question_options($result, true); } catch (Exception $e) { debug($e); } return $result; }
$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; $overtime = 0; if ($attempt->timefinish) { if ($timetaken = $attempt->timefinish - $attempt->timestart) { if ($timelimit && $timetaken > $timelimit + 60) { $overtime = $timetaken - $timelimit; $overtime = format_time($overtime);
function game_sudoku_getquestions($questionlist) { global $CFG, $DB; // Load the questions. $sql = "SELECT q.*,qmo.single " . " FROM {$CFG->prefix}question " . " LEFT JOIN {$CFG->prefix}qtype_multichoice_options qmo ON q.id=qmo.questionid AND q.qtype='multichoice' " . " WHERE q.id IN ({$questionlist})"; if (!($questions = $DB->get_records_select('question', "id IN ({$questionlist})"))) { print_error(get_string('no_questions', 'game')); } // Load the question type specific information. if (!get_question_options($questions)) { print_error('Could not load question options'); } return $questions; }
if (!($tocat = get_record('question_categories', 'id', $tocatid))) { print_error('categorydoesnotexist', 'question', $returnurl); } $tocat->context = get_context_instance_by_id($tocat->contextid); require_capability('moodle/question:add', $tocat->context); $tocoursefilesid = get_filesdir_from_context($tocat->context); $urls = array(); if ($tocoursefilesid == SITEID) { $toareaname = get_string('filesareasite', 'question'); } else { $toareaname = get_string('filesareacourse', 'question'); } $fromcoursefilesid = 0; foreach (array_keys($questions) as $id) { question_require_capability_on($questions[$id], 'move'); get_question_options($questions[$id]); $questions[$id]->context = get_context_instance_by_id($questions[$id]->contextid); $thisfilesid = get_filesdir_from_context($questions[$id]->context); if ($fromcoursefilesid && $thisfilesid != $fromcoursefilesid) { error('You can\'t use this script to move questions that have files associated with them from different areas.'); } else { $fromcoursefilesid = $thisfilesid; } if ($tocoursefilesid != $fromcoursefilesid) { $urls = array_merge_recursive($urls, $QTYPES[$questions[$id]->qtype]->find_file_links($questions[$id], $fromcoursefilesid)); } } $brokenurls = array(); foreach (array_keys($urls) as $url) { if (!file_exists($CFG->dataroot . "/{$fromcoursefilesid}/" . $url)) { $brokenurls[] = $url;
/** * Generates the LaTeX question form for an offlinequiz group. * * @param question_usage_by_activity $templateusage the template question usage for this offline group * @param object $offlinequiz The offlinequiz object * @param object $group the offline group object * @param int $courseid the ID of the Moodle course * @param object $context the context of the offline quiz. * @param boolean correction if true the correction form is generated. * @return stored_file instance, the generated PDF file. */ function offlinequiz_create_latex_question(question_usage_by_activity $templateusage, $offlinequiz, $group, $courseid, $context, $correction = false) { global $CFG, $DB, $OUTPUT; $letterstr = 'abcdefghijklmnopqrstuvwxyz'; $groupletter = strtoupper($letterstr[$group->number - 1]); $coursecontext = context_course::instance($courseid); $course = $DB->get_record('course', array('id' => $courseid)); $title = format_text($offlinequiz->name, FORMAT_HTML); $title .= ", " . get_string('group') . $groupletter; // Load all the questions needed for this offline quiz group. $sql = "SELECT q.*, c.contextid, ogq.page, ogq.slot, ogq.maxmark\n FROM {offlinequiz_group_questions} ogq,\n {question} q,\n {question_categories} c\n WHERE ogq.offlinequizid = :offlinequizid\n AND ogq.offlinegroupid = :offlinegroupid\n AND q.id = ogq.questionid\n AND q.category = c.id\n ORDER BY ogq.slot ASC "; $params = array('offlinequizid' => $offlinequiz->id, 'offlinegroupid' => $group->id); // Load the questions. $questions = $DB->get_records_sql($sql, $params); if (!$questions) { echo $OUTPUT->box_start(); echo $OUTPUT->error_text(get_string('noquestionsfound', 'offlinequiz', $groupletter)); echo $OUTPUT->box_end(); return; } // Load the question type specific information. if (!get_question_options($questions)) { print_error('Could not load question options'); } $number = 1; // We need a mapping from question IDs to slots, assuming that each question occurs only once. $slots = $templateusage->get_slots(); $latexforquestions = '\\begin{enumerate}' . "\n"; // If shufflequestions has been activated we go through the questions in the order determined by // the template question usage. if ($offlinequiz->shufflequestions) { foreach ($slots as $slot) { $slotquestion = $templateusage->get_question($slot); $currentquestionid = $slotquestion->id; $question = $questions[$currentquestionid]; $questiontext = offlinequiz_convert_html_to_latex($question->questiontext); $latexforquestions .= '\\item ' . $questiontext . "\n"; if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') { // There is only a slot for multichoice questions. $attempt = $templateusage->get_question_attempt($slot); $order = $slotquestion->get_order($attempt); // Order of the answers. $latexforquestions .= '\\begin{enumerate}' . " \n"; foreach ($order as $key => $answer) { $latexforquestions .= offlinequiz_get_answer_latex($question, $answer); } $latexforquestions .= '\\end{enumerate}' . "\n"; $infostr = offlinequiz_get_question_infostring($offlinequiz, $question); if ($infostr) { $latexforquestions .= $infostr . "\n"; } } } $latexforquestions .= '\\end{enumerate}' . "\n"; } else { // No shufflequestions, so go through the questions as they have been added to the offlinequiz group. // We also have to show description questions that are not in the template. // First, compute mapping questionid -> slotnumber. $questionslots = array(); foreach ($slots as $slot) { $questionslots[$templateusage->get_question($slot)->id] = $slot; } foreach ($questions as $question) { $currentquestionid = $question->id; $questiontext = $question->questiontext; $questiontext = offlinequiz_convert_html_to_latex($question->questiontext); if ($question->qtype == 'description') { $latexforquestions .= "\n" . '\\ ' . $questiontext . "\n"; } else { $latexforquestions .= '\\item ' . $questiontext . "\n"; } if ($question->qtype == 'multichoice' || $question->qtype == 'multichoiceset') { $slot = $questionslots[$currentquestionid]; // There is only a slot for multichoice questions. $slotquestion = $templateusage->get_question($slot); $attempt = $templateusage->get_question_attempt($slot); $order = $slotquestion->get_order($attempt); // Order of the answers. $latexforquestions .= '\\begin{enumerate}' . " \n"; foreach ($order as $key => $answer) { $latexforquestions .= offlinequiz_get_answer_latex($question, $answer); } $latexforquestions .= '\\end{enumerate}' . "\n"; $infostr = offlinequiz_get_question_infostring($offlinequiz, $question); if ($infostr) { $latexforquestions .= $infostr . "\n"; } } } $latexforquestions .= '\\end{enumerate}' . "\n"; } $a = array(); $a['latexforquestions'] = $latexforquestions; $a['coursename'] = offlinequiz_convert_html_to_latex($course->fullname); $a['groupname'] = $groupletter; // TODO exceptionhandling? if ($offlinequiz->time) { $a['date'] = ', ' . userdate($offlinequiz->time); } else { $a['date'] = ''; } $latex = get_string('questionsheetlatextemplate', 'offlinequiz', $a); $fs = get_file_storage(); $fileprefix = get_string('fileprefixform', 'offlinequiz'); // Prepare file record object. $date = usergetdate(time()); $timestamp = sprintf('%04d%02d%02d_%02d%02d%02d', $date['year'], $date['mon'], $date['mday'], $date['hours'], $date['minutes'], $date['seconds']); $fileinfo = array('contextid' => $context->id, 'component' => 'mod_offlinequiz', 'filearea' => 'pdfs', 'filepath' => '/', 'itemid' => 0, 'filename' => $fileprefix . '_' . $groupletter . '_' . $timestamp . '.tex'); if ($oldfile = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'], $fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename'])) { $oldfile->delete(); } $file = $fs->create_file_from_string($fileinfo, $latex); return $file; }
require_login($courseid, false); $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid); $module = null; $cm = null; } else { error('Need to pass courseid or cmid to this script.'); } $contexts = new question_edit_contexts($thiscontext); if (!$returnurl) { $returnurl = "{$CFG->wwwroot}/question/edit.php?courseid={$COURSE->id}"; } if ($id) { if (!($question = get_record('question', 'id', $id))) { print_error('questiondoesnotexist', 'question', $returnurl); } get_question_options($question); } else { if ($categoryid && $qtype) { // only for creating new questions $question = new stdClass(); $question->category = $categoryid; $question->qtype = $qtype; } else { print_error('notenoughdatatoeditaquestion', 'question', $returnurl); } } // Validate the question category. if (!($category = get_record('question_categories', 'id', $question->category))) { print_error('categorydoesnotexist', 'question', $returnurl); } //permissions
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $SESSION, $USER, $db, $QTYPES; $action = optional_param('action', 'viewquestions', PARAM_ALPHA); $questionid = optional_param('questionid', 0, PARAM_INT); $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $context)) { notify(get_string('gradingnotallowed', 'quiz_grading')); return true; } if (!empty($questionid)) { if (!($question = get_record('question', 'id', $questionid))) { error("Question with id {$questionid} not found"); } $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id); // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { error("Unable to load questiontype specific question information"); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib if ($data = data_submitted()) { // post data submitted, process it confirm_sesskey(); // now go through all of the responses and save them. foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt if (!($attempt = get_record('quiz_attempts', 'uniqueid', $uniqueid))) { error('No such attempt ID exists'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']); // If the state has changed save it and update the quiz grade if ($state->changed) { save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } notify(get_string('changessaved', 'quiz'), 'notifysuccess'); } // our 3 different views // the first one displays all of the manually graded questions in the quiz // with the number of ungraded attempts for each question // the second view displays the users who have answered the essay question // and all of their attempts at answering the question // the third prints the question with a comment // and grade form underneath it switch ($action) { case 'viewquestions': $this->view_questions($quiz); break; case 'viewquestion': $this->view_question($quiz, $question); break; case 'grade': $this->print_questions_and_form($quiz, $question); break; } return true; }
$thiscontext = context_course::instance($courseid); $module = null; $cm = null; } else { print_error('missingcourseorcmid', 'question'); } $contexts = new question_edit_contexts($thiscontext); $PAGE->set_pagelayout('admin'); if (optional_param('addcancel', false, PARAM_BOOL)) { redirect($returnurl); } if ($id) { if (!($question = $DB->get_record('question', array('id' => $id)))) { print_error('questiondoesnotexist', 'question', $returnurl); } get_question_options($question, true); } else { if ($categoryid && $qtype) { // only for creating new questions $question = new stdClass(); $question->category = $categoryid; $question->qtype = $qtype; $question->createdby = $USER->id; // Check that users are allowed to create this question type at the moment. if (!question_bank::qtype_enabled($qtype)) { print_error('cannotenable', 'question', $returnurl, $qtype); } } else { if ($categoryid) { // Category, but no qtype. They probably came from the addquestion.php // script without choosing a question type. Send them back.
function game_sudoku_getquestions($questionlist) { global $DB; // Load the questions if (!($questions = $DB->get_records_select('question', "id IN ({$questionlist})"))) { print_error(get_string('no_questions', 'game')); } // Load the question type specific information if (!get_question_options($questions)) { print_error('Could not load question options'); } return $questions; }
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; }
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; }
/** * Fully load some or all of the questions for this quiz. You must call * {@link preload_questions()} first. * * @param array $questionids question ids of the questions to load. null for all. */ public function load_questions($questionids = null) { if ($this->questions === null) { throw new coding_exception('You must call preload_questions before calling load_questions.'); } if (is_null($questionids)) { $questionids = array_keys($this->questions); } $questionstoprocess = array(); foreach ($questionids as $id) { if (array_key_exists($id, $this->questions)) { $questionstoprocess[$id] = $this->questions[$id]; } } get_question_options($questionstoprocess); }
function game_sudoku_getquestions($questionlist) { // Load the questions if (!($questions = get_records_select('question', "id IN ({$questionlist})"))) { error(get_string('noquestionsfound', 'quiz')); } // Load the question type specific information if (!get_question_options($questions)) { error('Could not load question options'); } return $questions; }