model.llms.student.quizzes.php 6.91 KB
Newer Older
cyrille's avatar
cyrille committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
<?php
/**
 * LLMS_Student_Quizzes model class file
 *
 * @package LifterLMS/Models/Classes
 *
 * @since 3.9.0
 * @version 4.21.2
 */

defined( 'ABSPATH' ) || exit;

/**
 * Access student quiz attempt data
 *
 * @see LLMS_Student->quizzes()
 *
 * @since 3.9.0
 */
class LLMS_Student_Quizzes extends LLMS_Abstract_User_Data {

	/**
	 * Retrieve the number of quiz attempts for a quiz
	 *
	 * @since 3.16.0
	 *
	 * @param int $quiz_id WP Post ID of the quiz.
	 * @return int
	 */
	public function count_attempts_by_quiz( $quiz_id ) {

		$query = new LLMS_Query_Quiz_Attempt(
			array(
				'student_id' => $this->get_id(),
				'quiz_id'    => $quiz_id,
				'per_page'   => 1,
			)
		);

		return $query->found_results;

	}

	/**
	 * Remove Student Quiz attempt by ID
	 *
	 * @since 3.9.0
	 * @since 3.16.11 Unknown.
	 *
	 * @param int $attempt_id Attempt ID.
	 * @return boolean Returns `true` on success and `false` on error.
	 */
	public function delete_attempt( $attempt_id ) {

		$attempt = $this->get_attempt_by_id( $attempt_id );
		return $attempt ? $attempt->delete() : false;

	}

	/**
	 * Retrieve quiz data for a student and optionally filter by quiz_id(s)
	 *
	 * @since 3.9.0
	 * @since 3.16.11 Unknown.
	 * @since 4.21.2 Retrieve only attempts for the initialized student.
	 *
	 * @param int[]|Int $quiz Array or single WP_Post ID for quizzes to retrieve attempts for.
	 * @return LLMS_Quiz_Attempt[] Array of quiz attempts for the requested quiz or quizzes.
	 */
	public function get_all( $quiz = array() ) {

		$query = new LLMS_Query_Quiz_Attempt(
			array(
				'quiz_id'    => $quiz,
				'per_page'   => 5000,
				'student_id' => $this->get( 'id' ),
			)
		);

		/**
		 * Filters the list of quiz attempts for a student
		 *
		 * @since Unknown
		 *
		 * @param int[]|Int $quiz Array or single WP_Post ID for quizzes to retrieve attempts for.
		 */
		return apply_filters( 'llms_student_get_quiz_data', $query->get_attempts(), $quiz );

	}

	/**
	 * Retrieve quiz attempts
	 *
	 * @since    3.16.0
	 *
	 * @param int   $quiz_id WP Post ID of the quiz.
	 * @param array $args    Additional args to pass to LLMS_Query_Quiz_Attempt.
	 * @return LLMS_Quiz_Attempt[]
	 */
	public function get_attempts_by_quiz( $quiz_id, $args = array() ) {

		$args = wp_parse_args(
			array(
				'student_id' => $this->get_id(),
				'quiz_id'    => $quiz_id,
			),
			$args
		);

		$query = new LLMS_Query_Quiz_Attempt( $args );

		if ( $query->has_results() ) {
			return $query->get_attempts();
		}

		return array();

	}

	/**
	 * Retrieve an attempt by attempt id
	 *
	 * @since 3.16.0
	 * @since 4.21.2 Return `false` for invalid IDs & check permissions before returning the attempt.
	 *
	 * @param int $attempt_id Attempt ID.
	 * @return LLMS_Quiz_Attempt|boolean Returns the quiz attempt or `false` if the attempt doesn't exist or
	 *                                   doesn't belong to the initialized student.
	 */
	public function get_attempt_by_id( $attempt_id ) {

		$attempt = new LLMS_Quiz_Attempt( $attempt_id );

		// Invalid ID.
		if ( ! $attempt->exists() || ! current_user_can( 'view_grades', absint( $attempt->get( 'student_id' ) ), absint( $attempt->get( 'quiz_id' ) ) ) ) {
			return false;
		}

		return $attempt;

	}

	/**
	 * Decodes an attempt string and returns the associated attempt
	 *
	 * @since 3.9.0
	 * @since 3.16.0 Unknown.
	 *
	 * @param string $attempt_key Encoded attempt key.
	 * @return LLMS_Quiz_Attempt|false
	 */
	public function get_attempt_by_key( $attempt_key ) {

		$id = $this->parse_attempt_key( $attempt_key );
		if ( ! $id ) {
			return false;
		}
		return $this->get_attempt_by_id( $id );

	}

