| Conditions | 40 |
| Paths | > 20000 |
| Total Lines | 317 |
| Code Lines | 177 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 74 | function build_current_courses_rows(int $userId, int $sessionId = 0): array |
||
| 75 | { |
||
| 76 | $rows = []; |
||
| 77 | $rowIndex = 0; |
||
| 78 | |||
| 79 | $myCourses = CourseManager::get_course_list_of_user_as_course_admin($userId); |
||
| 80 | |||
| 81 | if (empty($myCourses)) { |
||
| 82 | return $rows; |
||
| 83 | } |
||
| 84 | |||
| 85 | foreach ($myCourses as $course) { |
||
| 86 | $courseId = (int) ($course['id'] ?? 0); |
||
| 87 | |||
| 88 | if (0 === $courseId) { |
||
| 89 | continue; |
||
| 90 | } |
||
| 91 | |||
| 92 | $courseInfo = api_get_course_info_by_id($courseId); |
||
| 93 | if (empty($courseInfo)) { |
||
| 94 | // Safety check: if course info is not found, skip. |
||
| 95 | continue; |
||
| 96 | } |
||
| 97 | |||
| 98 | $courseCode = $courseInfo['code'] ?? $course['code']; |
||
| 99 | |||
| 100 | // Only show open courses. |
||
| 101 | if (0 === (int) $courseInfo['visibility']) { |
||
| 102 | continue; |
||
| 103 | } |
||
| 104 | |||
| 105 | // ------------------------------------------------------------ |
||
| 106 | // 1) Teachers list (for display). |
||
| 107 | // ------------------------------------------------------------ |
||
| 108 | $teachers = CourseManager::get_teacher_list_from_course_code($courseCode); |
||
| 109 | $teacherNames = []; |
||
| 110 | |||
| 111 | if (!empty($teachers)) { |
||
| 112 | foreach ($teachers as $teacher) { |
||
| 113 | $teacherNames[] = $teacher['firstname'].' '.$teacher['lastname']; |
||
| 114 | } |
||
| 115 | } |
||
| 116 | |||
| 117 | // ------------------------------------------------------------ |
||
| 118 | // 2) Students list (only "real" students, status = STUDENT). |
||
| 119 | // ------------------------------------------------------------ |
||
| 120 | $tmpStudents = CourseManager::get_student_list_from_course_code($courseCode, false); |
||
| 121 | $students = []; |
||
| 122 | |||
| 123 | foreach ($tmpStudents as $student) { |
||
| 124 | $userInfo = api_get_user_info($student['user_id']); |
||
| 125 | if (STUDENT !== (int) $userInfo['status']) { |
||
| 126 | continue; |
||
| 127 | } |
||
| 128 | |||
| 129 | $students[] = (int) $student['user_id']; |
||
| 130 | } |
||
| 131 | |||
| 132 | $studentCount = count($students); |
||
| 133 | $studentsIdList = $studentCount > 0 ? implode(',', $students) : ''; |
||
| 134 | |||
| 135 | // ------------------------------------------------------------ |
||
| 136 | // 3) Course tables and main tables. |
||
| 137 | // ------------------------------------------------------------ |
||
| 138 | $tLp = Database::get_course_table(TABLE_LP_MAIN); // c_lp |
||
| 139 | $tLpItem = Database::get_course_table(TABLE_LP_ITEM); // c_lp_item |
||
| 140 | $tNews = Database::get_course_table(TABLE_ANNOUNCEMENT); // c_announcement |
||
| 141 | $tLpView = Database::get_course_table(TABLE_LP_VIEW); // c_lp_view |
||
| 142 | $tLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW); // c_lp_item_view |
||
| 143 | $tResourceLink = 'resource_link'; // Main table name for resource links. |
||
| 144 | |||
| 145 | // ------------------------------------------------------------ |
||
| 146 | // 4) Get LP list for this course (base course, no session). |
||
| 147 | // FIRST TRY: through resource_link (original behaviour). |
||
| 148 | // ------------------------------------------------------------ |
||
| 149 | $sqlLp = "SELECT lp.iid, lp.title |
||
| 150 | FROM $tLp lp |
||
| 151 | INNER JOIN $tResourceLink li |
||
| 152 | ON lp.resource_node_id = li.id |
||
| 153 | WHERE li.c_id = $courseId |
||
| 154 | AND li.session_id = $sessionId"; |
||
| 155 | |||
| 156 | $resLp = Database::query($sqlLp); |
||
| 157 | |||
| 158 | if (0 === Database::num_rows($resLp)) { |
||
| 159 | // SECOND TRY (fallback): LPs that have tracking entries in c_lp_view. |
||
| 160 | $sqlLpFallback = "SELECT DISTINCT lp.iid, lp.title |
||
| 161 | FROM $tLp lp |
||
| 162 | INNER JOIN $tLpView v |
||
| 163 | ON v.lp_id = lp.iid |
||
| 164 | WHERE v.c_id = $courseId |
||
| 165 | AND (v.session_id = $sessionId OR v.session_id IS NULL)"; |
||
| 166 | $resLp = Database::query($sqlLpFallback); |
||
| 167 | } |
||
| 168 | |||
| 169 | if (0 === Database::num_rows($resLp)) { |
||
| 170 | // Course without learning paths: still add one row so the course |
||
| 171 | // is visible in the report (all stats = 0). |
||
| 172 | $rows[$rowIndex] = [ |
||
| 173 | 'lp' => get_lang('No learning paths'), |
||
| 174 | 'teachers' => !empty($teacherNames) ? implode(', ', $teacherNames) : '', |
||
| 175 | 'course_name' => $course['title'], |
||
| 176 | 'count_students' => $studentCount, |
||
| 177 | 'count_students_accessing' => 0, |
||
| 178 | 'count_students_accessing_percentage' => 0, |
||
| 179 | // Legacy naming: "_at_50" actually stores the count of 100% completed. |
||
| 180 | 'count_students_complete_all_activities_at_50' => 0, |
||
| 181 | // Legacy: this stores the percentage of 100% completed. |
||
| 182 | 'count_students_complete_all_activities' => 0, |
||
| 183 | 'average_percentage_activities_completed_per_student' => 0, |
||
| 184 | 'total_time_spent' => 0, |
||
| 185 | 'average_time_spent_per_student' => 0, |
||
| 186 | 'learnpath_docs' => 0, |
||
| 187 | 'learnpath_exercises' => 0, |
||
| 188 | 'learnpath_links' => 0, |
||
| 189 | 'learnpath_forums' => 0, |
||
| 190 | 'learnpath_assignments' => 0, |
||
| 191 | 'total_announcements' => 0, |
||
| 192 | ]; |
||
| 193 | $rowIndex++; |
||
| 194 | |||
| 195 | continue; |
||
| 196 | } |
||
| 197 | |||
| 198 | // Store LPs in a PHP array to be able to iterate twice. |
||
| 199 | $lpRows = []; |
||
| 200 | $lpIds = []; |
||
| 201 | |||
| 202 | while ($lpRow = Database::fetch_array($resLp)) { |
||
| 203 | $lpId = (int) $lpRow['iid']; |
||
| 204 | $lpRows[] = $lpRow; |
||
| 205 | $lpIds[] = $lpId; |
||
| 206 | } |
||
| 207 | |||
| 208 | if (empty($lpRows)) { |
||
| 209 | continue; |
||
| 210 | } |
||
| 211 | |||
| 212 | // ------------------------------------------------------------ |
||
| 213 | // 5) Pre-compute stats per (lp, user) using c_lp_view + c_lp_item_view. |
||
| 214 | // This avoids looping over each student in PHP. |
||
| 215 | // ------------------------------------------------------------ |
||
| 216 | $lpStats = []; // [lp_id => ['count_accessing', 'count_100', 'sum_progress', 'sum_time']] |
||
| 217 | |||
| 218 | if ($studentCount > 0 && !empty($lpIds) && '' !== $studentsIdList) { |
||
| 219 | $lpIdList = implode(',', $lpIds); |
||
| 220 | |||
| 221 | // This query returns ONE row per (lp_id, user_id) with: |
||
| 222 | // - progress (0..100) |
||
| 223 | // - total_time (sum of all item views in that LP for that user) |
||
| 224 | $sqlStats = " |
||
| 225 | SELECT |
||
| 226 | v.lp_id, |
||
| 227 | v.user_id, |
||
| 228 | COALESCE(MAX(v.progress), 0) AS progress, |
||
| 229 | COALESCE(SUM(iv.total_time), 0) AS total_time |
||
| 230 | FROM $tLpView v |
||
| 231 | LEFT JOIN $tLpItemView iv |
||
| 232 | ON iv.lp_view_id = v.iid |
||
| 233 | WHERE v.c_id = $courseId |
||
| 234 | AND (v.session_id = $sessionId OR v.session_id IS NULL) |
||
| 235 | AND v.lp_id IN ($lpIdList) |
||
| 236 | AND v.user_id IN ($studentsIdList) |
||
| 237 | GROUP BY v.lp_id, v.user_id |
||
| 238 | "; |
||
| 239 | |||
| 240 | $resStats = Database::query($sqlStats); |
||
| 241 | |||
| 242 | while ($row = Database::fetch_array($resStats)) { |
||
| 243 | $lpId = (int) $row['lp_id']; |
||
| 244 | $progress = (int) $row['progress']; |
||
| 245 | $totalTime = (int) $row['total_time']; |
||
| 246 | |||
| 247 | if (!isset($lpStats[$lpId])) { |
||
| 248 | $lpStats[$lpId] = [ |
||
| 249 | 'count_accessing' => 0, |
||
| 250 | 'count_100' => 0, |
||
| 251 | 'sum_progress' => 0, |
||
| 252 | 'sum_time' => 0, |
||
| 253 | ]; |
||
| 254 | } |
||
| 255 | |||
| 256 | // Student is considered "accessing" if there is progress or time. |
||
| 257 | if ($progress > 0 || $totalTime > 0) { |
||
| 258 | $lpStats[$lpId]['count_accessing']++; |
||
| 259 | } |
||
| 260 | |||
| 261 | // Student completed 100% of the LP. |
||
| 262 | if (100 === $progress) { |
||
| 263 | $lpStats[$lpId]['count_100']++; |
||
| 264 | } |
||
| 265 | |||
| 266 | $lpStats[$lpId]['sum_progress'] += $progress; |
||
| 267 | $lpStats[$lpId]['sum_time'] += $totalTime; |
||
| 268 | } |
||
| 269 | } |
||
| 270 | |||
| 271 | // ------------------------------------------------------------ |
||
| 272 | // 6) Build one row per LP with all stats. |
||
| 273 | // ------------------------------------------------------------ |
||
| 274 | foreach ($lpRows as $lpRow) { |
||
| 275 | $lpId = (int) $lpRow['iid']; |
||
| 276 | $lpTitle = $lpRow['title']; |
||
| 277 | |||
| 278 | // Base row with default values. |
||
| 279 | $rows[$rowIndex] = [ |
||
| 280 | 'lp' => '<a href="'. |
||
| 281 | api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?cidReq='. |
||
| 282 | $courseCode.'&action=view&lp_id='.$lpId. |
||
| 283 | '" target="_blank">'. |
||
| 284 | Security::remove_XSS($lpTitle). |
||
| 285 | '</a>', |
||
| 286 | 'teachers' => !empty($teacherNames) ? implode(', ', $teacherNames) : '', |
||
| 287 | 'course_name' => $course['title'], |
||
| 288 | 'count_students' => $studentCount, |
||
| 289 | 'count_students_accessing' => 0, |
||
| 290 | 'count_students_accessing_percentage' => 0, |
||
| 291 | // Legacy naming kept for compatibility. |
||
| 292 | 'count_students_complete_all_activities_at_50' => 0, |
||
| 293 | 'count_students_complete_all_activities' => 0, |
||
| 294 | 'average_percentage_activities_completed_per_student' => 0, |
||
| 295 | 'total_time_spent' => 0, |
||
| 296 | 'average_time_spent_per_student' => 0, |
||
| 297 | 'learnpath_docs' => 0, |
||
| 298 | 'learnpath_exercises' => 0, |
||
| 299 | 'learnpath_links' => 0, |
||
| 300 | 'learnpath_forums' => 0, |
||
| 301 | 'learnpath_assignments' => 0, |
||
| 302 | 'total_announcements' => 0, |
||
| 303 | ]; |
||
| 304 | |||
| 305 | // 6.a) Apply pre-computed stats for this LP (if any). |
||
| 306 | if ($studentCount > 0 && isset($lpStats[$lpId])) { |
||
| 307 | $stats = $lpStats[$lpId]; |
||
| 308 | |||
| 309 | $countAccessing = (int) $stats['count_accessing']; |
||
| 310 | $countComplete100 = (int) $stats['count_100']; |
||
| 311 | $sumProgress = (int) $stats['sum_progress']; |
||
| 312 | $sumTime = (int) $stats['sum_time']; |
||
| 313 | |||
| 314 | $rows[$rowIndex]['count_students_accessing'] = $countAccessing; |
||
| 315 | |||
| 316 | if ($studentCount > 0) { |
||
| 317 | $rows[$rowIndex]['count_students_accessing_percentage'] = |
||
| 318 | $countAccessing > 0 |
||
| 319 | ? round($countAccessing / $studentCount * 100, 0) |
||
| 320 | : 0; |
||
| 321 | |||
| 322 | // Keep legacy semantics: |
||
| 323 | // - "_at_50" column stores the absolute number of students at 100%. |
||
| 324 | // - "count_students_complete_all_activities" stores the percentage. |
||
| 325 | $rows[$rowIndex]['count_students_complete_all_activities_at_50'] = |
||
| 326 | $countComplete100; |
||
| 327 | |||
| 328 | $rows[$rowIndex]['count_students_complete_all_activities'] = |
||
| 329 | $countComplete100 > 0 |
||
| 330 | ? round($countComplete100 / $studentCount * 100, 0) |
||
| 331 | : 0; |
||
| 332 | |||
| 333 | // Average progress across all enrolled students (0..100). |
||
| 334 | $rows[$rowIndex]['average_percentage_activities_completed_per_student'] = |
||
| 335 | $sumProgress > 0 |
||
| 336 | ? round($sumProgress / $studentCount, 2) |
||
| 337 | : 0; |
||
| 338 | } |
||
| 339 | |||
| 340 | if ($sumTime > 0) { |
||
| 341 | $rows[$rowIndex]['total_time_spent'] = api_time_to_hms($sumTime); |
||
| 342 | $rows[$rowIndex]['average_time_spent_per_student'] = |
||
| 343 | api_time_to_hms($sumTime / max($studentCount, 1)); |
||
| 344 | } |
||
| 345 | } |
||
| 346 | |||
| 347 | // 6.b) Count LP items (documents, quizzes, links, forums, assignments). |
||
| 348 | $sqlLpItems = "SELECT lpi.item_type |
||
| 349 | FROM $tLpItem lpi |
||
| 350 | WHERE lpi.lp_id = $lpId |
||
| 351 | ORDER BY lpi.item_type"; |
||
| 352 | $resLpItems = Database::query($sqlLpItems); |
||
| 353 | |||
| 354 | while ($rowItem = Database::fetch_array($resLpItems)) { |
||
| 355 | switch ($rowItem['item_type']) { |
||
| 356 | case 'document': |
||
| 357 | $rows[$rowIndex]['learnpath_docs']++; |
||
| 358 | break; |
||
| 359 | case 'quiz': |
||
| 360 | $rows[$rowIndex]['learnpath_exercises']++; |
||
| 361 | break; |
||
| 362 | case 'link': |
||
| 363 | $rows[$rowIndex]['learnpath_links']++; |
||
| 364 | break; |
||
| 365 | case 'forum': |
||
| 366 | case 'thread': |
||
| 367 | $rows[$rowIndex]['learnpath_forums']++; |
||
| 368 | break; |
||
| 369 | case 'student_publication': |
||
| 370 | $rows[$rowIndex]['learnpath_assignments']++; |
||
| 371 | break; |
||
| 372 | } |
||
| 373 | } |
||
| 374 | |||
| 375 | // 6.c) Announcements count (optional – commented out for perf). |
||
| 376 | /* |
||
| 377 | $sqlNews = "SELECT COUNT(n.iid) AS total |
||
| 378 | FROM $tNews n |
||
| 379 | WHERE n.c_id = $courseId"; |
||
| 380 | $resNews = Database::query($sqlNews); |
||
| 381 | if ($rowNews = Database::fetch_array($resNews)) { |
||
| 382 | $rows[$rowIndex]['total_announcements'] = (int) $rowNews['total']; |
||
| 383 | } |
||
| 384 | */ |
||
| 385 | |||
| 386 | $rowIndex++; |
||
| 387 | } |
||
| 388 | } |
||
| 389 | |||
| 390 | return $rows; |
||
| 391 | } |
||
| 683 |