/** * The process grading function handles admin grading submissions. * * This function is hooked on to admin_init. It simply accepts * the grades as the Grader selected theme and saves the total grade and * individual question grades. * * @return bool */ public function admin_process_grading_submission() { // NEEDS REFACTOR/OPTIMISING, such as combining the various meta data stored against the sensei_user_answer entry if (!isset($_POST['sensei_manual_grade']) || !wp_verify_nonce($_POST['_wp_sensei_manual_grading_nonce'], 'sensei_manual_grading') || !isset($_GET['quiz_id']) || $_GET['quiz_id'] != $_POST['sensei_manual_grade']) { return false; //exit and do not grade } $quiz_id = $_GET['quiz_id']; $user_id = $_GET['user']; global $woothemes_sensei; $questions = WooThemes_Sensei_Utils::sensei_get_quiz_questions($quiz_id); $quiz_lesson_id = $woothemes_sensei->quiz->get_lesson_id($quiz_id); $quiz_grade = 0; $count = 0; $quiz_grade_total = $_POST['quiz_grade_total']; $all_question_grades = array(); $all_answers_feedback = array(); foreach ($questions as $question) { ++$count; $question_id = $question->ID; if (isset($_POST['question_' . $question_id])) { $question_grade = 0; if ($_POST['question_' . $question_id] == 'right') { $question_grade = $_POST['question_' . $question_id . '_grade']; } // add data to the array that will, after the loop, be stored on the lesson status $all_question_grades[$question_id] = $question_grade; // tally up the total quiz grade $quiz_grade += $question_grade; } // endif // Question answer feedback / notes $question_feedback = ''; if (isset($_POST['questions_feedback'][$question_id])) { $question_feedback = wp_unslash($_POST['questions_feedback'][$question_id]); } $all_answers_feedback[$question_id] = $question_feedback; } // end for each $questions //store all question grades on the lesson status $woothemes_sensei->quiz->set_user_grades($all_question_grades, $quiz_lesson_id, $user_id); //store the feedback from grading $woothemes_sensei->quiz->save_user_answers_feedback($all_answers_feedback, $quiz_lesson_id, $user_id); // $_POST['all_questions_graded'] is set when all questions have been graded // in the class sensei grading user quiz -> display() if ($_POST['all_questions_graded'] == 'yes') { // set the users total quiz grade if (0 < intval($quiz_grade_total)) { $grade = abs(round(doubleval($quiz_grade) * 100 / $quiz_grade_total, 2)); } else { $grade = 0; } WooThemes_Sensei_Utils::sensei_grade_quiz($quiz_id, $grade, $user_id); // Duplicating what Frontend->sensei_complete_quiz() does $pass_required = get_post_meta($quiz_id, '_pass_required', true); $quiz_passmark = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2)); $lesson_metadata = array(); if ($pass_required) { // Student has reached the pass mark and lesson is complete if ($quiz_passmark <= $grade) { $lesson_status = 'passed'; } else { $lesson_status = 'failed'; } // End If Statement } else { $lesson_status = 'graded'; } $lesson_metadata['grade'] = $grade; // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz()" above WooThemes_Sensei_Utils::update_lesson_status($user_id, $quiz_lesson_id, $lesson_status, $lesson_metadata); if (in_array($lesson_status, array('passed', 'graded'))) { /** * Summary. * * Description. * * @since 1.7.0 * * @param int $user_id * @param int $quiz_lesson_id */ do_action('sensei_user_lesson_end', $user_id, $quiz_lesson_id); } // end if in_array } // end if $_POST['all_que... if (isset($_POST['sensei_grade_next_learner']) && strlen($_POST['sensei_grade_next_learner']) > 0) { $load_url = add_query_arg(array('message' => 'graded')); } elseif (isset($_POST['_wp_http_referer'])) { $load_url = add_query_arg(array('message' => 'graded'), $_POST['_wp_http_referer']); } else { $load_url = add_query_arg(array('message' => 'graded')); } wp_safe_redirect(esc_url_raw($load_url)); exit; }
public function process_grading() { // NEEDS REFACTOR/OPTIMISING, such as combining the various meta data stored against the sensei_user_answer entry if (isset($_POST['sensei_manual_grade']) && isset($_GET['quiz_id'])) { // error_log( __CLASS__ . ':' . __FUNCTION__ . ':' . print_r($_POST, true)); $quiz_id = $_GET['quiz_id']; $user_id = $_GET['user']; $verify_nonce = wp_verify_nonce($_POST['_wp_sensei_manual_grading_nonce'], 'sensei_manual_grading'); if ($verify_nonce && $quiz_id == $_POST['sensei_manual_grade']) { $questions = WooThemes_Sensei_Utils::sensei_get_quiz_questions($quiz_id); $quiz_grade = 0; $count = 0; $quiz_grade_total = $_POST['quiz_grade_total']; foreach ($questions as $question) { ++$count; $question_id = $question->ID; if (isset($_POST['question_' . $question_id])) { $correct = false; $question_grade = 0; $question_total_grade = $_POST['question_total_grade']; if ($_POST['question_' . $question_id] == 'right') { $correct = true; $question_grade = $_POST['question_' . $question_id . '_grade']; } $activity_logged = WooThemes_Sensei_Utils::sensei_grade_question($question_id, $question_grade, $user_id); $quiz_grade += $question_grade; } else { WooThemes_Sensei_Utils::sensei_delete_question_grade($question_id); } // WP slashes all incoming data regardless of Magic Quotes setting (see wp_magic_quotes()), but // as an answer note is not direct post_content it won't have slashes removed, so we need to do it $answer_notes = wp_unslash($_POST['question_' . $question_id . '_notes']); if (!$answer_notes || $answer_notes == '') { $answer_notes = ''; } WooThemes_Sensei_Utils::sensei_add_answer_notes($question_id, $user_id, $answer_notes); } if ($_POST['all_questions_graded'] == 'yes') { $grade = abs(round(doubleval($quiz_grade) * 100 / $quiz_grade_total, 2)); $activity_logged = WooThemes_Sensei_Utils::sensei_grade_quiz($quiz_id, $grade, $user_id); // Duplicating what Frontend->sensei_complete_quiz() does $quiz_lesson_id = absint(get_post_meta($quiz_id, '_quiz_lesson', true)); $pass_required = get_post_meta($quiz_id, '_pass_required', true); $quiz_passmark = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2)); $lesson_metadata = array(); if ($pass_required) { // Student has reached the pass mark and lesson is complete if ($quiz_passmark <= $grade) { $lesson_status = 'passed'; } else { $lesson_status = 'failed'; } // End If Statement } else { $lesson_status = 'graded'; } $lesson_metadata['grade'] = $grade; // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz()" above WooThemes_Sensei_Utils::update_lesson_status($user_id, $quiz_lesson_id, $lesson_status, $lesson_metadata); switch ($lesson_status) { case 'passed': case 'graded': do_action('sensei_user_lesson_end', $user_id, $quiz_lesson_id); break; } } if (isset($_POST['sensei_grade_next_learner']) && strlen($_POST['sensei_grade_next_learner']) > 0) { $load_url = add_query_arg(array('message' => 'graded')); } elseif (isset($_POST['_wp_http_referer'])) { $load_url = add_query_arg(array('message' => 'graded'), $_POST['_wp_http_referer']); } else { $load_url = add_query_arg(array('message' => 'graded')); } wp_safe_redirect($load_url); exit; } } }
/** * This tests Sensei_Courses::get_completion_percentage * @since 1.8.0 */ public function testGetCompletionPercentage() { //does the function exist? $this->assertTrue(method_exists('WooThemes_Sensei_Course', 'get_completion_percentage'), 'The course class get_completion_percentage function does not exist.'); // setup the test $test_user_id = wp_create_user('testGetCompletionPercentage', 'testGetCompletionPercentage', '*****@*****.**'); $test_lessons = $this->factory->get_lessons(); $test_course_id = $this->factory->get_random_course_id(); remove_all_actions('sensei_user_course_start'); WooThemes_Sensei_Utils::user_start_course($test_user_id, $test_course_id); // add lessons to the course foreach ($test_lessons as $lesson_id) { add_post_meta($lesson_id, '_lesson_course', intval($test_course_id)); } // complete 3 lessons and check if the correct percentage returns $i = 0; for ($i = 0; $i < 3; $i++) { WooThemes_Sensei_Utils::update_lesson_status($test_user_id, $test_lessons[$i], 'complete'); } $expected_percentage = 3 / count($test_lessons) * 100; $this->assertEquals($expected_percentage, Sensei()->course->get_completion_percentage($test_course_id, $test_user_id), 'Course completed percentage is not accurate'); // complete all lessons foreach ($test_lessons as $lesson_id) { WooThemes_Sensei_Utils::update_lesson_status($test_user_id, $lesson_id, 'complete'); } //all lessons should no be completed $this->assertEquals(100, Sensei()->course->get_completion_percentage($test_course_id, $test_user_id), 'Course completed percentage is not accurate'); }
public function sensei_complete_quiz() { global $post, $woothemes_sensei, $current_user; // Default grade $grade = 0; // Get Quiz Questions $lesson_quiz_questions = $woothemes_sensei->post_types->lesson->lesson_quiz_questions($post->ID); $quiz_lesson_id = absint(get_post_meta($post->ID, '_quiz_lesson', true)); // Get quiz grade type $quiz_grade_type = get_post_meta($post->ID, '_quiz_grade_type', true); // Get quiz pass setting $pass_required = get_post_meta($post->ID, '_pass_required', true); // Get quiz pass mark $quiz_passmark = abs(round(doubleval(get_post_meta($post->ID, '_quiz_passmark', true)), 2)); // Handle Quiz Completion if (isset($_POST['quiz_complete']) && wp_verify_nonce($_POST['woothemes_sensei_complete_quiz_noonce'], 'woothemes_sensei_complete_quiz_noonce')) { $sanitized_submit = esc_html($_POST['quiz_complete']); $questions_asked = array_filter(array_map('absint', $_POST['questions_asked'])); $questions_asked_string = implode(',', $questions_asked); switch ($sanitized_submit) { case apply_filters('sensei_complete_quiz_text', __('Complete Quiz', 'woothemes-sensei')): // Mark the Lesson as in-progress (if it isn't already), the entry is needed for WooThemes_Sensei_Utils::sensei_grade_quiz_auto() (optimise at some point?) $activity_logged = WooThemes_Sensei_Utils::sensei_start_lesson($quiz_lesson_id); $lesson_status = 'ungraded'; // Default when completing a quiz // Save questions that were asked in this quiz if (!empty($questions_asked_string)) { update_comment_meta($activity_logged, 'questions_asked', $questions_asked_string); } // Save Quiz Answers if (isset($_POST['sensei_question'])) { WooThemes_Sensei_Utils::sensei_save_quiz_answers($_POST['sensei_question']); } // Grade quiz // 3rd arg is count of total number of questions but it's not used by sensei_grade_quiz_auto() $grade = WooThemes_Sensei_Utils::sensei_grade_quiz_auto($post->ID, $_POST['sensei_question'], count($lesson_quiz_questions), $quiz_grade_type); $lesson_metadata = array(); // Get Lesson Grading Setting if (is_wp_error($grade) || 'auto' != $quiz_grade_type) { $lesson_status = 'ungraded'; // Quiz is manually graded and this was a user submission } else { // Quiz has been automatically Graded if ($pass_required) { // Student has reached the pass mark and lesson is complete if ($quiz_passmark <= $grade) { $lesson_status = 'passed'; } else { $lesson_status = 'failed'; } // End If Statement } else { $lesson_status = 'graded'; } $lesson_metadata['grade'] = $grade; // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz_auto()" above } WooThemes_Sensei_Utils::update_lesson_status($current_user->ID, $quiz_lesson_id, $lesson_status, $lesson_metadata); switch ($lesson_status) { case 'passed': case 'graded': do_action('sensei_user_lesson_end', $current_user->ID, $quiz_lesson_id); break; } do_action('sensei_user_quiz_submitted', $current_user->ID, $post->ID, $grade, $quiz_passmark, $quiz_grade_type); break; case apply_filters('sensei_save_quiz_text', __('Save Quiz', 'woothemes-sensei')): $activity_logged = WooThemes_Sensei_Utils::sensei_start_lesson($quiz_lesson_id); if ($activity_logged) { // Save questions that were asked in this quiz if (!empty($questions_asked_string)) { update_comment_meta($activity_logged, 'questions_asked', $questions_asked_string); } if (isset($_POST['sensei_question'])) { WooThemes_Sensei_Utils::sensei_save_quiz_answers($_POST['sensei_question']); } } // Need message in case the data wasn't saved? $this->messages = '<div class="sensei-message note">' . apply_filters('sensei_quiz_saved_text', __('Quiz Saved Successfully.', 'woothemes-sensei')) . '</div>'; break; case apply_filters('sensei_reset_quiz_text', __('Reset Quiz', 'woothemes-sensei')): // Don't want to remove the lesson status (such as start meta data etc), just remove the answers, the questions asked meta and any grade meta // Delete quiz answers, this auto deletes the corresponding meta data, such as the question/answer grade WooThemes_Sensei_Utils::sensei_delete_quiz_answers($post->ID, $user_id); WooThemes_Sensei_Utils::update_lesson_status($current_user->ID, $quiz_lesson_id, 'in-progress', array('questions_asked' => '', 'grade' => '')); // Run any action on quiz/lesson reset (previously this didn't occur on resetting a quiz, see resetting a lesson in sensei_complete_lesson() do_action('sensei_user_lesson_reset', $current_user->ID, $quiz_lesson_id); $this->messages = '<div class="sensei-message note">' . apply_filters('sensei_quiz_reset_text', __('Quiz Reset Successfully.', 'woothemes-sensei')) . '</div>'; break; default: // Nothing break; } // End Switch Statement // Refresh page to avoid re-posting ?> <script type="text/javascript"> window.location = '<?php echo get_permalink($post->ID); ?> '; </script> <?php } // End If Statement, submission of quiz $this->data = new stdClass(); // Get latest quiz answers and grades $user_quizzes = $this->sensei_get_user_quiz_answers($post->ID); $user_lesson_status = WooThemes_Sensei_Utils::user_lesson_status($quiz_lesson_id, $current_user->ID); $user_quiz_grade = 0; if (isset($user_lesson_status->comment_ID)) { $user_quiz_grade = get_comment_meta($user_lesson_status->comment_ID, 'grade', true); } if (!is_array($user_quizzes)) { $user_quizzes = array(); } // Check again that the lesson is complete $user_lesson_end = WooThemes_Sensei_Utils::user_completed_lesson($user_lesson_status); $user_lesson_complete = false; if ($user_lesson_end) { $user_lesson_complete = true; } // End If Statement $reset_allowed = get_post_meta($post->ID, '_enable_quiz_reset', true); // Build frontend data object $this->data->user_quizzes = $user_quizzes; $this->data->user_quiz_grade = $user_quiz_grade; $this->data->quiz_passmark = $quiz_passmark; $this->data->quiz_lesson = $quiz_lesson_id; $this->data->quiz_grade_type = $quiz_grade_type; $this->data->user_lesson_end = $user_lesson_end; $this->data->user_lesson_complete = $user_lesson_complete; $this->data->lesson_quiz_questions = $lesson_quiz_questions; $this->data->reset_quiz_allowed = $reset_allowed; }
/** * Marked lesson as started for user * @param integer $lesson_id ID of lesson * @return mixed boolean or comment_ID */ public static function sensei_start_lesson($lesson_id = 0, $user_id = 0, $complete = false) { global $woothemes_sensei; if (intval($user_id) == 0) { $user_id = get_current_user_id(); } $activity_logged = false; if (intval($lesson_id) > 0) { $course_id = get_post_meta($lesson_id, '_lesson_course', true); if ($course_id) { $is_user_taking_course = WooThemes_Sensei_Utils::user_started_course($course_id, $user_id); if (!$is_user_taking_course) { WooThemes_Sensei_Utils::user_start_course($user_id, $course_id); } } $metadata = array(); $status = 'in-progress'; // Note: When this action runs the lesson status may not yet exist do_action('sensei_user_lesson_start', $user_id, $lesson_id); if ($complete) { $has_questions = get_post_meta($lesson_id, '_quiz_has_questions', true); if ($has_questions) { $status = 'passed'; // Force a pass $metadata['grade'] = 100; } else { $status = 'complete'; } } // Check if user is already taking the lesson $activity_logged = WooThemes_Sensei_Utils::user_started_lesson($lesson_id, $user_id); if (!$activity_logged) { $metadata['start'] = current_time('mysql'); $activity_logged = WooThemes_Sensei_Utils::update_lesson_status($user_id, $lesson_id, $status, $metadata); } if ($complete) { // Run this *after* the lesson status has been created/updated do_action('sensei_user_lesson_end', $user_id, $lesson_id); } } return $activity_logged; }
/** * Submit the users quiz answers for grading * * This function accepts users answers and stores it but also initiates the grading * if a quiz can be graded automatically it will, if not the answers can be graded by the teacher. * * @since 1.7.4 * @access public * * @param array $quiz_answers * @param array $files from $_FILES * @param int $user_id * @param int $lesson_id * * @return bool $answers_submitted */ public static function submit_answers_for_grading($quiz_answers, $files = array(), $lesson_id, $user_id = 0) { $answers_submitted = false; // get the user_id if none was passed in use the current logged in user if (!intval($user_id) > 0) { $user_id = get_current_user_id(); } // make sure the parameters are valid before continuing if (empty($lesson_id) || empty($user_id) || 'lesson' != get_post_type($lesson_id) || !get_userdata($user_id) || !is_array($quiz_answers)) { return false; } global $post, $woothemes_sensei; // Default grade $grade = 0; // Get Quiz ID $quiz_id = $woothemes_sensei->lesson->lesson_quizzes($lesson_id); // Get quiz grade type $quiz_grade_type = get_post_meta($quiz_id, '_quiz_grade_type', true); // Get quiz pass setting $pass_required = get_post_meta($quiz_id, '_pass_required', true); // Get the minimum percentage need to pass this quiz $quiz_pass_percentage = abs(round(doubleval(get_post_meta($quiz_id, '_quiz_passmark', true)), 2)); // Handle Quiz Completion submit for grading if (isset($_POST['questions_asked']) && is_array($_POST['questions_asked'])) { $questions_asked = array_filter(array_map('absint', $_POST['questions_asked'])); } else { $questions_asked = array_keys($quiz_answers); } $questions_asked_string = implode(',', $questions_asked); // Save Quiz Answers for grading, the save function also calls the sensei_start_lesson self::save_user_answers($quiz_answers, $files, $lesson_id, $user_id); // Save questions that were asked in this quiz $user_lesson_status = WooThemes_Sensei_Utils::user_lesson_status($lesson_id, $user_id); if (isset($user_lesson_status->comment_ID)) { update_comment_meta($user_lesson_status->comment_ID, 'questions_asked', $questions_asked_string); } // Grade quiz $grade = WooThemes_Sensei_Utils::sensei_grade_quiz_auto($quiz_id, $quiz_answers, 0, $quiz_grade_type); // Get Lesson Grading Setting $lesson_metadata = array(); $lesson_status = 'ungraded'; // Default when completing a quiz // At this point the answers have been submitted $answers_submitted = true; // if this condition is false the quiz should manually be graded by admin if ('auto' == $quiz_grade_type && !is_wp_error($grade)) { // Quiz has been automatically Graded if ('on' == $pass_required) { // Student has reached the pass mark and lesson is complete if ($quiz_pass_percentage <= $grade) { $lesson_status = 'passed'; } else { $lesson_status = 'failed'; } // End If Statement } else { // Student only has to partake the quiz $lesson_status = 'graded'; } $lesson_metadata['grade'] = $grade; // Technically already set as part of "WooThemes_Sensei_Utils::sensei_grade_quiz_auto()" above } // end if ! is_wp_error( $grade ... WooThemes_Sensei_Utils::update_lesson_status($user_id, $lesson_id, $lesson_status, $lesson_metadata); if ('passed' == $lesson_status || 'graded' == $lesson_status) { /** * Lesson end action hook * * This hook is fired after a lesson quiz has been graded and the lesson status is 'passed' OR 'graded' * * @param int $user_id * @param int $lesson_id */ do_action('sensei_user_lesson_end', $user_id, $lesson_id); } /** * User quiz has been submitted * * Fires the end of the submit_answers_for_grading function. It will fire irrespective of the submission * results. * * @param int $user_id * @param int $quiz_id * @param string $grade * @param string $quiz_pass_percentage * @param string $quiz_grade_type */ do_action('sensei_user_quiz_submitted', $user_id, $quiz_id, $grade, $quiz_pass_percentage, $quiz_grade_type); return $answers_submitted; }