forked from gocodebox/lifterlms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclass-llms-grades.php
273 lines (213 loc) · 6.66 KB
/
class-llms-grades.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
<?php
/**
* Get & Set grades for gradable post types
*
* @package LifterLMS/Classes
*
* @since 3.24.0
* @version 6.0.0
*/
defined( 'ABSPATH' ) || exit;
/**
* LLMS_Grades
*
* @since 3.24.0
* @since 5.3.0 Replace singleton code with `LLMS_Trait_Singleton`.
* @since 6.0.0 Removed the deprecated `LLMS_Grades::$_instance` property.
*/
class LLMS_Grades {
use LLMS_Trait_Singleton;
/**
* Determines the rounding precision used by grading functions
*
* @var int
*/
private $rounding_precision = 2;
/**
* Private constructor
*
* @since 3.24.0
* @version 3.24.0
*/
private function __construct() {
$this->rounding_precision = apply_filters( 'llms_grade_rounding_precision', $this->rounding_precision );
}
/**
* Calculates the grades for elements that have a list of children which are averaged / weighted to come up with the total grade
*
* @param array $children list of child objects
* @param LLMS_Student $student A LLMS_Student object.
* @return float|null
* @since 3.24.0
* @version 3.24.0
*/
private function calculate_grade_from_children( $children, $student ) {
$grade = null;
$grades = array();
// Loop through all the children and compile the overall grade & points data.
foreach ( $children as $child_id ) {
$child = llms_get_post( $child_id );
$grade = $this->get_grade( $child_id, $student, false );
// Non numeric grade (null) hasn't been taken yet or no gradable elements exist on the child.
if ( ! is_numeric( $grade ) ) {
continue;
}
$points = $child->get( 'points' );
// If no points assigned to the child, the grade doesn't count towards the overall grade.
if ( ! $points ) {
continue;
}
// Add the grade & points for further processing after we have all the data.
$grades[] = array(
'grade' => $grade,
'points' => $points,
);
}
// If we have at least one grade.
if ( count( $grades ) ) {
// Get the total available points for all children with a numeric grade & a points value.
$total_points = array_sum( wp_list_pluck( $grades, 'points' ) );
// If we don't have any points this element can't have an overall grade.
if ( $total_points ) {
// Sum up the adjusted grade.
$grade = 0;
foreach ( $grades as $data ) {
// Calculate the adjusted the grade.
// Grade multiplied by available points over total points.
$grade += $data['grade'] * ( $data['points'] / $total_points );
}
}
}
return $grade;
}
/**
* Calculate the grade for a course
*
* @param LLMS_Course $course A LLMS_Course object.
* @param LLMS_Student $student A LLMS_Student object.
* @return float|null
* @since 3.24.0
* @version 3.24.0
*/
private function calculate_course_grade( $course, $student ) {
return apply_filters(
'llms_calculate_course_grade',
$this->calculate_grade_from_children( $course->get_lessons( 'ids' ), $student ),
$course,
$student
);
}
/**
* Main grade calculation function
* Calculates the grade for a gradable post model
* DOES NOT CACHE RESULTS!
* See get_grade() for a function which uses caching
*
* @param LLMS_Post_Model $post A LLMS_Post_Model object.
* @param LLMS_Student $student A LLMS_Student object.
* @return float|null
* @since 3.24.0
* @version 3.24.0
*/
public function calculate_grade( $post, $student ) {
$grade = null;
$post_type = $post->get( 'type' );
switch ( $post_type ) {
case 'course':
/** @var LLMS_Course $post */
$grade = $this->calculate_course_grade( $post, $student );
break;
case 'lesson':
/** @var LLMS_Lesson $post */
$grade = $this->calculate_lesson_grade( $post, $student );
break;
case 'llms_quiz':
$attempt = $student->quizzes()->get_best_attempt( $post->get( 'id' ) );
if ( $attempt ) {
$grade = $attempt->get( 'grade' );
}
break;
// 3rd party / custom element grading.
default:
$grade = apply_filters( 'llms_calculate_' . $post_type . '_grade', $grade, $post, $student );
}
// Round numeric results.
if ( is_numeric( $grade ) ) {
$grade = $this->round( $grade );
}
return apply_filters( 'llms_calculate_grade', $grade, $post, $student );
}
/**
* Calculates the grade for a lesson
*
* @param LLMS_Lesson $lesson A LLMS_Lesson object.
* @param LLMS_Student $student A LLMS_Student object.
* @return float|null
* @since 3.24.0
* @version 3.24.0
*/
private function calculate_lesson_grade( $lesson, $student ) {
$grade = null;
if ( $lesson->is_quiz_enabled() ) {
$grade = $this->get_grade( $lesson->get( 'quiz' ), $student, false );
}
return apply_filters( 'llms_calculate_lesson_grade', $grade, $lesson, $student );
}
/**
* Main grade getter function
*
* Uses caching by default and can bypass cache when requested
*
* @since 3.24.0
* @since 4.4.4 Don't pass the `$use_cache` parameter to the `calculate_grade()` method.
*
* @param WP_Post|int $post_id An instance of WP_Post or a WP Post ID.
* @param LLMS_Student $student A LLMS_Student object.
* @param bool $use_cache when true, retrieves from cache if available
* @return float|null
*/
public function get_grade( $post_id, $student, $use_cache = true ) {
$post = llms_get_post( $post_id );
$student = llms_get_student( $student );
$grade = $use_cache ? $this->get_grade_from_cache( $post, $student ) : false;
// Grade not found in cache or we're not using the cache.
if ( false === $grade ) {
$grade = $this->calculate_grade( $post, $student );
// Store in the cache.
wp_cache_set(
sprintf( '%d_grade', $post->get( 'id' ) ),
$grade,
sprintf( 'student_%d', $student->get( 'id' ) )
);
}
return apply_filters( 'llms_get_grade', $grade, $post, $student );
}
/**
* Retrieve a grade from the wp_cache
*
* @param LLMS_Post_Model $post A LLMS_Post_Model object.
* @param LLMS_Student $student A LLMS_Student object.
* @return mixed grade as a float
* null if there's no grade for the post
* false if the grade wasn't found in the cache
* @since 3.24.0
* @version 3.24.0
*/
private function get_grade_from_cache( $post, $student ) {
return wp_cache_get(
sprintf( '%d_grade', $post->get( 'id' ) ),
sprintf( 'student_%d', $student->get( 'id' ) )
);
}
/**
* Round grades according to filterable rounding options set during construction
*
* @param float $grade Grade to round
* @return float
* @since 3.24.0
* @version 3.24.0
*/
public function round( $grade ) {
return round( $grade, $this->rounding_precision );
}
}