|
65 | 65 | if ($action === 'bulktoggle' && confirm_sesskey()) { |
66 | 66 | $enabled = required_param('enabled', PARAM_INT); |
67 | 67 | $scope = optional_param('scope', 'all', PARAM_ALPHA); // 'all' or 'selected'. |
68 | | - $selectedIds = optional_param('selected_ids', '', PARAM_RAW); |
69 | | - if ($scope === 'selected' && !empty($selectedIds)) { |
70 | | - $ids = array_map('intval', explode(',', $selectedIds)); |
| 68 | + $selected_ids = optional_param('selected_ids', '', PARAM_RAW); |
| 69 | + if ($scope === 'selected' && !empty($selected_ids)) { |
| 70 | + $ids = array_map('intval', explode(',', $selected_ids)); |
71 | 71 | } else { |
72 | 72 | $ids = array_map(function($c) { return (int)$c->id; }, |
73 | 73 | $DB->get_records_sql("SELECT c.id FROM {course} c WHERE c.id > 1 AND c.visible = 1")); |
|
82 | 82 | if ($action === 'bulktoggleut' && confirm_sesskey()) { |
83 | 83 | $utvalue = required_param('utvalue', PARAM_RAW); |
84 | 84 | $scope = optional_param('scope', 'all', PARAM_ALPHA); |
85 | | - $selectedIds = optional_param('selected_ids', '', PARAM_RAW); |
86 | | - if ($scope === 'selected' && !empty($selectedIds)) { |
87 | | - $ids = array_map('intval', explode(',', $selectedIds)); |
| 85 | + $selected_ids = optional_param('selected_ids', '', PARAM_RAW); |
| 86 | + if ($scope === 'selected' && !empty($selected_ids)) { |
| 87 | + $ids = array_map('intval', explode(',', $selected_ids)); |
88 | 88 | } else { |
89 | 89 | $ids = array_map(function($c) { return (int)$c->id; }, |
90 | 90 | $DB->get_records_sql("SELECT c.id FROM {course} c WHERE c.id > 1 AND c.visible = 1")); |
|
111 | 111 |
|
112 | 112 | // ── Build course list with stats ──────────────────────────────────────────── |
113 | 113 | // Get all courses that have at least one SOLA message. |
114 | | -$coursesWithData = $DB->get_records_sql( |
| 114 | +$courses_with_data = $DB->get_records_sql( |
115 | 115 | "SELECT DISTINCT m.courseid, c.fullname, c.shortname |
116 | 116 | FROM {local_ai_course_assistant_msgs} m |
117 | 117 | JOIN {course} c ON c.id = m.courseid |
|
120 | 120 | ); |
121 | 121 |
|
122 | 122 | // Also get all visible courses (for the enable/disable toggle even if no data yet). |
123 | | -$allCourses = $DB->get_records_sql( |
| 123 | +$all_courses = $DB->get_records_sql( |
124 | 124 | "SELECT c.id, c.fullname, c.shortname |
125 | 125 | FROM {course} c |
126 | 126 | WHERE c.id > 1 AND c.visible = 1 |
127 | 127 | ORDER BY c.fullname ASC" |
128 | 128 | ); |
129 | 129 |
|
130 | 130 | // Merge: courses with data + all visible courses. |
131 | | -$courseList = []; |
132 | | -foreach ($allCourses as $c) { |
133 | | - $hasData = isset($coursesWithData[$c->id]); |
134 | | - $enabledVal = get_config('local_ai_course_assistant', 'sola_enabled_course_' . $c->id); |
135 | | - $isEnabled = ($enabledVal !== '0'); // Default: enabled. |
136 | | - $utVal = get_config('local_ai_course_assistant', 'sola_usertesting_course_' . $c->id); |
| 131 | +$course_list = []; |
| 132 | +foreach ($all_courses as $c) { |
| 133 | + $has_data = isset($courses_with_data[$c->id]); |
| 134 | + $enabled_val = get_config('local_ai_course_assistant', 'sola_enabled_course_' . $c->id); |
| 135 | + $is_enabled = ($enabled_val !== '0'); // Default: enabled. |
| 136 | + $ut_val = get_config('local_ai_course_assistant', 'sola_usertesting_course_' . $c->id); |
137 | 137 | // User testing state: '' = inherit, '1' = on, '0' = off. |
138 | | - $utState = ($utVal === '1') ? 'on' : (($utVal === '0') ? 'off' : 'inherit'); |
139 | | - $utGlobal = (bool) get_config('local_ai_course_assistant', 'usertesting_enabled'); |
140 | | - $utEffective = ($utVal === '1') || ($utVal !== '0' && $utGlobal); |
141 | | - $courseList[] = [ |
| 138 | + $ut_state = ($ut_val === '1') ? 'on' : (($ut_val === '0') ? 'off' : 'inherit'); |
| 139 | + $ut_global = (bool) get_config('local_ai_course_assistant', 'usertesting_enabled'); |
| 140 | + $ut_effective = ($ut_val === '1') || ($ut_val !== '0' && $ut_global); |
| 141 | + $course_list[] = [ |
142 | 142 | 'id' => (int) $c->id, |
143 | 143 | 'fullname' => $c->fullname, |
144 | 144 | 'shortname' => $c->shortname, |
145 | | - 'has_data' => $hasData, |
146 | | - 'enabled' => $isEnabled, |
| 145 | + 'has_data' => $has_data, |
| 146 | + 'enabled' => $is_enabled, |
147 | 147 | 'selected' => ((int) $c->id === $courseid), |
148 | | - 'ut_on' => $utState === 'on', |
149 | | - 'ut_off' => $utState === 'off', |
150 | | - 'ut_inherit'=> $utState === 'inherit', |
151 | | - 'ut_effective' => $utEffective, |
| 148 | + 'ut_on' => $ut_state === 'on', |
| 149 | + 'ut_off' => $ut_state === 'off', |
| 150 | + 'ut_inherit'=> $ut_state === 'inherit', |
| 151 | + 'ut_effective' => $ut_effective, |
152 | 152 | 'sesskey' => sesskey(), |
153 | 153 | 'range' => $range, |
154 | 154 | 'courseid_param' => $courseid, |
|
157 | 157 | } |
158 | 158 |
|
159 | 159 | // ── Cross-course summary stats ────────────────────────────────────────────── |
160 | | -$totalMsgsAll = $DB->count_records_sql( |
| 160 | +$total_msgs_all = $DB->count_records_sql( |
161 | 161 | "SELECT COUNT(m.id) FROM {local_ai_course_assistant_msgs} m |
162 | 162 | JOIN {course} c ON c.id = m.courseid WHERE c.id > 1" |
163 | 163 | ); |
164 | | -$activeStudentsAll = $DB->count_records_sql( |
| 164 | +$active_students_all = $DB->count_records_sql( |
165 | 165 | "SELECT COUNT(DISTINCT m.userid) FROM {local_ai_course_assistant_msgs} m |
166 | 166 | JOIN {course} c ON c.id = m.courseid WHERE c.id > 1 AND m.role = 'user'" |
167 | 167 | ); |
168 | | -$activeCourses = count($coursesWithData); |
169 | | -$enabledCourses = 0; |
170 | | -foreach ($courseList as $cl) { |
| 168 | +$active_courses = count($courses_with_data); |
| 169 | +$enabled_courses = 0; |
| 170 | +foreach ($course_list as $cl) { |
171 | 171 | if ($cl['enabled']) { |
172 | | - $enabledCourses++; |
| 172 | + $enabled_courses++; |
173 | 173 | } |
174 | 174 | } |
175 | 175 |
|
176 | 176 | // ── Per-course analytics (if a course is selected) ────────────────────────── |
177 | | -$courseData = null; |
178 | | -$courseName = ''; |
| 177 | +$course_data = null; |
| 178 | +$course_name = ''; |
179 | 179 | if ($courseid > 0) { |
180 | 180 | $course = $DB->get_record('course', ['id' => $courseid]); |
181 | 181 | if ($course) { |
182 | | - $courseName = $course->fullname; |
| 182 | + $course_name = $course->fullname; |
183 | 183 | $overview = analytics::get_overview($courseid, $since); |
184 | 184 | $dailyusage = analytics::get_daily_usage($courseid, $range > 0 ? $range : 90); |
185 | 185 | $hotspots = analytics::get_hotspots($courseid, $since); |
|
252 | 252 | ? round((float) $feedbacksummary->avg_rating, 1) : 0; |
253 | 253 |
|
254 | 254 | // Survey results. |
255 | | - $surveyData = null; |
| 255 | + $survey_data = null; |
256 | 256 | try { |
257 | | - $surveyResults = \local_ai_course_assistant\survey_manager::get_survey_results($courseid, $since); |
258 | | - if ($surveyResults['total_responses'] > 0) { |
259 | | - $surveyQuestions = []; |
260 | | - foreach ($surveyResults['questions'] as $q) { |
| 257 | + $survey_results = \local_ai_course_assistant\survey_manager::get_survey_results($courseid, $since); |
| 258 | + if ($survey_results['total_responses'] > 0) { |
| 259 | + $survey_questions = []; |
| 260 | + foreach ($survey_results['questions'] as $q) { |
261 | 261 | $sq = [ |
262 | 262 | 'text' => $q['text'], |
263 | 263 | 'type' => $q['type'], |
|
284 | 284 | $sq['distribution'] = $dist; |
285 | 285 | } |
286 | 286 | if ($q['type'] === 'long_text' && !empty($q['answers'])) { |
287 | | - $textAnswers = []; |
| 287 | + $text_answers = []; |
288 | 288 | foreach (array_slice($q['answers'], 0, 20) as $a) { |
289 | | - $textAnswers[] = ['text' => htmlspecialchars($a)]; |
| 289 | + $text_answers[] = ['text' => htmlspecialchars($a)]; |
290 | 290 | } |
291 | | - $sq['answers'] = $textAnswers; |
| 291 | + $sq['answers'] = $text_answers; |
292 | 292 | $sq['has_answers'] = true; |
293 | 293 | } |
294 | | - $surveyQuestions[] = $sq; |
| 294 | + $survey_questions[] = $sq; |
295 | 295 | } |
296 | | - $surveyData = [ |
297 | | - 'total_responses' => $surveyResults['total_responses'], |
298 | | - 'questions' => $surveyQuestions, |
| 296 | + $survey_data = [ |
| 297 | + 'total_responses' => $survey_results['total_responses'], |
| 298 | + 'questions' => $survey_questions, |
299 | 299 | ]; |
300 | 300 | } |
301 | 301 | } catch (\Throwable $e) { |
302 | 302 | // Survey tables may not exist yet on older installs. |
303 | 303 | } |
304 | 304 |
|
305 | 305 | // User testing results. |
306 | | - $utData = null; |
| 306 | + $ut_data = null; |
307 | 307 | try { |
308 | | - $utResults = \local_ai_course_assistant\usertesting_manager::get_results($courseid); |
309 | | - if ($utResults['total_respondents'] > 0) { |
310 | | - $utTasks = []; |
311 | | - foreach ($utResults['tasks'] as $t) { |
| 308 | + $ut_results = \local_ai_course_assistant\usertesting_manager::get_results($courseid); |
| 309 | + if ($ut_results['total_respondents'] > 0) { |
| 310 | + $ut_tasks = []; |
| 311 | + foreach ($ut_results['tasks'] as $t) { |
312 | 312 | $ut = [ |
313 | 313 | 'instruction' => $t['instruction'], |
314 | 314 | 'type' => $t['type'], |
|
343 | 343 | $ut['answers'] = $answers; |
344 | 344 | $ut['has_answers'] = !empty($answers); |
345 | 345 | } |
346 | | - $utTasks[] = $ut; |
| 346 | + $ut_tasks[] = $ut; |
347 | 347 | } |
348 | | - $utData = [ |
349 | | - 'total_respondents' => $utResults['total_respondents'], |
350 | | - 'tasks' => $utTasks, |
| 348 | + $ut_data = [ |
| 349 | + 'total_respondents' => $ut_results['total_respondents'], |
| 350 | + 'tasks' => $ut_tasks, |
351 | 351 | ]; |
352 | 352 | } |
353 | 353 | } catch (\Throwable $e) { |
354 | 354 | // User testing tables may not exist yet. |
355 | 355 | } |
356 | 356 |
|
357 | | - $courseData = [ |
| 357 | + $course_data = [ |
358 | 358 | 'courseid' => $courseid, |
359 | | - 'coursename' => $courseName, |
| 359 | + 'coursename' => $course_name, |
360 | 360 | 'overview' => $overview, |
361 | 361 | 'has_data' => $overview['total_messages'] > 0, |
362 | 362 | 'daily_usage_json' => json_encode($dailyusage), |
|
379 | 379 | 'feedback_ratings' => $ratingrows, |
380 | 380 | 'feedback_entries' => $feedbackentries, |
381 | 381 | 'has_feedback' => $feedbacktotal > 0, |
382 | | - 'survey_data' => $surveyData, |
383 | | - 'has_survey_data' => ($surveyData !== null), |
384 | | - 'usertesting_data' => $utData, |
385 | | - 'has_usertesting_data' => ($utData !== null), |
386 | | - 'has_any_feedback_data' => ($feedbacktotal > 0 || $surveyData !== null || $utData !== null), |
| 382 | + 'survey_data' => $survey_data, |
| 383 | + 'has_survey_data' => ($survey_data !== null), |
| 384 | + 'usertesting_data' => $ut_data, |
| 385 | + 'has_usertesting_data' => ($ut_data !== null), |
| 386 | + 'has_any_feedback_data' => ($feedbacktotal > 0 || $survey_data !== null || $ut_data !== null), |
387 | 387 | 'token_analytics_url' => (new moodle_url('/local/ai_course_assistant/token_analytics.php', |
388 | 388 | ['courseid' => $courseid, 'range' => $range]))->out(false), |
389 | 389 | ]; |
|
393 | 393 | // ── Build template data ───────────────────────────────────────────────────── |
394 | 394 | $templatedata = [ |
395 | 395 | // Cross-course summary. |
396 | | - 'total_messages_all' => $totalMsgsAll, |
397 | | - 'active_students_all' => $activeStudentsAll, |
398 | | - 'active_courses' => $activeCourses, |
399 | | - 'enabled_courses' => $enabledCourses, |
400 | | - 'total_courses' => count($courseList), |
| 396 | + 'total_messages_all' => $total_msgs_all, |
| 397 | + 'active_students_all' => $active_students_all, |
| 398 | + 'active_courses' => $active_courses, |
| 399 | + 'enabled_courses' => $enabled_courses, |
| 400 | + 'total_courses' => count($course_list), |
401 | 401 |
|
402 | 402 | // Course list for selector + toggle table. |
403 | | - 'courses' => $courseList, |
404 | | - 'has_courses' => !empty($courseList), |
| 403 | + 'courses' => $course_list, |
| 404 | + 'has_courses' => !empty($course_list), |
405 | 405 |
|
406 | 406 | // Per-course analytics (null if none selected). |
407 | | - 'has_course_selected' => ($courseData !== null), |
408 | | - 'course' => $courseData, |
| 407 | + 'has_course_selected' => ($course_data !== null), |
| 408 | + 'course' => $course_data, |
409 | 409 |
|
410 | 410 | // Time range. |
411 | 411 | 'range' => $range, |
|
0 commit comments