Skip to content

Commit

Permalink
Merge pull request #7185 from Automattic/update/use-student-progress-…
Browse files Browse the repository at this point in the history
…models

Use student progress models on the frontend
  • Loading branch information
merkushin authored Oct 10, 2023
2 parents a560b3e + ac5e9a4 commit 7a5caf4
Show file tree
Hide file tree
Showing 56 changed files with 1,883 additions and 568 deletions.
206 changes: 18 additions & 188 deletions config/psalm/psalm-baseline.xml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions includes/admin/tools/class-sensei-tool-enrolment-debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ private function get_last_progress_activity_date( $user_id, $course_id, $course_
$course_lessons = Sensei()->course->course_lessons( $course_id );

foreach ( $course_lessons as $lesson ) {
$lesson_status = Sensei_Utils::user_lesson_status( $lesson->ID, $user_id );
if ( $lesson_status ) {
$dates[] = strtotime( $lesson_status->comment_date_gmt );
$lesson_progress = Sensei()->lesson_progress_repository->get( $lesson->ID, $user_id );
if ( $lesson_progress ) {
$dates[] = $lesson_progress->get_updated_at()->getTimestamp();
}
}

Expand Down
12 changes: 9 additions & 3 deletions includes/blocks/course-theme/class-course-navigation.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,15 @@ private function get_user_lesson_status( $lesson, $locked_lesson ): string {
if ( $completed ) {
$status = 'completed';
} else {
$user_lesson_status = \Sensei_Utils::user_lesson_status( $lesson_id, $this->user_id );
if ( isset( $user_lesson_status->comment_approved ) ) {
$status = $user_lesson_status->comment_approved;
// If the lesson has a quiz, use the quiz progress.
$quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
if ( $quiz_id ) {
$user_progress = \Sensei()->quiz_progress_repository->get( $quiz_id, $this->user_id );
} else {
$user_progress = \Sensei()->lesson_progress_repository->get( $lesson_id, $this->user_id );
}
if ( $user_progress ) {
$status = $user_progress->get_status();

if ( in_array( $status, $in_progress_statuses, true ) ) {
$status = 'in-progress';
Expand Down
50 changes: 21 additions & 29 deletions includes/class-sensei-course.php
Original file line number Diff line number Diff line change
Expand Up @@ -1551,11 +1551,11 @@ public function course_count( $post_status = 'publish' ) {


/**
* course_lessons function.
* Get course lessons.
*
* @access public
*
* @param int $course_id (default: 0). The course id.
* @param int|WP_Post $course_id (default: 0). The course id.
* @param string|array $post_status (default: 'publish'). The post status.
* @param string $fields (default: 'all'). WP only allows 3 types, but we will limit it to only 'ids' or 'all'.
* @param array $query_args Base arguments for the WP query.
Expand Down Expand Up @@ -1598,14 +1598,27 @@ public function course_lessons( $course_id = 0, $post_status = 'publish', $field
// that have been added to the course.
if ( count( $lessons ) > 1 ) {

$lesson_order = array();
foreach ( $lessons as $lesson ) {

$order = intval( get_post_meta( $lesson->ID, '_order_' . $course_id, true ) );
// for lessons with no order set it to be 10000 so that it show up at the end
$lesson->course_order = $order ? $order : 100000;
$lesson_order[ $lesson->ID ] = $order ? $order : 100000;
}

uasort( $lessons, [ $this, '_short_course_lessons_callback' ] );
uasort(
$lessons,
function ( $lesson_1, $lesson_2 ) use ( $lesson_order ) {
$lesson_1_order = $lesson_order[ $lesson_1->ID ];
$lesson_2_order = $lesson_order[ $lesson_2->ID ];

if ( $lesson_1_order == $lesson_2_order ) {
return 0;
}

return ( $lesson_1_order < $lesson_2_order ) ? -1 : 1;
}
);
}

/**
Expand All @@ -1618,9 +1631,8 @@ public function course_lessons( $course_id = 0, $post_status = 'publish', $field
*/
$lessons = apply_filters( 'sensei_course_get_lessons', $lessons, $course_id );

// return the requested fields
// runs after the sensei_course_get_lessons filter so the filter always give an array of lesson
// objects
// Return the requested fields.
// Runs after the sensei_course_get_lessons filter so the filter always give an array of lesson objects.
if ( 'ids' === $fields ) {
$lesson_objects = $lessons;
$lessons = [];
Expand All @@ -1634,25 +1646,6 @@ public function course_lessons( $course_id = 0, $post_status = 'publish', $field

}

/**
* Used for the uasort in $this->course_lessons()
*
* @since 1.8.0
* @access protected
*
* @param array $lesson_1
* @param array $lesson_2
* @return int
*/
protected function _short_course_lessons_callback( $lesson_1, $lesson_2 ) {

if ( $lesson_1->course_order == $lesson_2->course_order ) {
return 0;
}

return ( $lesson_1->course_order < $lesson_2->course_order ) ? -1 : 1;
}

/**
* Fetch all quiz ids in a course
*
Expand Down Expand Up @@ -1941,7 +1934,7 @@ public function load_user_courses_content( $user = false ) {

$progress_percentage = Sensei_Utils::quotient_as_absolute_rounded_percentage( $lessons_completed, $lesson_count, 0 );

$active_html .= $this->get_progress_meter( $progress_percentage );
$active_html .= $this->get_progress_meter( (int) $progress_percentage );

$active_html .= '</section>';

Expand Down Expand Up @@ -3569,8 +3562,7 @@ public static function the_course_enrolment_actions() {
// Check if course is completed.
$completed_course = false;
if ( ! empty( $user_id ) ) {
$user_course_status = Sensei_Utils::user_course_status( $course_id, $user_id );
$completed_course = Sensei_Utils::user_completed_course( $user_course_status );
$completed_course = Sensei_Utils::user_completed_course( $course_id, $user_id );
}

/**
Expand Down
33 changes: 17 additions & 16 deletions includes/class-sensei-grading.php
Original file line number Diff line number Diff line change
Expand Up @@ -688,12 +688,16 @@ public function admin_process_grading_submission() {
// store the feedback from grading
Sensei()->quiz->save_user_answers_feedback( $all_answers_feedback, $quiz_lesson_id, $user_id );

$lesson_progress = Sensei()->lesson_progress_repository->get( $quiz_lesson_id, $user_id );
if ( ! $lesson_progress ) {
$lesson_progress = Sensei()->lesson_progress_repository->create( $quiz_lesson_id, $user_id );
}

$quiz_progress = Sensei()->quiz_progress_repository->get( $quiz_id, $user_id );
if ( ! $quiz_progress ) {
return false;
}

$lesson_status = 'ungraded';
$lesson_metadata = [];
$quiz_progress->ungrade();

Expand All @@ -712,44 +716,41 @@ public function admin_process_grading_submission() {
if ( $pass_required ) {
// Student has reached the pass mark and lesson is complete.
if ( $quiz_passmark <= $grade ) {
$lesson_status = 'passed';
// Due to our internal logic, we need to complete the lesson first.
// This is because in the comments-based version the lesson status is used for both the lesson and the quiz.
$lesson_progress->complete();
$quiz_progress->pass();
} else {
$lesson_status = 'failed';
$quiz_progress->fail();
}
}

// Student only has to partake the quiz.
else {
$lesson_status = 'graded';
$lesson_progress->complete();
$quiz_progress->grade();
}
}

// Due to our internal logic, we need to save the lesson progress first.
// This is because in the comments-based version the lesson status is used for both the lesson and the quiz.
// And in this context the quiz status should be preserved in the comments-based version.
// For the tables-based version the order does not matter.
Sensei()->lesson_progress_repository->save( $lesson_progress );
Sensei()->quiz_progress_repository->save( $quiz_progress );
if ( count( $lesson_metadata ) ) {
foreach ( $lesson_metadata as $key => $value ) {
update_comment_meta( $quiz_progress->get_id(), $key, $value );
}
}

if ( in_array( $lesson_status, [ 'passed', 'graded' ], true ) ) {
if ( $lesson_progress->is_complete() ) {

/**
* Fires when a user completes a lesson.
*
* This hook is fired when a user passes a quiz or their quiz submission was graded.
* Therefore the corresponding lesson is marked as complete.
*
* @since 1.7.0
*
* @param int $user_id
* @param int $quiz_lesson_id
*/
/* The action is documented in includes/class-sensei-utils.php */
do_action( 'sensei_user_lesson_end', $user_id, $quiz_lesson_id );

}

if ( isset( $_POST['sensei_grade_next_learner'] ) && strlen( $_POST['sensei_grade_next_learner'] ) > 0 ) {

$load_url = add_query_arg( array( 'message' => 'graded' ) );
Expand Down
32 changes: 23 additions & 9 deletions includes/class-sensei-lesson.php
Original file line number Diff line number Diff line change
Expand Up @@ -3639,9 +3639,14 @@ public function get_quiz_permalink( $lesson ) {
* @return bool Whether quiz is submitted.
*/
public function is_quiz_submitted( int $lesson_id, int $user_id ) : bool {
$user_lesson_status = \Sensei_Utils::user_lesson_status( $lesson_id, $user_id );
$quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
if ( ! $quiz_id ) {
return false;
}

return ! empty( $user_lesson_status ) && in_array( $user_lesson_status->comment_approved, [ 'ungraded', 'passed', 'failed', 'graded' ], true );
$quiz_progress = Sensei()->quiz_progress_repository->get( $quiz_id, $user_id );

return ! empty( $quiz_progress ) && in_array( $quiz_progress->get_status(), [ 'ungraded', 'passed', 'failed', 'graded' ], true );
}


Expand Down Expand Up @@ -4500,8 +4505,8 @@ public static function the_lesson_meta( $lesson_id ) {

<?php

$meta_html = '';
$user_lesson_status = Sensei_Utils::user_lesson_status( get_the_ID(), get_current_user_id() );
$meta_html = '';
$has_progress = Sensei()->lesson_progress_repository->has( $lesson_id, get_current_user_id() );

$lesson_length = get_post_meta( $lesson_id, '_lesson_length', true );
if ( '' != $lesson_length ) {
Expand All @@ -4525,7 +4530,7 @@ public static function the_lesson_meta( $lesson_id ) {

$meta_html .= '<span class="lesson-status complete">' . esc_html__( 'Complete', 'sensei-lms' ) . '</span>';

} elseif ( $user_lesson_status ) {
} elseif ( $has_progress ) {

$meta_html .= '<span class="lesson-status in-progress">' . esc_html__( 'In Progress', 'sensei-lms' ) . '</span>';

Expand Down Expand Up @@ -4742,9 +4747,14 @@ public static function user_not_taking_course_message() {
*/
public static function course_signup_link() {

$course_id = Sensei()->lesson->get_course_id( get_the_ID() );
$lesson_id = get_the_ID();
if ( ! $lesson_id ) {
return;
}

$course_id = Sensei()->lesson->get_course_id( $lesson_id );

if ( empty( $course_id ) || 'course' !== get_post_type( $course_id ) || sensei_all_access() || Sensei_Utils::is_preview_lesson( get_the_ID() ) ) {
if ( empty( $course_id ) || 'course' !== get_post_type( $course_id ) || sensei_all_access() || Sensei_Utils::is_preview_lesson( $lesson_id ) ) {
return;
}

Expand Down Expand Up @@ -4831,8 +4841,12 @@ public function get_take_course_url( $course_id ) {
* @since 1.9.0
*/
public static function prerequisite_complete_message() {
$lesson_id = get_the_ID();
if ( false === $lesson_id ) {
return;
}

$lesson_prerequisite = self::find_first_prerequisite_lesson( get_the_ID(), get_current_user_id() );
$lesson_prerequisite = self::find_first_prerequisite_lesson( $lesson_id, get_current_user_id() );

if ( $lesson_prerequisite > 0 ) {

Expand Down Expand Up @@ -5049,7 +5063,7 @@ public static function output_comments() {
*/
public static function user_lesson_quiz_status_message( $lesson_id = 0, $user_id = 0 ) {

$lesson_id = empty( $lesson_id ) ? get_the_ID() : $lesson_id;
$lesson_id = empty( $lesson_id ) ? (int) get_the_ID() : $lesson_id;
$user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
$lesson_course_id = (int) get_post_meta( $lesson_id, '_lesson_course', true );
$quiz_id = Sensei()->lesson->lesson_quizzes( $lesson_id );
Expand Down
9 changes: 2 additions & 7 deletions includes/class-sensei-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -743,13 +743,7 @@ public function sensei_course_preview_titles( $title, $lesson_id ) {
$title_text = '';

if ( method_exists( 'Sensei_Utils', 'is_preview_lesson' ) && Sensei_Utils::is_preview_lesson( $lesson_id ) ) {
$is_user_taking_course = Sensei_Utils::sensei_check_for_activity(
array(
'post_id' => $course_id,
'user_id' => $current_user->ID,
'type' => 'sensei_course_status',
)
);
$is_user_taking_course = Sensei()->course_progress_repository->has( $course_id, $current_user->ID );
if ( ! $is_user_taking_course ) {
if ( method_exists( 'Sensei_Frontend', 'sensei_lesson_preview_title_text' ) ) {
$title_text = Sensei()->frontend->sensei_lesson_preview_title_text( $course_id );
Expand Down Expand Up @@ -1194,6 +1188,7 @@ public function calculate_user_module_progress( $user_id = 0, $module_id = 0, $c
$completed = false;
$lesson_count = 0;
$completed_count = 0;
$lessons = array_map( 'intval', $lessons );
foreach ( $lessons as $lesson_id ) {
$completed = Sensei_Utils::user_completed_lesson( $lesson_id, $user_id );
++$lesson_count;
Expand Down
4 changes: 2 additions & 2 deletions includes/class-sensei-question.php
Original file line number Diff line number Diff line change
Expand Up @@ -907,10 +907,10 @@ public static function the_answer_feedback( $question_id ) {
return;
}

$user_lesson_status = Sensei_Utils::user_lesson_status( $lesson_id, get_current_user_id() );
$user_quiz_progress = Sensei()->quiz_progress_repository->get( $quiz_id, get_current_user_id() );
$user_quiz_grade = Sensei_Quiz::get_user_quiz_grade( $lesson_id, get_current_user_id() );
$reset_quiz_allowed = Sensei_Quiz::is_reset_allowed( $lesson_id );
$quiz_graded = isset( $user_lesson_status->comment_approved ) && ! in_array( $user_lesson_status->comment_approved, array( 'ungraded', 'in-progress' ) );
$quiz_graded = $user_quiz_progress && ! in_array( $user_quiz_progress->get_status(), array( 'ungraded', 'in-progress' ) );

$quiz_required_pass_grade = intval( get_post_meta( $quiz_id, '_quiz_passmark', true ) );
$succeeded = $user_quiz_grade >= $quiz_required_pass_grade;
Expand Down
Loading

0 comments on commit 7a5caf4

Please sign in to comment.