PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.3
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.3
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 4.2.0 4.2.1 All 138 releases
learnpress / inc / Statistics / HealthCheckProvider.php

HealthCheckProvider.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.3, at inc/Statistics/HealthCheckProvider.php

193 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class HealthCheckProvider
4 *
5 * @package LearnPress/Classes/Statistics
6 * @since 4.4.2
7 */
8
9 namespace LearnPress\Statistics;
10
11 use LP_Database;
12
13 defined( 'ABSPATH' ) || exit();
14
15 /**
16 * Content health checks for the statistics dashboard.
17 *
18 * Deliberately NOT time-filtered — a course with zero enrollments is a
19 * problem regardless of the selected period.
20 *
21 * @since 4.4.2
22 */
23 class HealthCheckProvider extends LP_Database {
24 /**
25 * @var HealthCheckProvider
26 */
27 private static $_instance;
28
29 protected function __construct() {
30 parent::__construct();
31 }
32
33 /**
34 * @return HealthCheckProvider
35 */
36 public static function getInstance(): HealthCheckProvider {
37 if ( is_null( self::$_instance ) ) {
38 self::$_instance = new self();
39 }
40
41 return self::$_instance;
42 }
43
44 /**
45 * @param StatisticsScope|null $scope
46 * @return array [ 'no_enrollment' => int, 'no_content' => int, 'pending_review' => int, 'quiz_low_pass' => int ]
47 */
48 public function get_checks( ?StatisticsScope $scope = null ): array {
49 $sig = $scope ? $scope->signature() : array( 0, 0 );
50 // The low-pass quiz check depends on these filters, so vary the key on them —
51 // otherwise a threshold change stays masked until the TTL expires.
52 $quiz_config = array(
53 (float) apply_filters( 'learn-press/statistics/quiz-pass-alert', 50 ),
54 (int) apply_filters( 'learn-press/statistics/quiz-pass-alert-min-attempts', 5 ),
55 );
56
57 return StatisticsCache::remember(
58 'health_checks',
59 array( $sig, $quiz_config ),
60 function () use ( $scope ) {
61 return array(
62 'no_enrollment' => $this->count_courses_without_enrollment( $scope ),
63 'no_content' => $this->count_courses_without_content( $scope ),
64 'pending_review' => $this->count_pending_courses( $scope ),
65 'quiz_low_pass' => $this->count_low_pass_quizzes( $scope ),
66 );
67 }
68 );
69 }
70
71 /**
72 * @param StatisticsScope|null $scope
73 * @return string
74 */
75 private function scope_condition( ?StatisticsScope $scope, string $course_id_field ): string {
76 if ( ! $scope || $scope->is_empty() ) {
77 return '';
78 }
79
80 return $scope->sql_conditions( $course_id_field );
81 }
82
83 /**
84 * Published courses no user ever had a course row for.
85 *
86 * @param StatisticsScope|null $scope
87 * @return int
88 */
89 private function count_courses_without_enrollment( ?StatisticsScope $scope ): int {
90 $where = $this->scope_condition( $scope, 'p.ID' );
91
92 $sql = $this->wpdb->prepare(
93 "SELECT COUNT(*) FROM {$this->tb_posts} AS p
94 WHERE p.post_type = %s AND p.post_status = %s {$where}
95 AND NOT EXISTS (
96 SELECT 1 FROM {$this->tb_lp_user_items} AS ui
97 WHERE ui.item_id = p.ID AND ui.item_type = %s
98 )",
99 LP_COURSE_CPT,
100 'publish',
101 LP_COURSE_CPT
102 );
103
104 return (int) $this->wpdb->get_var( $sql );
105 }
106
107 /**
108 * Published courses with no section items (empty curriculum).
109 *
110 * @param StatisticsScope|null $scope
111 * @return int
112 */
113 private function count_courses_without_content( ?StatisticsScope $scope ): int {
114 $where = $this->scope_condition( $scope, 'p.ID' );
115
116 $sql = $this->wpdb->prepare(
117 "SELECT COUNT(*) FROM {$this->tb_posts} AS p
118 WHERE p.post_type = %s AND p.post_status = %s {$where}
119 AND NOT EXISTS (
120 SELECT 1 FROM {$this->tb_lp_sections} AS s
121 INNER JOIN {$this->tb_lp_section_items} AS si ON si.section_id = s.section_id
122 WHERE s.section_course_id = p.ID
123 )",
124 LP_COURSE_CPT,
125 'publish'
126 );
127
128 return (int) $this->wpdb->get_var( $sql );
129 }
130
131 /**
132 * Courses waiting for review.
133 *
134 * @param StatisticsScope|null $scope
135 * @return int
136 */
137 private function count_pending_courses( ?StatisticsScope $scope ): int {
138 $where = $this->scope_condition( $scope, 'p.ID' );
139
140 $sql = $this->wpdb->prepare(
141 "SELECT COUNT(*) FROM {$this->tb_posts} AS p
142 WHERE p.post_type = %s AND p.post_status = %s {$where}",
143 LP_COURSE_CPT,
144 'pending'
145 );
146
147 return (int) $this->wpdb->get_var( $sql );
148 }
149
150 /**
151 * Quizzes with a pass rate below the alert threshold, minimum attempts
152 * required so one failed try does not flag a quiz.
153 *
154 * Scope reaches the course through the section lineage
155 * ( quiz → section_items → sections → course ).
156 *
157 * @param StatisticsScope|null $scope
158 * @return int
159 */
160 private function count_low_pass_quizzes( ?StatisticsScope $scope ): int {
161 $threshold = (float) apply_filters( 'learn-press/statistics/quiz-pass-alert', 50 );
162 $min_attempts = (int) apply_filters( 'learn-press/statistics/quiz-pass-alert-min-attempts', 5 );
163
164 $where = '';
165 if ( $scope && ! $scope->is_empty() ) {
166 $course_conditions = $scope->sql_conditions( 's.section_course_id' );
167 $where = " AND EXISTS (
168 SELECT 1 FROM {$this->tb_lp_section_items} AS si
169 INNER JOIN {$this->tb_lp_sections} AS s ON s.section_id = si.section_id
170 WHERE si.item_id = ui.item_id {$course_conditions}
171 )";
172 }
173
174 $sql = $this->wpdb->prepare(
175 "SELECT COUNT(*) FROM (
176 SELECT ui.item_id
177 FROM {$this->tb_lp_user_items} AS ui
178 WHERE ui.item_type = %s AND ui.graduation IN ( %s, %s ) {$where}
179 GROUP BY ui.item_id
180 HAVING COUNT(*) >= %d AND ( SUM( ui.graduation = %s ) / COUNT(*) ) * 100 < %f
181 ) AS low_quizzes",
182 LP_QUIZ_CPT,
183 'passed',
184 'failed',
185 $min_attempts,
186 'passed',
187 $threshold
188 );
189
190 return (int) $this->wpdb->get_var( $sql );
191 }
192 }
193