Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract get_courses with dependent methods from Sensei_Analysis_Overview_List_Table #4938

Merged
merged 32 commits into from Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f26013
Add a rough structure for Reports
merkushin Mar 22, 2022
a7b8cfc
Pass date filters with other arguments
merkushin Mar 23, 2022
863fc09
Rename get_date_filters to get_additional_filters
merkushin Mar 23, 2022
f114f53
Conform new interface of data provider
merkushin Mar 23, 2022
8018348
Fix classnames to correspond conventions
merkushin Mar 23, 2022
6a5e66d
Fix linter issues
merkushin Mar 23, 2022
8eeadfb
Fix classname and other linter issues
merkushin Mar 23, 2022
22ae2fd
Fix linter issues for list tables
merkushin Mar 23, 2022
c45eea2
Update filter names
merkushin Mar 24, 2022
71773bf
Add new classes to autoloader
merkushin Mar 24, 2022
c45f40e
Add first test for courses data provider
merkushin Mar 24, 2022
20f976b
Merge branch 'master' into update/reports-structure-proposal
merkushin Mar 24, 2022
8f09d4f
Extract the students overview report logic into multiple classes
m1r0 Mar 24, 2022
ec7aef3
Add tests for the `Sensei_Reports_Overview_Data_Provider_Students_Tes…
m1r0 Mar 25, 2022
f2e6df2
Add test with last activity
merkushin Mar 26, 2022
49b42c0
Merge branch 'trunk' into update/reports-structure-proposal
merkushin Mar 26, 2022
6a064b4
Add tests for list table
merkushin Mar 28, 2022
87429be
Add tests for overview list table factory
merkushin Mar 29, 2022
2140108
Update load_data_object to use overview list table factory
merkushin Mar 29, 2022
ba7b7b7
Add column for course list table
merkushin Mar 29, 2022
bfdd17f
Fix default order value
merkushin Mar 29, 2022
ce54d52
Add test for Sensei_Analysis::load_data_object
merkushin Mar 29, 2022
230dc23
Fix tests for displaying columns
merkushin Mar 29, 2022
eb696db
Fix linter issues
merkushin Mar 29, 2022
431d360
Update since tag from 4.2.1 to 4.3.0
merkushin Mar 29, 2022
3efcaa1
Merge branch 'update/reports-structure-proposal' into refactor/report…
m1r0 Mar 29, 2022
1e047e0
Fix linter
m1r0 Mar 29, 2022
1bf8587
Fix tests for multisite
m1r0 Mar 29, 2022
3f2845b
Add user date registered column to the students report
m1r0 Mar 29, 2022
5e23129
Add tests for the `Sensei_Reports_Overview_List_Table_Students` class
m1r0 Mar 29, 2022
a3ac9b0
Add comments and newlines for better readability
merkushin Mar 30, 2022
c582358
Merge pull request #4947 from Automattic/refactor/reports-get-learners
m1r0 Mar 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions includes/class-sensei-autoloader.php
Expand Up @@ -74,6 +74,8 @@ public function __construct() {
new Sensei_Autoloader_Bundle( 'course-theme' ),
new Sensei_Autoloader_Bundle( 'course-video' ),
new Sensei_Autoloader_Bundle( 'course-video/blocks' ),
new Sensei_Autoloader_Bundle( 'reports/overview/data-provider' ),
new Sensei_Autoloader_Bundle( 'reports/overview/list-table' ),
);

// Add Sensei custom auto loader.
Expand Down
@@ -0,0 +1,162 @@
<?php
/**
* File containing the Sensei_Reports_Overview_Data_Provider_Courses class.
*
* @package sensei
*/

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Class Sensei_Reports_Overview_Data_Provider_Courses
*
* @since 4.2.1
*/
class Sensei_Reports_Overview_Data_Provider_Courses implements Sensei_Reports_Overview_Data_Provider_Interface {

/**
* Total number of courses found with given criteria.
*
* @var int Total number of items
*/
private $last_total_items = 0;

/**
* Contains start date and time for filtering.
*
* @var string|null
*/
private $date_from;

/**
* Contains end date and time for filtering.
*
* @var string|null
*/
private $date_to;

/**
* Get the data for the overview report.
*
* @param array $filters Filters to apply to the data.
*
* @return array
*/
public function get_items( array $filters ): array {
$this->date_from = $filters['last_activity_date_from'] ?? null;
$this->date_to = $filters['last_activity_date_to'] ?? null;

$course_args = array(
'post_type' => 'course',
'post_status' => array( 'publish', 'private' ),
'posts_per_page' => $filters['number'],
'offset' => $filters['offset'],
'orderby' => $filters['orderby'],
'order' => $filters['order'],
'suppress_filters' => 0,
);

if ( isset( $filters['search'] ) ) {
$course_args['s'] = $filters['search'];
}

add_filter( 'posts_clauses', [ $this, 'filter_courses_by_last_activity' ] );
add_filter( 'posts_clauses', [ $this, 'add_days_to_completion_to_courses_queries' ] );
$courses_query = new WP_Query( apply_filters( 'sensei_analysis_overview_filter_courses', $course_args ) );
remove_filter( 'posts_clauses', [ $this, 'filter_courses_by_last_activity' ] );
remove_filter( 'posts_clauses', [ $this, 'add_days_to_completion_to_courses_queries' ] );

$this->last_total_items = $courses_query->found_posts;

return $courses_query->posts;
}

/**
* Filter the courses by last activity start/end date.
*
* @access private
*
* @param array $clauses Associative array of the clauses for the query.
*
* @return array Modified associative array of the clauses for the query.
*/
public function filter_courses_by_last_activity( array $clauses ): array {
global $wpdb;

if ( ! $this->date_from && ! $this->date_to ) {
return $clauses;
}
// Fetch the lessons within the expected last activity range.
$lessons_query = "SELECT cm.comment_post_id lesson_id, MAX(cm.comment_date_gmt) as comment_date_gmt
FROM {$wpdb->comments} cm
WHERE cm.comment_approved IN ('complete', 'passed', 'graded')
AND cm.comment_type = 'sensei_lesson_status'";

// Filter by start date.
if ( $this->date_from ) {
$lessons_query .= $wpdb->prepare(
' AND cm.comment_date_gmt >= %s',
$this->date_from
);
}
$lessons_query .= ' GROUP BY cm.comment_post_id';

// Fetch the course IDs associated with those lessons.
$course_query = "SELECT DISTINCT(pm.meta_value) course_id
FROM {$wpdb->postmeta} pm JOIN ({$lessons_query}) cm
ON cm.lesson_id = pm.post_id
AND pm.meta_key = '_lesson_course'
GROUP BY pm.meta_value
";

// Filter by end date.
if ( $this->date_to ) {
$course_query .= $wpdb->prepare(
' HAVING MAX(cm.comment_date_gmt) <= %s',
$this->date_to
);
}

$clauses['where'] .= " AND {$wpdb->posts}.ID IN ({$course_query})";

return $clauses;
}


/**
* Add the sum of days taken by each student to complete a course and the number of completions for each course.
*
* @access private
*
* @param array $clauses Associative array of the clauses for the query.
*
* @return array Modified associative array of the clauses for the query.
*/
public function add_days_to_completion_to_courses_queries( $clauses ) {
global $wpdb;

// Get the number of days to complete a course: `days to complete = complete date - start date + 1`.
$clauses['fields'] .= ", SUM( ABS( DATEDIFF( {$wpdb->comments}.comment_date, STR_TO_DATE( {$wpdb->commentmeta}.meta_value, '%Y-%m-%d %H:%i:%s' ) ) ) + 1 ) AS days_to_completion";
// We consider the course as completed if there is a comment and corresponding meta for it.
$clauses['fields'] .= ", COUNT({$wpdb->commentmeta}.comment_id) AS count_of_completions";
$clauses['join'] .= " LEFT JOIN {$wpdb->comments} ON {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID";
$clauses['join'] .= " AND {$wpdb->comments}.comment_type IN ('sensei_course_status')";
$clauses['join'] .= " AND {$wpdb->comments}.comment_approved IN ( 'complete' )";
$clauses['join'] .= " AND {$wpdb->comments}.comment_post_ID = {$wpdb->posts}.ID";
$clauses['join'] .= " LEFT JOIN {$wpdb->commentmeta} ON {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id";
$clauses['join'] .= " AND {$wpdb->commentmeta}.meta_key = 'start'";
$clauses['groupby'] .= " {$wpdb->posts}.ID";

return $clauses;
}

/**
* Get the total number of items found for the last query.
*
* @return int
*/
public function get_last_total_items(): int {
return $this->last_total_items;
}
}
@@ -0,0 +1,30 @@
<?php
/**
* File containing the interface Sensei_Reports_Overview_DataProvider_Interface.
*
* @package sensei
*/

/**
* Interface Sensei_Reports_Overview_Data_Provider_Interface.
*
* @since 4.2.1
*/
interface Sensei_Reports_Overview_Data_Provider_Interface {
/**
* Get the data for the overview report.
*
* @param array $filters Filters to apply to the data.
*
* @return array
*/
public function get_items( array $filters ): array;

/**
* Get the total number of items found for the last query.
*
* @return int
*/
public function get_last_total_items(): int;

}