| @@ -155,21 +155,8 @@ | ||
| 155 | 155 | return ! empty( $statuses[ $status ] ) ? $statuses[ $status ] : __( 'Not Started', 'learnpress' ); |
| 156 | 156 | } |
| 157 | 157 | |
| 158 | 158 | /** |
| 159 | - * Get current question ID (quiz). | |
| 160 | - * | |
| 161 | - * @param string $return - Optional. | |
| 162 | - * | |
| 163 | - * @return int|LP_Question | |
| 164 | - */ | |
| 165 | - public function get_current_question( $return = '' ) { | |
| 166 | - _deprecated_function( sprintf( '%s::%s', __CLASS__, __FUNCTION__ ), '4.0.0' ); | |
| 167 | - | |
| 168 | - learn_press_error_log( sprintf( 'Deprecated %s::%s', __CLASS__, __FUNCTION__ ) ); | |
| 169 | - } | |
| 170 | - | |
| 171 | - /** | |
| 172 | 159 | * Get ID of the course that this item assigned to. |
| 173 | 160 | * |
| 174 | 161 | * @return array|mixed |
| 175 | 162 | */ |
| @@ -204,9 +191,9 @@ | ||
| 204 | 191 | * |
| 205 | 192 | * @param string $prop |
| 206 | 193 | * @param bool $force - Optional. Force to refresh cache. |
| 207 | 194 | * |
| 208 | - * Clear cache on | |
| 195 | + * Clear cache on | |
| 209 | 196 | * @see LP_REST_Users_Controller::start_quiz() | retake quiz |
| 210 | 197 | * |
| 211 | 198 | * @return LP_Quiz_Results|bool|mixed |
| 212 | 199 | * @throws Exception |
| @@ -220,9 +207,9 @@ | ||
| 220 | 207 | } |
| 221 | 208 | |
| 222 | 209 | $lp_quiz_cache = LP_Quiz_Cache::instance(); |
| 223 | 210 | |
| 224 | - $key_cache = sprintf( '%d/user/%d/course/%d', $this->get_item_id(), $this->get_user_id(), $this->get_course_id() ); | |
| 211 | + $key_cache = $this->get_results_cache_key(); | |
| 225 | 212 | $result = $lp_quiz_cache->get_cache( $key_cache ); |
| 226 | 213 | |
| 227 | 214 | if ( false === $result || $force ) { |
| 228 | 215 | // $result = $this->_get_results(); |
| @@ -242,8 +229,31 @@ | ||
| 242 | 229 | return $prop ? $result[ $prop ] : new LP_Quiz_Results( $result ); |
| 243 | 230 | } |
| 244 | 231 | |
| 245 | 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 | + /** | |
| 246 | 256 | * Get user quiz graduation text for displaying purpose. [Passed, Failed, null] |
| 247 | 257 | * |
| 248 | 258 | * @since 4.0.0 |
| 249 | 259 | * |
| @@ -258,26 +268,26 @@ | ||
| 258 | 268 | /** |
| 259 | 269 | * Get Timestamp remaining when user doing quiz |
| 260 | 270 | * |
| 261 | 271 | * @author tungnx |
| 262 | - * @version 1.0.0 | |
| 272 | + * @version 1.0.1 | |
| 263 | 273 | * @sicne 4.1.4.1 |
| 264 | 274 | * @return int |
| 265 | 275 | */ |
| 266 | 276 | public function get_timestamp_remaining(): int { |
| 267 | 277 | $timestamp_remaining = - 1; |
| 278 | + $quiz = false; | |
| 279 | + $user_quiz = false; | |
| 268 | 280 | |
| 269 | 281 | try { |
| 270 | 282 | $quiz = learn_press_get_quiz( $this->get_item_id() ); |
| 271 | - | |
| 272 | 283 | if ( ! $quiz || LP_ITEM_STARTED != $this->get_status() ) { |
| 273 | 284 | return $timestamp_remaining; |
| 274 | 285 | } |
| 275 | 286 | |
| 276 | 287 | $parent_id = $this->get_parent_id(); |
| 288 | + $duration = $quiz->get_duration()->get() . ' second'; | |
| 277 | 289 | |
| 278 | - $duration = $quiz->get_duration()->get() . ' second'; | |
| 279 | - | |
| 280 | 290 | $filter = new LP_User_Items_Filter(); |
| 281 | 291 | $filter->parent_id = $parent_id; |
| 282 | 292 | $filter->item_id = $this->get_item_id(); |
| 283 | 293 | $filter->user_id = get_current_user_id(); |
| @@ -283,9 +293,9 @@ | ||
| 283 | 293 | $filter->user_id = get_current_user_id(); |
| 284 | 294 | $user_quiz = LP_User_Items_DB::getInstance()->get_user_course_item( $filter, true ); |
| 285 | 295 | $course_start_time = $user_quiz->start_time; |
| 286 | 296 | $timestamp_expire = strtotime( $course_start_time . ' +' . $duration ); |
| 287 | - $timestamp_current = strtotime( current_time( 'mysql', 1 ) ); | |
| 297 | + $timestamp_current = time(); | |
| 288 | 298 | $timestamp_remaining = $timestamp_expire - $timestamp_current; |
| 289 | 299 | |
| 290 | 300 | if ( $timestamp_remaining < 0 ) { |
| 291 | 301 | $timestamp_remaining = 0; |
| @@ -293,9 +303,9 @@ | ||
| 293 | 303 | } catch ( Throwable $e ) { |
| 294 | 304 | |
| 295 | 305 | } |
| 296 | 306 | |
| 297 | - return apply_filters( 'learnpress/course/block_duration_expire/timestamp_remaining', $timestamp_remaining ); | |
| 307 | + return apply_filters( 'learn-press/user-course-quiz/timestamp_remaining', $timestamp_remaining, $user_quiz, $quiz ); | |
| 298 | 308 | } |
| 299 | 309 | |
| 300 | 310 | /** |
| 301 | 311 | * Get all attempts of a quiz. |
| @@ -348,11 +358,10 @@ | ||
| 348 | 358 | * @throws Exception |
| 349 | 359 | * @version 4.0.0 |
| 350 | 360 | */ |
| 351 | 361 | public function calculate_results(): array { |
| 352 | - $quiz = learn_press_get_quiz( $this->get_item_id() ); | |
| 353 | - $last_results = LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() ); | |
| 354 | - $is_has_change = 0; | |
| 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() ); | |
| 355 | 364 | |
| 356 | 365 | if ( ! $last_results ) { |
| 357 | 366 | $last_results = array(); |
| 358 | 367 | } |
| @@ -425,9 +434,8 @@ | ||
| 425 | 434 | |
| 426 | 435 | $result['question_count'] = count( $questions ); |
| 427 | 436 | |
| 428 | 437 | if ( $grade ) { |
| 429 | - $is_has_change = 1; | |
| 430 | 438 | learn_press_update_user_item_field( |
| 431 | 439 | array( |
| 432 | 440 | 'graduation' => $grade, |
| 433 | 441 | ), |
| @@ -437,12 +445,8 @@ | ||
| 437 | 445 | ); |
| 438 | 446 | } |
| 439 | 447 | } |
| 440 | 448 | |
| 441 | - if ( $is_has_change ) { | |
| 442 | - // LP_User_Items_Result_DB::instance()->update( $this->get_user_item_id(), wp_json_encode( $result ) ); | |
| 443 | - } | |
| 444 | - | |
| 445 | 449 | return $result; |
| 446 | 450 | } |
| 447 | 451 | |
| 448 | 452 | /** |
| @@ -450,9 +454,9 @@ | ||
| 450 | 454 | * |
| 451 | 455 | * @param array $answered [question_id => answered, 'instant_check' => 0] |
| 452 | 456 | * |
| 453 | 457 | * @return array |
| 454 | - * @version 1.0.0 | |
| 458 | + * @version 1.0.1 | |
| 455 | 459 | * @author tungnx |
| 456 | 460 | * @since 4.1.4.1 |
| 457 | 461 | */ |
| 458 | 462 | public function calculate_quiz_result( array $answered = array() ): array { |
| @@ -459,8 +463,9 @@ | ||
| 459 | 463 | $result = array( |
| 460 | 464 | 'questions' => array(), |
| 461 | 465 | 'mark' => 0, |
| 462 | 466 | 'user_mark' => 0, |
| 467 | + 'minus_point' => 0, | |
| 463 | 468 | 'question_count' => 0, |
| 464 | 469 | 'question_empty' => 0, |
| 465 | 470 | 'question_answered' => 0, |
| 466 | 471 | 'question_wrong' => 0, |
| @@ -492,11 +497,11 @@ | ||
| 492 | 497 | if ( ! $quiz ) { |
| 493 | 498 | throw new Exception(); |
| 494 | 499 | } |
| 495 | 500 | |
| 496 | - $question_ids = $quiz->get_questions(); | |
| 501 | + $question_ids = $quiz->get_question_ids(); | |
| 497 | 502 | $result['mark'] = $quiz->get_mark(); |
| 498 | - $result['question_count'] = count( $question_ids ); | |
| 503 | + $result['question_count'] = $quiz->count_questions(); | |
| 499 | 504 | $result['time_spend'] = $this->get_time_interval( 'display' ); |
| 500 | 505 | $result['passing_grade'] = $quiz->get_passing_grade(); |
| 501 | 506 | $checked_questions = $this->get_checked_questions(); |
| 502 | 507 | |
| @@ -503,18 +508,16 @@ | ||
| 503 | 508 | foreach ( $question_ids as $question_id ) { |
| 504 | 509 | $question = LP_Question::get_question( $question_id ); |
| 505 | 510 | $point = floatval( $question->get_mark() ); |
| 506 | 511 | |
| 507 | - // if ( ! array_key_exists( 'instant_check', $answered ) || array_key_exists( $question_id, $answered ) ) { | |
| 508 | 512 | $result['questions'][ $question_id ] = array(); |
| 509 | 513 | $result['questions'][ $question_id ]['answered'] = $answered[ $question_id ] ?? ''; |
| 510 | 514 | |
| 511 | - // } | |
| 512 | - | |
| 513 | 515 | if ( isset( $answered[ $question_id ] ) ) { // User's answer |
| 514 | 516 | $result['question_answered']++; |
| 515 | 517 | |
| 516 | 518 | $check = $question->check( $answered[ $question_id ] ); |
| 519 | + $point = apply_filters( 'learn-press/user/calculate-quiz-result/point', $point, $question, $check ); | |
| 517 | 520 | if ( $check['correct'] ) { |
| 518 | 521 | $result['question_correct']++; |
| 519 | 522 | $result['user_mark'] += $point; |
| 520 | 523 | |
| @@ -521,9 +524,10 @@ | ||
| 521 | 524 | $result['questions'][ $question_id ]['correct'] = true; |
| 522 | 525 | $result['questions'][ $question_id ]['mark'] = $point; |
| 523 | 526 | } else { |
| 524 | 527 | if ( $quiz->get_negative_marking() ) { |
| 525 | - $result['user_mark'] -= $point; | |
| 528 | + $result['user_mark'] -= $point; | |
| 529 | + $result['minus_point'] += $point; | |
| 526 | 530 | } |
| 527 | 531 | $result['question_wrong']++; |
| 528 | 532 | |
| 529 | 533 | $result['questions'][ $question_id ]['correct'] = false; |
| @@ -529,10 +533,11 @@ | ||
| 529 | 533 | $result['questions'][ $question_id ]['correct'] = false; |
| 530 | 534 | $result['questions'][ $question_id ]['mark'] = 0; |
| 531 | 535 | } |
| 532 | 536 | } elseif ( ! array_key_exists( 'instant_check', $answered ) ) { // User skip question |
| 533 | - if ( $quiz->get_negative_marking() && $quiz->get_minus_skip_questions() ) { | |
| 534 | - $result['user_mark'] -= $point; | |
| 537 | + if ( $quiz->get_minus_skip_questions() ) { | |
| 538 | + $result['user_mark'] -= $point; | |
| 539 | + $result['minus_point'] += $point; | |
| 535 | 540 | } |
| 536 | 541 | $result['question_empty']++; |
| 537 | 542 | |
| 538 | 543 | $result['questions'][ $question_id ]['correct'] = false; |
| @@ -566,13 +571,8 @@ | ||
| 566 | 571 | $result['pass'] = 1; |
| 567 | 572 | } else { |
| 568 | 573 | $result['pass'] = 0; |
| 569 | 574 | } |
| 570 | - | |
| 571 | - // $result['answered'] = $answered; | |
| 572 | - // $results['status'] = $quiz->get_status(); | |
| 573 | - // $result['results'] = $result; | |
| 574 | - // $result['attempts'] = $this->get_attempts(); | |
| 575 | 575 | } catch ( Throwable $e ) { |
| 576 | 576 | |
| 577 | 577 | } |
| 578 | 578 | |
| @@ -587,9 +587,9 @@ | ||
| 587 | 587 | return LP_User_Items_Result_DB::instance()->get_result( $this->get_user_item_id() ); |
| 588 | 588 | } |
| 589 | 589 | |
| 590 | 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() ) ); | |
| 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 | 592 | } |
| 593 | 593 | |
| 594 | 594 | public function get_time_interval( $context = '' ) { |
| 595 | 595 | $interval = parent::get_time_interval(); |
| @@ -680,11 +680,15 @@ | ||
| 680 | 680 | |
| 681 | 681 | return $return; |
| 682 | 682 | } |
| 683 | 683 | |
| 684 | + /** | |
| 685 | + * @deprecated 4.2.0 | |
| 686 | + */ | |
| 684 | 687 | public function get_total_questions() { |
| 688 | + _deprecated_function( __METHOD__, '4.2.0' ); | |
| 685 | 689 | $quiz = learn_press_get_quiz( $this->get_item_id() ); |
| 686 | - $questions = $quiz->get_questions(); | |
| 690 | + $questions = $quiz->get_question_ids(); | |
| 687 | 691 | |
| 688 | 692 | return sizeof( $questions ); |
| 689 | 693 | } |
| 690 | 694 | |
| @@ -699,13 +703,15 @@ | ||
| 699 | 703 | * |
| 700 | 704 | * @param string $return - Optional. |
| 701 | 705 | * |
| 702 | 706 | * @return LP_Duration |
| 707 | + * @deprecated 4.1.7.3 | |
| 703 | 708 | */ |
| 704 | 709 | public function get_time_remaining( $return = 'object' ) { |
| 705 | - $time = parent::get_time_remaining( $return ); | |
| 710 | + _deprecated_function( __METHOD__, '4.1.7.3' ); | |
| 711 | + /*$time = parent::get_time_remaining( $return ); | |
| 706 | 712 | |
| 707 | - return apply_filters( 'learn-press/quiz/time-remaining', $time, $this->get_item_id(), $this->get_course_id(), $this->get_user_id() ); | |
| 713 | + return apply_filters( 'learn-press/quiz/time-remaining', $time, $this->get_item_id(), $this->get_course_id(), $this->get_user_id() );*/ | |
| 708 | 714 | } |
| 709 | 715 | |
| 710 | 716 | /** |
| 711 | 717 | * Get all questions user has already used "Check" |
| @@ -756,14 +762,14 @@ | ||
| 756 | 762 | */ |
| 757 | 763 | public function instant_check_question( int $question_id, $answered = null ): array { |
| 758 | 764 | $question = learn_press_get_question( $question_id ); |
| 759 | 765 | if ( ! $question ) { |
| 760 | - throw new Exception( __( 'Question is invalid!', 'learnpress' ) ); | |
| 766 | + throw new Exception( __( 'The question is invalid!', 'learnpress' ) ); | |
| 761 | 767 | } |
| 762 | 768 | |
| 763 | 769 | $can_check = $this->can_check_answer( $question_id ); |
| 764 | 770 | if ( ! $can_check ) { |
| 765 | - throw new Exception( __( 'Cannot check answer the question.', 'learnpress' ) ); | |
| 771 | + throw new Exception( __( 'Cannot check the answer to the question.', 'learnpress' ) ); | |
| 766 | 772 | } |
| 767 | 773 | |
| 768 | 774 | $answered_check = array( |
| 769 | 775 | 'instant_check' => 1, |
| @@ -782,8 +788,9 @@ | ||
| 782 | 788 | |
| 783 | 789 | $result_answer = $this->calculate_quiz_result( $answered_check ); |
| 784 | 790 | |
| 785 | 791 | LP_User_Items_Result_DB::instance()->update( $this->get_user_item_id(), json_encode( $result_answer ) ); |
| 792 | + $this->clear_results_cache(); | |
| 786 | 793 | $this->add_checked_question( $question_id ); |
| 787 | 794 | |
| 788 | 795 | $checked['answered'] = $answered; |
| 789 | 796 | $checked['mark'] = $result_answer['questions'][ $question_id ]['mark']; |
| @@ -795,10 +802,13 @@ | ||
| 795 | 802 | /** |
| 796 | 803 | * @param int $question_id |
| 797 | 804 | * |
| 798 | 805 | * @return int |
| 806 | + * @deprecated 4.2.5 | |
| 799 | 807 | */ |
| 800 | 808 | public function hint( $question_id ) { |
| 809 | + _deprecated_function( __METHOD__, '4.2.5' ); | |
| 810 | + return false; | |
| 801 | 811 | $remain = $this->can_hint_answer(); |
| 802 | 812 | |
| 803 | 813 | if ( $remain ) { |
| 804 | 814 | if ( ! $this->has_hinted_question( $question_id ) ) { |
| @@ -876,10 +886,13 @@ | ||
| 876 | 886 | /** |
| 877 | 887 | * Get all questions user has already used "Check" |
| 878 | 888 | * |
| 879 | 889 | * @return array |
| 890 | + * @deprecated 4.2.5 | |
| 880 | 891 | */ |
| 881 | 892 | public function get_hint_questions() { |
| 893 | + return []; | |
| 894 | + _deprecated_function( __METHOD__, '4.2.5' ); | |
| 882 | 895 | $value = $this->get_meta( '_lp_question_hint', true ); |
| 883 | 896 | |
| 884 | 897 | if ( $value ) { |
| 885 | 898 | $value = (array) $value; |
| @@ -889,9 +902,14 @@ | ||
| 889 | 902 | |
| 890 | 903 | return $value; |
| 891 | 904 | } |
| 892 | 905 | |
| 906 | + /** | |
| 907 | + * @deprecated 4.2.5 | |
| 908 | + */ | |
| 893 | 909 | public function get_count_hint() { |
| 910 | + _deprecated_function( __METHOD__, '4.2.5' ); | |
| 911 | + return 0; | |
| 894 | 912 | return count( $this->get_hint_questions() ); |
| 895 | 913 | } |
| 896 | 914 | |
| 897 | 915 | /** |