/** * * * @param int|null $userid * * @return array An array of group objects keyed by groupid */ public function get_user_groups($userid = null) { global $USER; // assume current user when none specified if (empty($userid)) { $userid = $USER->id; } if (!empty($this->rtq->getRTQ()->grouping)) { return groups_get_all_groups($this->rtq->getCourse()->id, $userid, $this->rtq->getRTQ()->grouping); } else { return array(); // return empty array when there is no grouping } }
/** * Gets the data for the table * * @return array $data The array of data to show */ protected function get_data() { global $DB, $USER; $data = array(); $sessions = $this->rtq->get_sessions(); foreach ($sessions as $session) { /** @var \mod_activequiz\activequiz_session $session */ $sessionattempts = $session->getall_attempts(false, 'closed', $USER->id); foreach ($sessionattempts as $sattempt) { $ditem = new \stdClass(); $ditem->attemptid = $sattempt->id; $ditem->sessionid = $sattempt->sessionid; $ditem->sessionname = $session->get_session()->name; if ($this->rtq->group_mode()) { $ditem->group = $this->rtq->get_groupmanager()->get_group_name($sattempt->forgroupid); } $ditem->timestart = $sattempt->timestart; $ditem->timefinish = $sattempt->timefinish; $ditem->grade = number_format($this->rtq->get_grader()->calculate_attempt_grade($sattempt), 2); $ditem->totalgrade = $this->rtq->getRTQ()->scale; $data[$sattempt->id] = $ditem; } } return $data; }
/** * Function to provide a display of how many open attempts have responded * * @param array $notresponded Array of the people who haven't responded * @param int $total * * @return string HTML fragment for the amount responded */ public function respondedbox($notresponded, $total) { $output = ''; $output .= html_writer::start_div(); $output .= html_writer::start_div('respondedbox', array('id' => 'respondedbox')); $output .= html_writer::tag('h3', get_string('notresponded', 'activequiz'), array('class' => 'inline')); $output .= html_writer::div(' ' . count($notresponded) . '/' . $total, 'inline'); $output .= html_writer::end_div(); // output the list of students, but only if we're not in anonymous mode if ($this->rtq->getRTQ()->anonymizeresponses == 0) { $output .= html_writer::start_div(); $output .= html_writer::alist($notresponded, array('id' => 'notrespondedlist')); $output .= html_writer::end_div(); } $output .= html_writer::end_div(); return $output; }
/** * Gets the data for the table * * @return array $data The array of data to show */ protected function get_data() { global $DB; $data = array(); $attempts = $this->session->getall_attempts(true); $userids = array(); foreach ($attempts as $attempt) { $userids[] = $attempt->userid; } // get user records to get the full name if (!empty($userids)) { list($useridsql, $params) = $DB->get_in_or_equal($userids); $sql = 'SELECT * FROM {user} WHERE id ' . $useridsql; $userrecs = $DB->get_records_sql($sql, $params); } else { $userrecs = array(); } foreach ($attempts as $attempt) { /** @var \mod_activequiz\activequiz_attempt $attempt */ $ditem = new \stdClass(); $ditem->attemptid = $attempt->id; $ditem->sessionid = $attempt->sessionid; if ($this->rtq->group_mode()) { $ditem->userid = $attempt->userid; $ditem->takenby = fullname($userrecs[$attempt->userid]); $ditem->groupname = $this->rtq->get_groupmanager()->get_group_name($attempt->forgroupid); } else { $ditem->userid = $attempt->userid; $userrec = $userrecs[$attempt->userid]; $ditem->username = fullname($userrec); } $ditem->attemptno = $attempt->attemptnum; $ditem->preview = $attempt->preview; $ditem->status = $attempt->getStatus(); $ditem->timestart = $attempt->timestart; $ditem->timefinish = $attempt->timefinish; $ditem->timemodified = $attempt->timemodified; $ditem->grade = number_format($this->rtq->get_grader()->calculate_attempt_grade($attempt), 2); $ditem->totalgrade = $this->rtq->getRTQ()->scale; $data[$attempt->id] = $ditem; } return $data; }
/** * Figures out the grades for group members if the quiz was taken in group mode * * IMPORTANT: IF A USER IS IN MORE THAN 1 GROUP FOR THE SPECIFIED GROUPING, THIS FUNCTION * WILL TAKE THE HIGHER OF THE 2 GRADES THAT WILL BE GIVEN TO THEM * * @param array $sessionsgrades Array of session grades being built for user * @param int $forgroupid * @param number $grade * @param int $uid * @param int $sessionid */ protected function calculate_group_grades(&$sessionsgrades, $forgroupid, $grade, $uid, $sessionid) { if (empty($forgroupid)) { // just add the grade for the userid to the sessiongrades $sessionsgrades[$uid][$sessionid] = $grade; return; } // get the group attendance if we have it if ($this->rtq->getRTQ()->groupattendance == 1) { $groupattendance = $this->rtq->get_groupmanager()->get_attendance(null, null, null, $forgroupid); foreach ($groupattendance as $gattendanceuser) { $this->add_group_grade($sessionsgrades, $gattendanceuser->userid, $sessionid, $grade); } } else { $groupusers = $this->rtq->get_groupmanager()->get_group_members($forgroupid); foreach ($groupusers as $guser) { $this->add_group_grade($sessionsgrades, $guser->id, $sessionid, $grade); } } }
/** * Orders the real time questions and then * puts question bank ordered questions into the qbankorderedquestions var * */ private function init_qbank_questions() { global $DB; // start by ordering the RTQ question ids into an array $questionorder = $this->rtq->getRTQ()->questionorder; // generate empty array for ordered questions for no question order if (empty($questionorder)) { $this->qbankOrderedQuestions = array(); return; } else { // otherwise explode it and continue on $questionorder = explode(',', $questionorder); } // using the question order saved in rtq object, get the qbank question ids from the rtq questions $orderedquestionids = array(); foreach ($questionorder as $qorder) { // store the rtq question id as the key so that it can be used later when adding question time to // question bank question object $orderedquestionids[$qorder] = $this->rtqQuestions[$qorder]->questionid; } // get qbank questions based on the question ids from the RTQ questions table list($sql, $params) = $DB->get_in_or_equal($orderedquestionids); $query = 'SELECT * FROM {question} WHERE id ' . $sql; $questions = $DB->get_records_sql($query, $params); // Now order the qbank questions based on the order that we got above $qbankOrderedQuestions = array(); foreach ($orderedquestionids as $rtqqid => $questionid) { // use the ordered question ids we got earlier if (!empty($questions[$questionid])) { // create realtime quiz question and add it to the array $quizquestion = new \mod_activequiz\activequiz_question($rtqqid, $this->rtqQuestions[$rtqqid]->notime, $this->rtqQuestions[$rtqqid]->questiontime, $this->rtqQuestions[$rtqqid]->tries, $this->rtqQuestions[$rtqqid]->points, $this->rtqQuestions[$rtqqid]->showhistoryduringquiz, $questions[$questionid]); $qbankOrderedQuestions[$rtqqid] = $quizquestion; // add question to the ordered questions } } $this->qbankOrderedQuestions = $qbankOrderedQuestions; }
/** * Process a comment for a particular question on an attempt * * @param int $slot * @param \mod_activequiz\activequiz $rtq * * @return bool */ public function process_comment($slot = null, $rtq) { global $DB; // if there is no slot return false if (empty($slot)) { return false; } // Process any data that was submitted. if (data_submitted() && confirm_sesskey()) { if (optional_param('submit', false, PARAM_BOOL) && \question_engine::is_manual_grade_in_range($this->attempt->questionengid, $slot)) { $transaction = $DB->start_delegated_transaction(); $this->quba->process_all_actions(time()); $this->save(); $transaction->allow_commit(); // Trigger event for question manually graded $params = array('objectid' => $this->quba->get_question($slot)->id, 'courseid' => $rtq->getCourse()->id, 'context' => $rtq->getContext(), 'other' => array('rtqid' => $rtq->getRTQ()->id, 'attemptid' => $this->attempt->id, 'slot' => $slot, 'sessionid' => $this->attempt->sessionid)); $event = \mod_activequiz\event\question_manually_graded::create($params); $event->trigger(); return true; } } return false; }
/** * Gets the data for the table * * @return array $data The array of data to show */ protected function get_data() { global $DB, $CFG; $data = array(); $grades = \mod_activequiz\utils\grade::get_user_grade($this->rtq->getRTQ()); $userids = array(); foreach ($grades as $grade) { $userids[] = $grade->userid; } // get user records to get the full name if (!empty($userids)) { list($useridsql, $params) = $DB->get_in_or_equal($userids); $sql = 'SELECT * FROM {user} WHERE id ' . $useridsql; $userrecs = $DB->get_records_sql($sql, $params); } else { $userrecs = array(); } foreach ($grades as $grade) { // check to see if the grade is for a gradebook role. if not then their grade // shouldn't show up here $add = false; if ($roles = get_user_roles($this->rtq->getContext(), $grade->userid)) { $gradebookroles = explode(',', $CFG->gradebookroles); foreach ($roles as $role) { if (in_array($role->roleid, $gradebookroles)) { // if they have at least one gradebook role show their grade $add = true; } } if ($add === false) { // don't show grade for non gradebook role continue; } } else { // if there are no given roles for the context, then they aren't students continue; } $gradedata = new \stdClass(); $gradedata->fullname = fullname($userrecs[$grade->userid]); if ($this->rtq->group_mode()) { $groups = $this->rtq->get_groupmanager()->get_user_groups($grade->userid); if (!empty($groups)) { $groupstring = ''; foreach ($groups as $group) { if (strlen($groupstring) > 0) { // add a comma space if we're back in the foreach for a second or more time $groupstring .= ', '; } $groupstring .= $this->rtq->get_groupmanager()->get_group_name($group->id); } $gradedata->group = $groupstring; } else { $gradedata->group = ' - '; } } $gradedata->grade = number_format($grade->rawgrade, 2); $gradedata->timemodified = $grade->dategraded; $data[] = $gradedata; } return $data; }
/** * Initializes quiz javascript and strings for javascript when on the * quiz view page, or the "quizstart" action * * @param \mod_activequiz\activequiz_attempt $attempt * @param \mod_activequiz\activequiz_session $session * @throws moodle_exception throws exception when invalid question on the attempt is found */ public function init_quiz_js($attempt, $session) { global $USER, $CFG; // include classList javascript to add the class List HTML5 for compatibility // below IE 10 $this->page->requires->js('/mod/activequiz/js/classList.js'); $this->page->requires->js('/mod/activequiz/js/core.js'); // add window.onload script manually to handle removing the loading mask echo html_writer::start_tag('script', array('type' => 'text/javascript')); echo <<<EOD (function preLoad(){ window.addEventListener('load', function(){activequiz.quiz_page_loaded();}, false); }()); EOD; echo html_writer::end_tag('script'); if ($this->rtq->is_instructor()) { $this->page->requires->js('/mod/activequiz/js/instructor.js'); } else { $this->page->requires->js('/mod/activequiz/js/student.js'); } // next set up a class to pass to js for js info $jsinfo = new stdClass(); $jsinfo->sesskey = sesskey(); $jsinfo->siteroot = $CFG->wwwroot; $jsinfo->rtqid = $this->rtq->getRTQ()->id; $jsinfo->sessionid = $session->get_session()->id; $jsinfo->attemptid = $attempt->id; $jsinfo->slots = $attempt->getSlots(); $jsinfo->isinstructor = $this->rtq->is_instructor() ? 'true' : 'false'; // manually create the questions stdClass as we can't support JsonSerializable yet $questions = array(); foreach ($attempt->get_questions() as $q) { /** @var \mod_activequiz\activequiz_question $q */ $question = new stdClass(); $question->id = $q->getId(); $question->questiontime = $q->getQuestionTime(); $question->tries = $q->getTries(); $question->question = $q->getQuestion(); $question->slot = $attempt->get_question_slot($q); // if the slot is false, throw exception for invalid question on quiz attempt if ($question->slot === false) { $a = new stdClass(); $a->questionname = $q->getQuestion()->name; throw new moodle_exception('invalidquestionattempt', 'mod_activequiz', '', $a, 'invalid slot when building questions array on quiz renderer'); } $questions[$question->slot] = $question; } $jsinfo->questions = $questions; // resuming quiz feature // this will check if the session has started already and print out $jsinfo->resumequiz = 'false'; if ($session->get_session()->status != 'notrunning') { $sessionstatus = $session->get_session()->status; $currentquestion = $session->get_session()->currentquestion; $nextstarttime = $session->get_session()->nextstarttime; if ($sessionstatus == 'running' && !empty($currentquestion)) { // we're in a currently running question $jsinfo->resumequiz = 'true'; $jsinfo->resumequizstatus = $sessionstatus; $jsinfo->resumequizcurrentquestion = $currentquestion; $nextQuestion = $this->rtq->get_questionmanager()->get_question_with_slot($session->get_session()->currentqnum, $attempt); if ($nextstarttime > time()) { // we're wating for question $jsinfo->resumequizaction = 'waitforquestion'; $jsinfo->resumequizdelay = $session->get_session()->nextstarttime - time(); $jsinfo->resumequizquestiontime = $nextQuestion->getQuestionTime(); } else { $jsinfo->resumequizaction = 'startquestion'; // how much time has elapsed since start time // first check if the question has a time limit if ($nextQuestion->getNoTime()) { $jsinfo->resumequizquestiontime = 0; } else { // otherwise figure out how much time is left $timeelapsed = time() - $nextstarttime; $timeLeft = $nextQuestion->getQuestionTime() - $timeelapsed; $jsinfo->resumequizquestiontime = $timeLeft; } } } else { if ($sessionstatus == 'reviewing' || $sessionstatus == 'endquestion') { // if we're reviewing, resume with quiz info of reviewing and just let // set interval capture next question start time $jsinfo->resumequiz = 'true'; $jsinfo->resumequizaction = 'reviewing'; $jsinfo->resumequizstatus = $sessionstatus; $jsinfo->resumequizcurrentquestion = $currentquestion; if ($attempt->lastquestion) { $jsinfo->lastquestion = 'true'; } else { $jsinfo->lastquestion = 'false'; } } } } // print jsinfo to javascript echo html_writer::start_tag('script', array('type' => 'text/javascript')); echo "rtqinitinfo = " . json_encode($jsinfo); echo html_writer::end_tag('script'); // add strings for js $this->page->requires->strings_for_js(array('waitforquestion', 'gatheringresults', 'feedbackintro', 'nofeedback', 'closingsession', 'sessionclosed', 'trycount', 'timertext', 'waitforrevewingend', 'show_correct_answer', 'hide_correct_answer', 'hidestudentresponses', 'showstudentresponses', 'hidenotresponded', 'shownotresponded'), 'activequiz'); // finally allow question modifiers to add their own css/js $this->rtq->call_question_modifiers('add_js', null); }