	/**
	 * Get the # of attempts remaining by a student for a given quiz
	 *
	 * @since 3.16.0
	 *
	 * @param int $quiz_id WP Post ID of the Quiz.
	 * @return mixed
	 */
	public function get_attempts_remaining_for_quiz( $quiz_id ) {

		$quiz = llms_get_post( $quiz_id );

		$ret = _x( 'Unlimited', 'quiz attempts remaining', 'lifterlms' );

		if ( $quiz->has_attempt_limit() ) {

			$allowed = $quiz->get( 'allowed_attempts' );
			$used    = $this->count_attempts_by_quiz( $quiz->get( 'id' ) );

			// Ensure undefined, null, '', etc. show as an int.
			if ( ! $allowed ) {
				$allowed = 0;
			}

			$remaining = ( $allowed - $used );

			// Don't show negative attempts.
			$ret = max( 0, $remaining );

		}

		return apply_filters( 'llms_student_quiz_attempts_remaining_for_quiz', $ret, $quiz, $this );

	}

	/**
	 * Get all the attempts for a given quiz/lesson from an attempt key
	 *
	 * @since 3.9.0
	 *
	 * @param string $attempt_key An encoded attempt key.
	 * @return false|array
	 */
	public function get_sibling_attempts_by_key( $attempt_key ) {

		$id = $this->parse_attempt_key( $attempt_key );
		if ( ! $id ) {
			return false;
		}

	}

	/**
	 * Get the quiz attempt with the highest grade for a given quiz and lesson combination
	 *
	 * @since 3.9.0
	 * @since 3.16.0 Unknown.
	 *
	 * @param int  $quiz_id    WP Post ID of a Quiz.
	 * @param null $deprecated Deprecated.
	 * @return false|LLMS_Quiz_Attempt
	 */
	public function get_best_attempt( $quiz_id = null, $deprecated = null ) {

		$attempts = $this->get_attempts_by_quiz(
			$quiz_id,
			array(
				'per_page' => 1,
				'sort'     => array(
					'grade'       => 'DESC',
					'update_date' => 'DESC',
					'id'          => 'DESC',
				),
				'status'   => array( 'pass', 'fail' ),
			)
		);

		if ( $attempts ) {
			return $attempts[0];
		}

		return false;

	}

	/**
	 * Retrieve the last recorded attempt for a student for a given quiz/lesson
	 *
	 * "Last" is defined as the attempt with the highest attempt number
	 *
	 * @since 3.9.0
	 * @since 3.16.0 Unknown.
	 *
	 * @param int $quiz_id WP Post ID of the quiz.
	 * @return LLMS_Quiz_Attempt|false
	 */
	public function get_last_attempt( $quiz_id ) {

		$attempts = $this->get_attempts_by_quiz(
			$quiz_id,
			array(
				'per_page' => 1,
				'sort'     => array(
					'attempt' => 'DESC',
				),
			)
		);

		if ( $attempts ) {
			return $attempts[0];
		}

		return false;

	}

	/**
	 * Get the last completed attempt for a given quiz or quiz/lesson combination
	 *
	 * @since 3.9.0
	 * @since 3.16.0 Unknown.
	 *
	 * @param int $quiz_id    WP Post ID of a Quiz.
	 * @param int $deprecated Deprecated.
	 * @return false|LLMS_Quiz_Attempt
	 */
	public function get_last_completed_attempt( $quiz_id = null, $deprecated = null ) {

		$query = new LLMS_Query_Quiz_Attempt(
			array(
				'student_id'     => $this->get_id(),
				'quiz_id'        => $quiz_id,
				'per_page'       => 1,
				'status_exclude' => array( 'incomplete' ),
				'sort'           => array(
					'end_date' => 'DESC',
					'id'       => 'DESC',
				),
			)
		);

		if ( $query->has_results() ) {
			return $query->get_attempts()[0];
		}

		return false;
	}

	/**
	 * Parse an attempt key into it's parts
	 *
	 * @since 3.9.0
	 * @since 3.16.7 Unknown.
	 *
	 * @param string $attempt_key An encoded attempt key.
	 * @return int
	 */
	private function parse_attempt_key( $attempt_key ) {
		return LLMS_Hasher::unhash( $attempt_key );
	}

}