PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 All 140 releases
learnpress / inc / user-item / class-lp-user-item-quiz.php

class-lp-user-item-quiz.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.9, at inc/user-item/class-lp-user-item-quiz.php

931 lines 24.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class LP_User_Item_Quiz
5 */
6 class LP_User_Item_Quiz extends LP_User_Item {
7 public $_item_type = LP_QUIZ_CPT;
8 public $_ref_type = LP_COURSE_CPT;
9 /**
10 * @var array
11 */
12 protected $_answers = array();
13
14 /**
15 * LP_User_Item_Quiz constructor.
16 *
17 * @param array $data
18 */
19 public function __construct( $data ) {
20 $this->_curd = new LP_Quiz_CURD();
21
22 parent::__construct( $data );
23 $this->_parse_answers();
24 }
25
26 /**
27 *
28 */
29 protected function _parse_answers() {
30 foreach ( array( '_question_answers', 'question_answers' ) as $k ) {
31 $answers = learn_press_get_user_item_meta( $this->get_user_item_id(), $k, true );
32
33 if ( $answers ) {
34 $this->_answers = $answers;
35 break;
36 }
37 }
38 }
39
40 /**
41 * Add user answer to DB.
42 *
43 * @param int|array $id
44 * @param mixed $values
45 *
46 * @return array|bool|LP_Quiz_Results|mixed
47 * @throws Exception
48 * @editor tungnx
49 * @modify 4.1.4.1 - comment - not use
50 */
51 /*
52 public function add_question_answer( $id, $values = null ) {
53 $results = $this->get_results( '' );
54
55 if ( ! $results ) {
56 return false;
57 }
58
59 $questions = $results->get( 'questions', array() );
60
61 if ( is_numeric( $id ) ) {
62 $values = array( $id => $values );
63 } else {
64 $values = (array) $id;
65 }
66
67 foreach ( $values as $id => $answer ) {
68 if ( ! $this->has_checked_question( $id ) ) {
69 if ( ! empty( $questions[ $id ] ) ) {
70 $questions[ $id ]['answered'] = $answer;
71 } else {
72 $questions[ $id ] = array( 'answered' => $answer );
73 }
74 }
75 }
76
77 $results['questions'] = $questions;
78
79 //LP_User_Items_Result_DB::instance()->update( $this->get_user_item_id(), wp_json_encode( $results->get() ) );
80
81 $this->calculate_results();
82
83 $cache_key = sprintf( 'quiz-%d-%d-%d', $this->get_user_id(), $this->get_course_id(), $this->get_item_id() );
84
85 LP_Object_Cache::set( $cache_key, false, 'learn-press/quiz-result' );
86
87 return $this->get_results( '' );
88 }*/
89
90 public function get_question_answer( $id ) {
91 $results = $this->get_results( '' );
92
93 if ( ! $results ) {
94 return false;
95 }
96
97 $questions = $results->get( 'questions', false );
98
99 if ( $questions && is_array( $questions[ $id ] ) ) {
100 return $questions[ $id ]['answered'];
101 }
102
103 return false;
104 }
105
106 /**
107 * Update data to database
108 *
109 * @param bool $force
110 * @param bool $wp_error
111 *
112 * @return bool|mixed
113 */
114 /*
115 public function update( $force = false, $wp_error = false ) {
116 $return = parent::update( $force, $wp_error );
117 $this->calculate_results();
118
119 return $return;
120 }*/
121
122 /**
123 * Get list of data to update to database
124 *
125 * @since 3.1.0
126 *
127 * @return array
128 */
129 public function get_mysql_data() {
130 $columns = parent::get_mysql_data();
131
132 return apply_filters( 'learn-press/update-user-item-quiz-data', $columns, $this->get_item_id(), $this->get_course_id(), $this->get_user_id() );
133 }
134
135 /**
136 * @return bool|LP_Quiz
137 */
138 public function get_quiz() {
139 return learn_press_get_quiz( $this->get_item_id() );
140 }
141
142 public function get_status_label( $status = '' ) {
143 $statuses = array(
144 'started' => __( 'In Progress', 'learnpress' ),
145 'in-progress' => __( 'In Progress', 'learnpress' ),
146 'completed' => __( 'Completed', 'learnpress' ),
147 'passed' => __( 'Passed', 'learnpress' ),
148 'failed' => __( 'Failed', 'learnpress' ),
149 );
150
151 if ( ! $status ) {
152 $status = $this->get_status();
153 }
154
155 return ! empty( $statuses[ $status ] ) ? $statuses[ $status ] : __( 'Not Started', 'learnpress' );
156 }
157
158 /**
159 * Get ID of the course that this item assigned to.
160 *
161 * @return array|mixed
162 */
163 public function get_course_id() {
164 return $this->get_data( 'ref_id' );
165 }
166
167 /**
168 * Get result
169 *
170 * @param string $prop
171 *
172 * @return array|bool|mixed
173 */
174 public function get_result( $prop = '' ) {
175 $result = $this->calculate_quiz_result();
176
177 // Fix temporary for case call 'grade' - addons called
178 if ( 'grade' === $prop ) {
179 if ( $result['pass'] ) {
180 $result['grade'] = 'passed';
181 } else {
182 $result['grade'] = 'failed';
183 }
184 }
185
186 return $prop && $result && array_key_exists( $prop, $result ) ? $result[ $prop ] : $result;
187 }
188
189 /**
190 * Calculate result of quiz.
191 *
192 * @param string $prop
193 * @param bool $force - Optional. Force to refresh cache.
194 *
195 * Clear cache on
196 * @see LP_REST_Users_Controller::start_quiz() | retake quiz
197 *
198 * @return LP_Quiz_Results|bool|mixed
199 * @throws Exception
200 * @editor tungnx
201 * @modify 4.1.3
202 * @version 4.0.1
203 */
204 public function get_results( string $prop = 'result', bool $force = false ) {
205 if ( ! $this->get_status() ) {
206 return false;
207 }
208
209 $lp_quiz_cache = LP_Quiz_Cache::instance();
210
211 $key_cache = $this->get_results_cache_key();
212 $result = $lp_quiz_cache->get_cache( $key_cache );
213
214 if ( false === $result || $force ) {
215 // $result = $this->_get_results();
216
217 // if ( false === $result ) {
218 $result = $this->calculate_results();
219 // }
220
221 $lp_quiz_cache->set_cache( $key_cache, $result );
222 }
223
224 $result['user_item_id'] = $this->get_user_item_id();
225 $result['interval'] = array( $this->get_start_time(), $this->get_end_time() );
226 $result['graduation'] = $this->get_graduation();
227 $result['graduationText'] = $this->get_graduation_text();
228
229 return $prop ? $result[ $prop ] : new LP_Quiz_Results( $result );
230 }
231
232 /**
233 * Key of the cache filled by @see LP_User_Item_Quiz::get_results.
234 *
235 * @return string
236 */
237 public function get_results_cache_key(): string {
238 return sprintf( '%d/user/%d/course/%d', $this->get_item_id(), $this->get_user_id(), $this->get_course_id() );
239 }
240
241 /**
242 * Drop the cached result of this quiz for this user.
243 *
244 * get_results() caches what it calculates, and that cache outlives the request wherever a
245 * persistent object cache is installed. Anything that rewrites the stored result must clear
246 * it, or the next read serves the result from before the write - a quiz submitted after the
247 * in-progress result was cached comes back with every question still unanswered.
248 *
249 * @return void
250 */
251 public function clear_results_cache() {
252 LP_Quiz_Cache::instance()->clear( $this->get_results_cache_key() );
253 }
254
255 /**
256 * Get user quiz graduation text for displaying purpose. [Passed, Failed, null]
257 *
258 * @since 4.0.0
259 *
260 * @return mixed
261 */
262 public function get_graduation_text() {
263 $graduation = $this->get_graduation();
264
265 return apply_filters( 'learn-press/user-quiz-graduation-text', learn_press_get_graduation_text( $graduation ) );
266 }
267
268 /**
269 * Get Timestamp remaining when user doing quiz
270 *
271 * @author tungnx
272 * @version 1.0.1
273 * @sicne 4.1.4.1
274 * @return int
275 */
276 public function get_timestamp_remaining(): int {
277 $timestamp_remaining = - 1;
278 $quiz = false;
279 $user_quiz = false;
280
281 try {
282 $quiz = learn_press_get_quiz( $this->get_item_id() );
283 if ( ! $quiz || LP_ITEM_STARTED != $this->get_status() ) {
284 return $timestamp_remaining;
285 }
286
287 $parent_id = $this->get_parent_id();
288 $duration = $quiz->get_duration()->get() . ' second';
289
290 $filter = new LP_User_Items_Filter();
291 $filter->parent_id = $parent_id;
292 $filter->item_id = $this->get_item_id();
293 $filter->user_id = get_current_user_id();
294 $user_quiz = LP_User_Items_DB::getInstance()->get_user_course_item( $filter, true );
295 $course_start_time = $user_quiz->start_time;
296 $timestamp_expire = strtotime( $course_start_time . ' +' . $duration );
297 $timestamp_current = time();
298 $timestamp_remaining = $timestamp_expire - $timestamp_current;
299
300 if ( $timestamp_remaining < 0 ) {
301 $timestamp_remaining = 0;
302 }
303 } catch ( Throwable $e ) {
304
305 }
306
307 return apply_filters( 'learn-press/user-course-quiz/timestamp_remaining', $timestamp_remaining, $user_quiz, $quiz );
308 }
309
310 /**
311 * Get all attempts of a quiz.
312 *
313 * @param string $args
314 *
315 * @return array
316 */
317 public function get_attempts( $limit = 3 ) {
318 $limit = $limit ?? 3;
319
320 $limit = absint( apply_filters( 'lp/quiz/get-attempts/limit', $limit ) );
321
322 $results = LP_User_Items_Result_DB::instance()->get_results( $this->get_user_item_id(), $limit, true );
323 $output = array();
324
325 if ( ! empty( $results ) ) {
326 foreach ( $results as $result ) {
327 if ( $result && is_string( $result ) ) {
328 $result = json_decode( $result );
329
330 unset( $result->questions );
331
332 $output[] = $result;
333 }
334 }
335 }
336
337 return $output;
338 }
339
340 /**
341 * Get question ids user has started inside quiz.
342 *
343 * @since 4.0.0
344 *
345 * @return bool|array
346 */
347 public function get_questions() {
348 $quiz = learn_press_get_quiz( $this->get_item_id() );
349 $ids = $quiz->get_question_ids();
350
351 return apply_filters( 'learn-press/user-item-quiz-questions', $ids, $this->get_user_id(), $this );
352 }
353
354 /**
355 * Calculate results of quiz.
356 *
357 * @return array
358 * @throws Exception
359 * @version 4.0.0
360 */
361 public function calculate_results(): array {
362 $quiz = learn_press_get_quiz( $this->get_item_id() );
363 $last_results = LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() );
364
365 if ( ! $last_results ) {
366 $last_results = array();
367 }
368
369 $questions = $last_results['questions'] ?? array_fill_keys( $quiz->get_question_ids(), array() );
370
371 $result = array(
372 'questions' => array(),
373 'mark' => $quiz->get_mark(),
374 'user_mark' => 0,
375 'question_count' => 0,
376 'question_empty' => 0,
377 'question_answered' => 0,
378 'question_wrong' => 0,
379 'question_correct' => 0,
380 'status' => $this->get_status(),
381 'result' => 0,
382 'time_spend' => $this->get_time_interval( 'display' ),
383 'passing_grade' => $quiz->get_passing_grade(),
384 );
385
386 if ( $questions ) {
387 foreach ( $questions as $question_id => $last_checked ) {
388 $question = LP_Question::get_question( $question_id );
389 if ( ! $question ) {
390 continue;
391 }
392
393 $answered = array_key_exists( 'answered', $last_checked ) ? $last_checked['answered'] : '';
394 $check = apply_filters( 'learn-press/quiz/check-question-result', $question->check( $answered ), $question_id, $this );
395 $check['answered'] = $answered;
396
397 if ( $check['answered'] && $check['correct'] ) {
398 $result['question_correct'] ++;
399 $result['user_mark'] += array_key_exists( 'mark', $check ) ? floatval( $check['mark'] ) : $question->get_mark();
400 } else {
401 $negative_marking = apply_filters( 'learn-press/get-negative-marking-value', floatval( $question->get_mark() ), $question_id, $quiz->get_id() );
402
403 // If answered is empty consider user has skipped question
404 if ( ! $check['answered'] ) {
405 if ( $quiz->get_negative_marking() && $quiz->get_minus_skip_questions() ) {
406 $result['user_mark'] -= $negative_marking;
407 }
408 $result['question_empty'] ++;
409 } else {
410 if ( $quiz->get_negative_marking() ) {
411 $result['user_mark'] -= $negative_marking;
412 }
413 $result['question_wrong'] ++;
414 }
415 }
416
417 $result['questions'][ $question_id ] = apply_filters( 'learn-press/question-results-data', $last_checked ? array_merge( $last_checked, $check ) : $check, $question_id, $quiz->get_id() );
418
419 if ( $check['answered'] ) {
420 $result['question_answered'] ++;
421 }
422 }
423
424 $result['user_mark'] = ( $result['user_mark'] >= 0 ) ? $result['user_mark'] : 0;
425
426 $percent = $result['mark'] ? ( $result['user_mark'] / $result['mark'] ) * 100 : 0;
427 $result['result'] = $percent;
428 $grade = '';
429
430 if ( $this->get_status() === 'completed' ) {
431 $grade = $percent >= $this->get_quiz()->get_data( 'passing_grade' ) ? 'passed' : 'failed';
432 $this->_set_data( 'graduation', $grade );
433 }
434
435 $result['question_count'] = count( $questions );
436
437 if ( $grade ) {
438 learn_press_update_user_item_field(
439 array(
440 'graduation' => $grade,
441 ),
442 array(
443 'user_item_id' => $this->get_user_item_id(),
444 )
445 );
446 }
447 }
448
449 return $result;
450 }
451
452 /**
453 * Calculate result of quiz.
454 *
455 * @param array $answered [question_id => answered, 'instant_check' => 0]
456 *
457 * @return array
458 * @version 1.0.1
459 * @author tungnx
460 * @since 4.1.4.1
461 */
462 public function calculate_quiz_result( array $answered = array() ): array {
463 $result = array(
464 'questions' => array(),
465 'mark' => 0,
466 'user_mark' => 0,
467 'minus_point' => 0,
468 'question_count' => 0,
469 'question_empty' => 0,
470 'question_answered' => 0,
471 'question_wrong' => 0,
472 'question_correct' => 0,
473 'status' => '',
474 'result' => 0,
475 'time_spend' => '',
476 'passing_grade' => '',
477 'pass' => 0,
478 );
479
480 try {
481 if ( LP_ITEM_COMPLETED === $this->get_status() ) {
482 $result_tmp = LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() );
483
484 if ( $result_tmp ) {
485 if ( isset( $result_tmp['user_mark'] ) && $result_tmp['user_mark'] < 0 ) {
486 $result_tmp['user_mark'] = 0;
487 }
488
489 $result = $result_tmp;
490 }
491
492 return $result;
493 }
494
495 $quiz = learn_press_get_quiz( $this->get_item_id() );
496
497 if ( ! $quiz ) {
498 throw new Exception();
499 }
500
501 $question_ids = $quiz->get_question_ids();
502 $result['mark'] = $quiz->get_mark();
503 $result['question_count'] = $quiz->count_questions();
504 $result['time_spend'] = $this->get_time_interval( 'display' );
505 $result['passing_grade'] = $quiz->get_passing_grade();
506 $checked_questions = $this->get_checked_questions();
507
508 foreach ( $question_ids as $question_id ) {
509 $question = LP_Question::get_question( $question_id );
510 $point = floatval( $question->get_mark() );
511
512 $result['questions'][ $question_id ] = array();
513 $result['questions'][ $question_id ]['answered'] = $answered[ $question_id ] ?? '';
514
515 if ( isset( $answered[ $question_id ] ) ) { // User's answer
516 $result['question_answered']++;
517
518 $check = $question->check( $answered[ $question_id ] );
519 $point = apply_filters( 'learn-press/user/calculate-quiz-result/point', $point, $question, $check );
520 if ( $check['correct'] ) {
521 $result['question_correct']++;
522 $result['user_mark'] += $point;
523
524 $result['questions'][ $question_id ]['correct'] = true;
525 $result['questions'][ $question_id ]['mark'] = $point;
526 } else {
527 if ( $quiz->get_negative_marking() ) {
528 $result['user_mark'] -= $point;
529 $result['minus_point'] += $point;
530 }
531 $result['question_wrong']++;
532
533 $result['questions'][ $question_id ]['correct'] = false;
534 $result['questions'][ $question_id ]['mark'] = 0;
535 }
536 } elseif ( ! array_key_exists( 'instant_check', $answered ) ) { // User skip question
537 if ( $quiz->get_minus_skip_questions() ) {
538 $result['user_mark'] -= $point;
539 $result['minus_point'] += $point;
540 }
541 $result['question_empty']++;
542
543 $result['questions'][ $question_id ]['correct'] = false;
544 $result['questions'][ $question_id ]['mark'] = 0;
545 }
546
547 $can_review_quiz = get_post_meta( $quiz->get_id(), '_lp_review', true ) === 'yes';
548 if ( $can_review_quiz && ! array_key_exists( 'instant_check', $answered ) ) {
549
550 $result['questions'][ $question_id ]['explanation'] = $question->get_explanation();
551 $result['questions'][ $question_id ]['options'] = learn_press_get_question_options_for_js(
552 $question,
553 array(
554 'include_is_true' => in_array( $question_id, $checked_questions ) || get_post_meta( $quiz->get_id(), '_lp_show_correct_review', true ) === 'yes',
555 'answer' => $answered[ $question_id ] ?? '',
556 )
557 );
558 }
559 }
560
561 if ( $result['user_mark'] < 0 ) {
562 $result['user_mark'] = 0;
563 }
564
565 if ( $result['user_mark'] > 0 && $result['mark'] > 0 ) {
566 $result['result'] = round( $result['user_mark'] * 100 / $result['mark'], 2, PHP_ROUND_HALF_DOWN );
567 }
568
569 $passing_grade = $quiz->get_data( 'passing_grade', 0 );
570 if ( $result['result'] >= $passing_grade ) {
571 $result['pass'] = 1;
572 } else {
573 $result['pass'] = 0;
574 }
575 } catch ( Throwable $e ) {
576
577 }
578
579 return $result;
580 }
581
582 public function get_option_answer() {
583
584 }
585
586 protected function _get_results() {
587 return LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() );
588 }
589
590 public function get_percent_result( $decimal = 2 ) {
591 return apply_filters( 'learn-press/user/quiz-percent-result', sprintf( '%s%%', round( $this->get_result( 'result' ), $decimal ) ), $this->get_user_id(), $this->get_item_id() );
592 }
593
594 public function get_time_interval( $context = '' ) {
595 $interval = parent::get_time_interval();
596
597 if ( $context == 'display' ) {
598 $quiz = $this->get_quiz();
599
600 if ( $quiz && $interval && $quiz->get_duration() ) {
601 $interval = new LP_Duration( $interval );
602 $interval = $interval->to_timer();
603 } else {
604 $interval = '--:--';
605 }
606 }
607
608 return $interval;
609 }
610
611 /**
612 * Return TRUE if user has pressed SKIP on this question
613 *
614 * @param int $question_id
615 *
616 * @return bool
617 */
618 public function is_skipped( $question_id ) {
619 return $this->get_question_answer( $question_id ) === '__SKIPPED__';
620 }
621
622 public function is_answered( $question_id ) {
623 $result = $this->get_results();
624
625 if ( ! empty( $result['questions'][ $question_id ] ) ) {
626 return $result['questions'][ $question_id ]['answered'];
627 }
628
629 return false;
630 }
631
632 public function is_answered_true( $question_id ) {
633 $result = $this->get_results( false );
634
635 if ( ! empty( $result['questions'][ $question_id ] ) ) {
636 return $result['questions'][ $question_id ]['correct'];
637 }
638
639 return false;
640 }
641
642 /**
643 * Get questions user has answered.
644 *
645 * @param bool $percent - Optional. TRUE will return by percentage with total questions.
646 *
647 * @return float|int|mixed
648 */
649 public function get_questions_answered( $percent = false ) {
650 $result = $this->get_results( '' );
651
652 if ( $percent ) {
653 if ( $result['question_count'] ) {
654 $return = 0;
655 } else {
656 $return = $result['question_answered'] ? ( $result['question_answered'] / $result['question_count'] ) * 100 : 0;
657 }
658 } else {
659 $return = $result['question_answered'];
660 }
661
662 return $return;
663 }
664
665 /**
666 * Get total mark user achieved.
667 *
668 * @param bool $percent - Optional. TRUE will return by percentage with total mark.
669 *
670 * @return float|int|mixed
671 */
672 public function get_mark( $percent = false ) {
673 $result = $this->get_results();
674
675 if ( $percent ) {
676 $return = $result['mark'] ? ( $result['user_mark'] / $result['mark'] ) * 100 : 0;
677 } else {
678 $return = $result['user_mark'];
679 }
680
681 return $return;
682 }
683
684 /**
685 * @deprecated 4.2.0
686 */
687 public function get_total_questions() {
688 _deprecated_function( __METHOD__, '4.2.0' );
689 $quiz = learn_press_get_quiz( $this->get_item_id() );
690 $questions = $quiz->get_question_ids();
691
692 return sizeof( $questions );
693 }
694
695 public function get_quiz_mark() {
696 $result = $this->get_results();
697
698 return $result['mark'];
699 }
700
701 /**
702 * Return time remaining.
703 *
704 * @param string $return - Optional.
705 *
706 * @return LP_Duration
707 * @deprecated 4.1.7.3
708 */
709 public function get_time_remaining( $return = 'object' ) {
710 _deprecated_function( __METHOD__, '4.1.7.3' );
711 /*$time = parent::get_time_remaining( $return );
712
713 return apply_filters( 'learn-press/quiz/time-remaining', $time, $this->get_item_id(), $this->get_course_id(), $this->get_user_id() );*/
714 }
715
716 /**
717 * Get all questions user has already used "Check"
718 *
719 * @return array
720 */
721 public function get_checked_questions(): array {
722 $value = $this->get_meta( '_lp_question_checked', true );
723
724 if ( $value ) {
725 $value = (array) $value;
726 } else {
727 $value = array();
728 }
729
730 return $value;
731 }
732
733 public function add_checked_question( $id ) {
734 settype( $id, 'array' );
735 $checked = $this->get_checked_questions();
736 $checked = array_merge( $checked, $id );
737
738 $this->update_meta( '_lp_question_checked', $checked );
739
740 return $checked;
741 }
742
743 /**
744 * Return true if user has already checked a question.
745 *
746 * @param int $question_id
747 *
748 * @return bool
749 */
750 public function has_checked_question( $question_id ) {
751 return in_array( $question_id, $this->get_checked_questions() );
752 }
753
754 /**
755 * Instant check question
756 *
757 * @param int $question_id
758 * @param mixed $answered
759 *
760 * @return array
761 * @throws Exception
762 */
763 public function instant_check_question( int $question_id, $answered = null ): array {
764 $question = learn_press_get_question( $question_id );
765 if ( ! $question ) {
766 throw new Exception( __( 'The question is invalid!', 'learnpress' ) );
767 }
768
769 $can_check = $this->can_check_answer( $question_id );
770 if ( ! $can_check ) {
771 throw new Exception( __( 'Cannot check the answer to the question.', 'learnpress' ) );
772 }
773
774 $answered_check = array(
775 'instant_check' => 1,
776 $question_id => $answered,
777 );
778
779 // For case save result when check instant answer
780 $result_instant_check = LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() );
781 if ( $result_instant_check ) {
782 foreach ( $result_instant_check['questions'] as $question_answer_id => $question_answer ) {
783 if ( ! empty( $question_answer['answered'] ) ) {
784 $answered_check[ $question_answer_id ] = $question_answer['answered'];
785 }
786 }
787 }
788
789 $result_answer = $this->calculate_quiz_result( $answered_check );
790
791 LP_User_Items_Result_DB::instance()->update( $this->get_user_item_id(), json_encode( $result_answer ) );
792 $this->clear_results_cache();
793 $this->add_checked_question( $question_id );
794
795 $checked['answered'] = $answered;
796 $checked['mark'] = $result_answer['questions'][ $question_id ]['mark'];
797 $checked['correct'] = $result_answer['questions'][ $question_id ]['correct'];
798
799 return $checked;
800 }
801
802 /**
803 * @param int $question_id
804 *
805 * @return int
806 * @deprecated 4.2.5
807 */
808 public function hint( $question_id ) {
809 _deprecated_function( __METHOD__, '4.2.5' );
810 return false;
811 $remain = $this->can_hint_answer();
812
813 if ( $remain ) {
814 if ( ! $this->has_hinted_question( $question_id ) ) {
815 $checked = $this->get_hint_questions();
816 $checked[] = $question_id;
817 $this->set_meta( '_lp_question_hint', $checked );
818 }
819
820 $count = $this->get_count_hint();
821 $this->update_meta();
822 $remain --;
823 } else {
824 return false;
825 }
826
827 return $remain;
828 }
829
830 /**
831 * Return true if user has already checked a question.
832 *
833 * @param int $question_id
834 *
835 * @return bool
836 */
837 public function has_hinted_question( $question_id ) {
838 return in_array( $question_id, $this->get_hint_questions() );
839 }
840
841 public function get_check_answer_count() {
842 return count( $this->get_checked_questions() );
843 }
844
845 /**
846 * @param int $question_id - Added since 3.3.0
847 *
848 * @return bool
849 */
850 public function can_check_answer( int $question_id = 0 ) : bool {
851 $can = false;
852 $quiz = learn_press_get_quiz( $this->get_item_id() );
853
854 if ( ! $quiz ) {
855 return $can;
856 }
857
858 if ( $quiz->get_instant_check() && $this->get_status() === 'started' ) {
859 $can = ! $this->has_checked_question( $question_id );
860 }
861
862 return apply_filters( 'learn-press/can-instant-check-question', $can, $question_id, $this->get_item_id(), $this->get_course_id() );
863 }
864
865 /**
866 * Get number retaken count.
867 *
868 * @return integer
869 */
870 public function get_retaken_count(): int {
871 return absint( learn_press_get_user_item_meta( $this->get_user_item_id(), '_lp_retaken_count' ) );
872 }
873
874 /**
875 * Update learnpress_user_itemmeta retaken
876 *
877 * @return void
878 */
879 public function update_retake_count() {
880 $count = $this->get_retaken_count();
881 $count ++;
882
883 return $this->update_meta( '_lp_retaken_count', $count );
884 }
885
886 /**
887 * Get all questions user has already used "Check"
888 *
889 * @return array
890 * @deprecated 4.2.5
891 */
892 public function get_hint_questions() {
893 return [];
894 _deprecated_function( __METHOD__, '4.2.5' );
895 $value = $this->get_meta( '_lp_question_hint', true );
896
897 if ( $value ) {
898 $value = (array) $value;
899 } else {
900 $value = array();
901 }
902
903 return $value;
904 }
905
906 /**
907 * @deprecated 4.2.5
908 */
909 public function get_count_hint() {
910 _deprecated_function( __METHOD__, '4.2.5' );
911 return 0;
912 return count( $this->get_hint_questions() );
913 }
914
915 /**
916 * Return true if check answer is enabled.
917 *
918 * @return bool
919 */
920 public function can_hint_answer() {
921 return apply_filters( 'learn-press/user-quiz/can-hint-answer', true, $this->get_id(), $this->get_course_id() );
922 }
923
924 /**
925 * @return bool
926 */
927 public function is_review_questions(): bool {
928 return LP_Global::quiz_question() && ( $this->get_status() === 'completed' );
929 }
930 }
931