| Línea 28... |
Línea 28... |
| 28 |
use moodle_exception;
|
28 |
use moodle_exception;
|
| 29 |
use moodle_url;
|
29 |
use moodle_url;
|
| 30 |
use context_system;
|
30 |
use context_system;
|
| 31 |
use stdClass;
|
31 |
use stdClass;
|
| 32 |
use html_writer;
|
32 |
use html_writer;
|
| - |
|
33 |
use core_plugin_manager;
|
| Línea 33... |
Línea 34... |
| 33 |
|
34 |
|
| 34 |
/**
|
35 |
/**
|
| 35 |
* Methods to use when registering the site at the moodle sites directory.
|
36 |
* Methods to use when registering the site at the moodle sites directory.
|
| 36 |
*
|
37 |
*
|
| Línea 41... |
Línea 42... |
| 41 |
class registration {
|
42 |
class registration {
|
| Línea 42... |
Línea 43... |
| 42 |
|
43 |
|
| 43 |
/** @var array Fields used in a site registration form.
|
44 |
/** @var array Fields used in a site registration form.
|
| 44 |
* IMPORTANT: any new fields with non-empty defaults have to be added to CONFIRM_NEW_FIELDS */
|
45 |
* IMPORTANT: any new fields with non-empty defaults have to be added to CONFIRM_NEW_FIELDS */
|
| 45 |
const FORM_FIELDS = ['policyagreed', 'language', 'countrycode', 'privacy',
|
46 |
const FORM_FIELDS = ['policyagreed', 'language', 'countrycode', 'privacy',
|
| 46 |
'contactemail', 'contactable', 'emailalert', 'emailalertemail', 'commnews', 'commnewsemail',
|
47 |
'contactemail', 'emailalert', 'emailalertemail', 'commnews', 'commnewsemail',
|
| - |
|
48 |
'contactname', 'name', 'description', 'imageurl', 'contactphone', 'regioncode',
|
| Línea 47... |
Línea 49... |
| 47 |
'contactname', 'name', 'description', 'imageurl', 'contactphone', 'regioncode', 'geolocation', 'street'];
|
49 |
'geolocation', 'street', 'organisationtype'];
|
| 48 |
|
50 |
|
| 49 |
/** @var array List of new FORM_FIELDS or siteinfo fields added indexed by the version when they were added.
|
51 |
/** @var array List of new FORM_FIELDS or siteinfo fields added indexed by the version when they were added.
|
| 50 |
* If site was already registered, admin will be promted to confirm new registration data manually. Until registration is manually confirmed,
|
52 |
* If site was already registered, admin will be promted to confirm new registration data manually. Until registration is manually confirmed,
|
| Línea 60... |
Línea 62... |
| 60 |
2019022200 => ['analyticsenabledmodels', 'analyticspredictions', 'analyticsactions', 'analyticsactionsnotuseful'],
|
62 |
2019022200 => ['analyticsenabledmodels', 'analyticspredictions', 'analyticsactions', 'analyticsactionsnotuseful'],
|
| 61 |
// Active users stats added in Moodle 3.9.
|
63 |
// Active users stats added in Moodle 3.9.
|
| 62 |
2020022600 => ['activeusers', 'activeparticipantnumberaverage'],
|
64 |
2020022600 => ['activeusers', 'activeparticipantnumberaverage'],
|
| 63 |
// Database type, course date info, site theme, primary auth type added in Moodle 4.2.
|
65 |
// Database type, course date info, site theme, primary auth type added in Moodle 4.2.
|
| 64 |
2023021700 => ['dbtype', 'coursesnodates', 'sitetheme', 'primaryauthtype'],
|
66 |
2023021700 => ['dbtype', 'coursesnodates', 'sitetheme', 'primaryauthtype'],
|
| - |
|
67 |
// Plugin usage added in Moodle 4.5.
|
| - |
|
68 |
2023072300 => ['pluginusage'],
|
| - |
|
69 |
// AI usage added in Moodle 4.5.
|
| - |
|
70 |
2023081200 => ['aiusage'],
|
| 65 |
];
|
71 |
];
|
| Línea 66... |
Línea 72... |
| 66 |
|
72 |
|
| 67 |
/** @var string Site privacy: not displayed */
|
73 |
/** @var string Site privacy: not displayed */
|
| Línea 137... |
Línea 143... |
| 137 |
}
|
143 |
}
|
| 138 |
return $registration->token;
|
144 |
return $registration->token;
|
| 139 |
}
|
145 |
}
|
| Línea 140... |
Línea 146... |
| 140 |
|
146 |
|
| - |
|
147 |
/**
|
| - |
|
148 |
* Returns registration secret.
|
| - |
|
149 |
*
|
| - |
|
150 |
* @return string
|
| - |
|
151 |
*/
|
| - |
|
152 |
public static function get_secret(): string {
|
| - |
|
153 |
if ($registration = self::get_registration()) {
|
| - |
|
154 |
return $registration->secret;
|
| - |
|
155 |
}
|
| - |
|
156 |
return '';
|
| - |
|
157 |
}
|
| - |
|
158 |
|
| 141 |
/**
|
159 |
/**
|
| 142 |
* When was the registration last updated
|
160 |
* When was the registration last updated
|
| 143 |
*
|
161 |
*
|
| 144 |
* @return int|null timestamp or null if site is not registered
|
162 |
* @return int|null timestamp or null if site is not registered
|
| 145 |
*/
|
163 |
*/
|
| Línea 183... |
Línea 201... |
| 183 |
$siteinfo['activeparticipantnumberaverage'] = average_number_of_participants(true, time() - DAYSECS * 30);
|
201 |
$siteinfo['activeparticipantnumberaverage'] = average_number_of_participants(true, time() - DAYSECS * 30);
|
| 184 |
$siteinfo['modulenumberaverage'] = average_number_of_courses_modules();
|
202 |
$siteinfo['modulenumberaverage'] = average_number_of_courses_modules();
|
| 185 |
$siteinfo['dbtype'] = $CFG->dbtype;
|
203 |
$siteinfo['dbtype'] = $CFG->dbtype;
|
| 186 |
$siteinfo['coursesnodates'] = $DB->count_records_select('course', 'enddate = ?', [0]) - 1;
|
204 |
$siteinfo['coursesnodates'] = $DB->count_records_select('course', 'enddate = ?', [0]) - 1;
|
| 187 |
$siteinfo['sitetheme'] = get_config('core', 'theme');
|
205 |
$siteinfo['sitetheme'] = get_config('core', 'theme');
|
| - |
|
206 |
$siteinfo['pluginusage'] = json_encode(self::get_plugin_usage_data());
|
| - |
|
207 |
|
| - |
|
208 |
// AI usage data.
|
| - |
|
209 |
$aiusagedata = self::get_ai_usage_data();
|
| - |
|
210 |
$siteinfo['aiusage'] = !empty($aiusagedata) ? json_encode($aiusagedata) : '';
|
| Línea 188... |
Línea 211... |
| 188 |
|
211 |
|
| 189 |
// Primary auth type.
|
212 |
// Primary auth type.
|
| 190 |
$primaryauthsql = 'SELECT auth, count(auth) as tc FROM {user} GROUP BY auth ORDER BY tc DESC';
|
213 |
$primaryauthsql = 'SELECT auth, count(auth) as tc FROM {user} GROUP BY auth ORDER BY tc DESC';
|
| Línea 226... |
Línea 249... |
| 226 |
*
|
249 |
*
|
| 227 |
* @param array $siteinfo result of get_site_info()
|
250 |
* @param array $siteinfo result of get_site_info()
|
| 228 |
* @return string
|
251 |
* @return string
|
| 229 |
*/
|
252 |
*/
|
| 230 |
public static function get_stats_summary($siteinfo) {
|
253 |
public static function get_stats_summary($siteinfo) {
|
| - |
|
254 |
global $OUTPUT;
|
| - |
|
255 |
|
| 231 |
$fieldsneedconfirm = self::get_new_registration_fields();
|
256 |
$fieldsneedconfirm = self::get_new_registration_fields();
|
| 232 |
$summary = html_writer::tag('p', get_string('sendfollowinginfo_help', 'hub')) .
|
257 |
$summary = html_writer::tag('p', get_string('sendfollowinginfo_help', 'hub')) .
|
| 233 |
html_writer::start_tag('ul');
|
258 |
html_writer::start_tag('ul');
|
| Línea 234... |
Línea 259... |
| 234 |
|
259 |
|
| 235 |
$mobileservicesenabled = $siteinfo['mobileservicesenabled'] ? get_string('yes') : get_string('no');
|
260 |
$mobileservicesenabled = $siteinfo['mobileservicesenabled'] ? get_string('yes') : get_string('no');
|
| 236 |
$mobilenotificationsenabled = $siteinfo['mobilenotificationsenabled'] ? get_string('yes') : get_string('no');
|
261 |
$mobilenotificationsenabled = $siteinfo['mobilenotificationsenabled'] ? get_string('yes') : get_string('no');
|
| 237 |
$moodlerelease = $siteinfo['moodlerelease'];
|
262 |
$moodlerelease = $siteinfo['moodlerelease'];
|
| 238 |
if (preg_match('/^(\d+\.\d.*?)[\. ]/', $moodlerelease, $matches)) {
|
263 |
if (preg_match('/^(\d+\.\d.*?)[\. ]/', $moodlerelease, $matches)) {
|
| 239 |
$moodlerelease = $matches[1];
|
264 |
$moodlerelease = $matches[1];
|
| - |
|
265 |
}
|
| - |
|
266 |
$pluginusagelinks = [
|
| - |
|
267 |
'overview' => new moodle_url('/admin/plugins.php'),
|
| - |
|
268 |
'activities' => new moodle_url('/admin/modules.php'),
|
| - |
|
269 |
'blocks' => new moodle_url('/admin/blocks.php'),
|
| 240 |
}
|
270 |
];
|
| 241 |
$senddata = [
|
271 |
$senddata = [
|
| 242 |
'moodlerelease' => get_string('sitereleasenum', 'hub', $moodlerelease),
|
272 |
'moodlerelease' => get_string('sitereleasenum', 'hub', $moodlerelease),
|
| 243 |
'courses' => get_string('coursesnumber', 'hub', $siteinfo['courses']),
|
273 |
'courses' => get_string('coursesnumber', 'hub', $siteinfo['courses']),
|
| 244 |
'users' => get_string('usersnumber', 'hub', $siteinfo['users']),
|
274 |
'users' => get_string('usersnumber', 'hub', $siteinfo['users']),
|
| Línea 265... |
Línea 295... |
| 265 |
'analyticsactionsnotuseful' => get_string('analyticsactionsnotuseful', 'hub', $siteinfo['analyticsactionsnotuseful']),
|
295 |
'analyticsactionsnotuseful' => get_string('analyticsactionsnotuseful', 'hub', $siteinfo['analyticsactionsnotuseful']),
|
| 266 |
'dbtype' => get_string('dbtype', 'hub', $siteinfo['dbtype']),
|
296 |
'dbtype' => get_string('dbtype', 'hub', $siteinfo['dbtype']),
|
| 267 |
'coursesnodates' => get_string('coursesnodates', 'hub', $siteinfo['coursesnodates']),
|
297 |
'coursesnodates' => get_string('coursesnodates', 'hub', $siteinfo['coursesnodates']),
|
| 268 |
'sitetheme' => get_string('sitetheme', 'hub', $siteinfo['sitetheme']),
|
298 |
'sitetheme' => get_string('sitetheme', 'hub', $siteinfo['sitetheme']),
|
| 269 |
'primaryauthtype' => get_string('primaryauthtype', 'hub', $siteinfo['primaryauthtype']),
|
299 |
'primaryauthtype' => get_string('primaryauthtype', 'hub', $siteinfo['primaryauthtype']),
|
| - |
|
300 |
'pluginusage' => get_string('pluginusagedata', 'hub', $pluginusagelinks),
|
| - |
|
301 |
'aiusage' => $OUTPUT->render_from_template('core/ai_usage_data', ['aiusagedata' => self::show_ai_usage()]),
|
| 270 |
];
|
302 |
];
|
| Línea 271... |
Línea 303... |
| 271 |
|
303 |
|
| 272 |
foreach ($senddata as $key => $str) {
|
304 |
foreach ($senddata as $key => $str) {
|
| 273 |
$class = in_array($key, $fieldsneedconfirm) ? ' needsconfirmation mark' : '';
|
305 |
$class = in_array($key, $fieldsneedconfirm) ? ' needsconfirmation mark' : '';
|
| Línea 359... |
Línea 391... |
| 359 |
|
391 |
|
| 360 |
$registration = self::get_registration(false);
|
392 |
$registration = self::get_registration(false);
|
| 361 |
if (!$registration || $registration->token !== $token) {
|
393 |
if (!$registration || $registration->token !== $token) {
|
| 362 |
throw new moodle_exception('wrongtoken', 'hub', new moodle_url('/admin/registration/index.php'));
|
394 |
throw new moodle_exception('wrongtoken', 'hub', new moodle_url('/admin/registration/index.php'));
|
| - |
|
395 |
}
|
| - |
|
396 |
|
| 363 |
}
|
397 |
// Update hub information of the site.
|
| 364 |
$record = ['id' => $registration->id];
|
398 |
$record = ['id' => $registration->id];
|
| 365 |
$record['token'] = $newtoken;
|
399 |
$record['token'] = $newtoken;
|
| 366 |
$record['confirmed'] = 1;
|
400 |
$record['confirmed'] = 1;
|
| 367 |
$record['hubname'] = $hubname;
|
401 |
$record['hubname'] = $hubname;
|
| Línea 396... |
Línea 430... |
| 396 |
self::HUB_SITELINKPUBLISHED => get_string('siteprivacylinked', 'hub')
|
430 |
self::HUB_SITELINKPUBLISHED => get_string('siteprivacylinked', 'hub')
|
| 397 |
];
|
431 |
];
|
| 398 |
}
|
432 |
}
|
| Línea 399... |
Línea 433... |
| 399 |
|
433 |
|
| - |
|
434 |
/**
|
| - |
|
435 |
* Get the options for organisation type form element to use in registration form.
|
| - |
|
436 |
*
|
| - |
|
437 |
* Indexes reference Moodle internal ids and should not be changed.
|
| - |
|
438 |
*
|
| - |
|
439 |
* @return array
|
| - |
|
440 |
*/
|
| - |
|
441 |
public static function get_site_organisation_type_options(): array {
|
| - |
|
442 |
return [
|
| - |
|
443 |
1 => get_string('siteorganisationtype:wholeuniversity', 'hub'),
|
| - |
|
444 |
2 => get_string('siteorganisationtype:universitydepartment', 'hub'),
|
| - |
|
445 |
3 => get_string('siteorganisationtype:college', 'hub'),
|
| - |
|
446 |
4 => get_string('siteorganisationtype:collegedepartment', 'hub'),
|
| - |
|
447 |
5 => get_string('siteorganisationtype:highschool', 'hub'),
|
| - |
|
448 |
6 => get_string('siteorganisationtype:highschooldepartment', 'hub'),
|
| - |
|
449 |
7 => get_string('siteorganisationtype:primaryschool', 'hub'),
|
| - |
|
450 |
8 => get_string('siteorganisationtype:independentteacher', 'hub'),
|
| - |
|
451 |
9 => get_string('siteorganisationtype:companyinternal', 'hub'),
|
| - |
|
452 |
10 => get_string('siteorganisationtype:companydepartment', 'hub'),
|
| - |
|
453 |
11 => get_string('siteorganisationtype:commercialcourseprovider', 'hub'),
|
| - |
|
454 |
12 => get_string('siteorganisationtype:other', 'hub'),
|
| - |
|
455 |
13 => get_string('siteorganisationtype:highschooldistrict', 'hub'),
|
| - |
|
456 |
14 => get_string('siteorganisationtype:government', 'hub'),
|
| - |
|
457 |
16 => get_string('siteorganisationtype:charityornotforprofit', 'hub'),
|
| - |
|
458 |
17 => get_string('siteorganisationtype:charterschool', 'hub'),
|
| - |
|
459 |
18 => get_string('siteorganisationtype:schooldistrict', 'hub'),
|
| - |
|
460 |
19 => get_string('siteorganisationtype:hospital', 'hub'),
|
| - |
|
461 |
];
|
| - |
|
462 |
}
|
| - |
|
463 |
|
| 400 |
/**
|
464 |
/**
|
| 401 |
* Registers a site
|
465 |
* Registers a site
|
| 402 |
*
|
466 |
*
|
| 403 |
* This method will make sure that unconfirmed registration record is created and then redirect to
|
467 |
* This method will make sure that unconfirmed registration record is created and then redirect to
|
| 404 |
* registration script on the sites directory.
|
468 |
* registration script on the sites directory.
|
| Línea 409... |
Línea 473... |
| 409 |
* @throws \coding_exception
|
473 |
* @throws \coding_exception
|
| 410 |
*/
|
474 |
*/
|
| 411 |
public static function register($returnurl) {
|
475 |
public static function register($returnurl) {
|
| 412 |
global $DB, $SESSION;
|
476 |
global $DB, $SESSION;
|
| Línea -... |
Línea 477... |
| - |
|
477 |
|
| 413 |
|
478 |
// We should also check if the url is registered in the hub.
|
| 414 |
if (self::is_registered()) {
|
479 |
if (self::is_registered() && api::is_site_registered_in_hub()) {
|
| 415 |
// Caller of this method must make sure that site is not registered.
|
480 |
// Caller of this method must make sure that site is not registered.
|
| 416 |
throw new \coding_exception('Site already registered');
|
481 |
throw new \coding_exception('Site already registered');
|
| Línea -... |
Línea 482... |
| - |
|
482 |
}
|
| - |
|
483 |
|
| - |
|
484 |
// Delete 'confirmed' registrations.
|
| - |
|
485 |
$DB->delete_records('registration_hubs', ['confirmed' => 1]);
|
| 417 |
}
|
486 |
|
| 418 |
|
487 |
// Get 'unconfirmed' registration.
|
| 419 |
$hub = self::get_registration(false);
|
488 |
$hub = self::get_registration(false);
|
| 420 |
if (empty($hub)) {
|
489 |
if (empty($hub)) {
|
| - |
|
490 |
// Create a new record in 'registration_hubs'.
|
| 421 |
// Create a new record in 'registration_hubs'.
|
491 |
$hub = new stdClass();
|
| - |
|
492 |
// Let's add date('Ymdhis') to make the token unique.
|
| 422 |
$hub = new stdClass();
|
493 |
$hub->token = get_site_identifier() . date('Ymdhis');
|
| 423 |
$hub->token = get_site_identifier();
|
494 |
// Secret is identical to token until registration confirmed (confirmregistration.php).
|
| 424 |
$hub->secret = $hub->token;
|
495 |
$hub->secret = $hub->token;
|
| 425 |
$hub->huburl = HUB_MOODLEORGHUBURL;
|
496 |
$hub->huburl = HUB_MOODLEORGHUBURL;
|
| 426 |
$hub->hubname = 'moodle';
|
497 |
$hub->hubname = 'moodle';
|
| Línea 601... |
Línea 672... |
| 601 |
return;
|
672 |
return;
|
| 602 |
}
|
673 |
}
|
| 603 |
if (!has_capability('moodle/site:config', context_system::instance())) {
|
674 |
if (!has_capability('moodle/site:config', context_system::instance())) {
|
| 604 |
return;
|
675 |
return;
|
| 605 |
}
|
676 |
}
|
| - |
|
677 |
if (
|
| - |
|
678 |
site_is_public() &&
|
| 606 |
if (self::show_after_install() || self::get_new_registration_fields()) {
|
679 |
(self::show_after_install() || self::get_new_registration_fields())
|
| - |
|
680 |
) {
|
| 607 |
$returnurl = new moodle_url($url);
|
681 |
$returnurl = new moodle_url($url);
|
| 608 |
redirect(new moodle_url('/admin/registration/index.php', ['returnurl' => $returnurl->out_as_local_url(false)]));
|
682 |
redirect(new moodle_url('/admin/registration/index.php', ['returnurl' => $returnurl->out_as_local_url(false)]));
|
| 609 |
}
|
683 |
}
|
| 610 |
}
|
684 |
}
|
| - |
|
685 |
|
| - |
|
686 |
/**
|
| - |
|
687 |
* Return a list of plugins.
|
| - |
|
688 |
*
|
| - |
|
689 |
* Only blocks and activities will include instance counts.
|
| - |
|
690 |
*
|
| - |
|
691 |
* @return array
|
| - |
|
692 |
*/
|
| - |
|
693 |
public static function get_plugin_usage_data(): array {
|
| - |
|
694 |
global $DB;
|
| - |
|
695 |
|
| - |
|
696 |
$pluginman = core_plugin_manager::instance();
|
| - |
|
697 |
$plugininfo = $pluginman->get_plugins();
|
| - |
|
698 |
$data = [];
|
| - |
|
699 |
|
| - |
|
700 |
foreach ($plugininfo as $plugins) {
|
| - |
|
701 |
foreach ($plugins as $plugin) {
|
| - |
|
702 |
// Plugins are considered enabled if $plugin->is_enabled() returns true or null.
|
| - |
|
703 |
// Plugins that return null cannot be disabled.
|
| - |
|
704 |
$enabled = ($plugin->is_enabled() || is_null($plugin->is_enabled()));
|
| - |
|
705 |
$data[$plugin->type][$plugin->name]['enabled'] = $enabled ? 1 : 0;
|
| - |
|
706 |
|
| - |
|
707 |
if ($plugin->type === 'mod') {
|
| - |
|
708 |
$mid = $DB->get_field('modules', 'id', ['name' => $plugin->name]);
|
| - |
|
709 |
$count = $DB->count_records('course_modules', ['module' => $mid]);
|
| - |
|
710 |
$data[$plugin->type][$plugin->name]['count'] = $count;
|
| - |
|
711 |
|
| - |
|
712 |
} else if ($plugin->type === 'block') {
|
| - |
|
713 |
$count = $DB->count_records('block_instances', ['blockname' => $plugin->name]);
|
| - |
|
714 |
$data[$plugin->type][$plugin->name]['count'] = $count;
|
| - |
|
715 |
}
|
| - |
|
716 |
}
|
| - |
|
717 |
}
|
| - |
|
718 |
|
| - |
|
719 |
return $data;
|
| - |
|
720 |
}
|
| - |
|
721 |
|
| - |
|
722 |
/**
|
| - |
|
723 |
* Get the time range to use in collected and reporting AI usage data.
|
| - |
|
724 |
*
|
| - |
|
725 |
* @param bool $format Use true to format timestamp.
|
| - |
|
726 |
* @return array
|
| - |
|
727 |
*/
|
| - |
|
728 |
private static function get_ai_usage_time_range(bool $format = false): array {
|
| - |
|
729 |
global $DB;
|
| - |
|
730 |
|
| - |
|
731 |
// We will try and use the last time this site was last registered for our 'from' time.
|
| - |
|
732 |
// Otherwise, default to using one week's worth of data to roughly match the site rego scheduled task.
|
| - |
|
733 |
$timenow = \core\di::get(\core\clock::class)->time();
|
| - |
|
734 |
$defaultfrom = $timenow - WEEKSECS;
|
| - |
|
735 |
$timeto = $timenow;
|
| - |
|
736 |
$params = [
|
| - |
|
737 |
'huburl' => HUB_MOODLEORGHUBURL,
|
| - |
|
738 |
'confirmed' => 1,
|
| - |
|
739 |
];
|
| - |
|
740 |
$lastregistered = $DB->get_field('registration_hubs', 'timemodified', $params);
|
| - |
|
741 |
$timefrom = $lastregistered ? (int)$lastregistered : $defaultfrom;
|
| - |
|
742 |
|
| - |
|
743 |
if ($format) {
|
| - |
|
744 |
$timefrom = userdate($timefrom);
|
| - |
|
745 |
$timeto = userdate($timeto);
|
| - |
|
746 |
}
|
| - |
|
747 |
|
| - |
|
748 |
return [
|
| - |
|
749 |
'timefrom' => $timefrom,
|
| - |
|
750 |
'timeto' => $timeto,
|
| - |
|
751 |
];
|
| - |
|
752 |
}
|
| - |
|
753 |
|
| - |
|
754 |
/**
|
| - |
|
755 |
* Displays AI usage data for all providers.
|
| - |
|
756 |
*
|
| - |
|
757 |
* @return array Array containing usage data, grouped by provider
|
| - |
|
758 |
*/
|
| - |
|
759 |
public static function show_ai_usage(): array {
|
| - |
|
760 |
// Initialize aiusage collection.
|
| - |
|
761 |
$aiusage = [];
|
| - |
|
762 |
|
| - |
|
763 |
// Process each provider's data.
|
| - |
|
764 |
foreach (self::get_ai_usage_data() as $provider => $actions) {
|
| - |
|
765 |
if ($provider === 'time_range') {
|
| - |
|
766 |
$aiusage['timerange'] = [
|
| - |
|
767 |
'label' => get_string($provider, 'hub'),
|
| - |
|
768 |
'values' => self::format_ai_usage_actions($actions),
|
| - |
|
769 |
];
|
| - |
|
770 |
} else {
|
| - |
|
771 |
// Initialize provider data structure.
|
| - |
|
772 |
$aiusage['providers'][] = [
|
| - |
|
773 |
'providername' => get_string('pluginname', $provider),
|
| - |
|
774 |
'aiactions' => self::format_ai_usage_actions($actions),
|
| - |
|
775 |
];
|
| - |
|
776 |
}
|
| - |
|
777 |
}
|
| - |
|
778 |
|
| - |
|
779 |
return $aiusage;
|
| - |
|
780 |
}
|
| - |
|
781 |
|
| - |
|
782 |
/**
|
| - |
|
783 |
* Formats individual actions for a provider.
|
| - |
|
784 |
*
|
| - |
|
785 |
* @param array $actions Raw actions data
|
| - |
|
786 |
* @return array Formatted action data
|
| - |
|
787 |
*/
|
| - |
|
788 |
private static function format_ai_usage_actions(array $actions): array {
|
| - |
|
789 |
$formattedactions = [];
|
| - |
|
790 |
|
| - |
|
791 |
foreach ($actions as $action => $values) {
|
| - |
|
792 |
if (in_array($action, ['timefrom', 'timeto'])) {
|
| - |
|
793 |
$formattedactions[] = get_string($action, 'hub', userdate($values));
|
| - |
|
794 |
} else {
|
| - |
|
795 |
$formattedactions[] = [
|
| - |
|
796 |
'actionname' => get_string("action_$action", 'core_ai'),
|
| - |
|
797 |
'aiactionvalues' => self::format_ai_usage_action_values($values),
|
| - |
|
798 |
];
|
| - |
|
799 |
}
|
| - |
|
800 |
}
|
| - |
|
801 |
|
| - |
|
802 |
return $formattedactions;
|
| - |
|
803 |
}
|
| - |
|
804 |
|
| - |
|
805 |
/**
|
| - |
|
806 |
* Formats action values into formatted strings.
|
| - |
|
807 |
*
|
| - |
|
808 |
* @param array $values Action values to format
|
| - |
|
809 |
* @return array Formatted action values
|
| - |
|
810 |
*/
|
| - |
|
811 |
private static function format_ai_usage_action_values(array $values): array {
|
| - |
|
812 |
$formattedvalues = [];
|
| - |
|
813 |
|
| - |
|
814 |
foreach ($values as $key => $value) {
|
| - |
|
815 |
if (get_string_manager()->string_exists($key, 'hub')) {
|
| - |
|
816 |
if ($key === 'models') {
|
| - |
|
817 |
$formattedvalues[] = [
|
| - |
|
818 |
'label' => get_string($key, 'hub'),
|
| - |
|
819 |
'models' => self::format_ai_usage_model_values($value),
|
| - |
|
820 |
];
|
| - |
|
821 |
} else {
|
| - |
|
822 |
$formattedvalues[]['values'] = get_string($key, 'hub', $value);
|
| - |
|
823 |
}
|
| - |
|
824 |
}
|
| - |
|
825 |
}
|
| - |
|
826 |
|
| - |
|
827 |
return $formattedvalues;
|
| - |
|
828 |
}
|
| - |
|
829 |
|
| - |
|
830 |
/**
|
| - |
|
831 |
* Formats model values with their counts.
|
| - |
|
832 |
*
|
| - |
|
833 |
* @param array $values Formatted model values
|
| - |
|
834 |
*/
|
| - |
|
835 |
private static function format_ai_usage_model_values(array $values): array {
|
| - |
|
836 |
$modelvalues = [];
|
| - |
|
837 |
|
| - |
|
838 |
foreach ($values as $model => $count) {
|
| - |
|
839 |
$modelvalues[] = "$model ($count[count])";
|
| - |
|
840 |
}
|
| - |
|
841 |
|
| - |
|
842 |
return $modelvalues;
|
| - |
|
843 |
}
|
| - |
|
844 |
|
| - |
|
845 |
/**
|
| - |
|
846 |
* Get AI usage data.
|
| - |
|
847 |
*
|
| - |
|
848 |
* @return array
|
| - |
|
849 |
*/
|
| - |
|
850 |
public static function get_ai_usage_data(): array {
|
| - |
|
851 |
global $DB;
|
| - |
|
852 |
|
| - |
|
853 |
$params = self::get_ai_usage_time_range();
|
| - |
|
854 |
|
| - |
|
855 |
$sql = "SELECT aar.*
|
| - |
|
856 |
FROM {ai_action_register} aar
|
| - |
|
857 |
WHERE aar.timecompleted >= :timefrom
|
| - |
|
858 |
AND aar.timecompleted <= :timeto";
|
| - |
|
859 |
|
| - |
|
860 |
$actions = $DB->get_records_sql($sql, $params);
|
| - |
|
861 |
|
| - |
|
862 |
// Build data for site info reporting.
|
| - |
|
863 |
$data = [];
|
| - |
|
864 |
|
| - |
|
865 |
foreach ($actions as $action) {
|
| - |
|
866 |
$provider = $action->provider;
|
| - |
|
867 |
$actionname = $action->actionname;
|
| - |
|
868 |
|
| - |
|
869 |
// Initialise data structure.
|
| - |
|
870 |
if (!isset($data[$provider][$actionname])) {
|
| - |
|
871 |
$data[$provider][$actionname] = [
|
| - |
|
872 |
'success_count' => 0,
|
| - |
|
873 |
'fail_count' => 0,
|
| - |
|
874 |
'times' => [],
|
| - |
|
875 |
'errors' => [],
|
| - |
|
876 |
'modelstemp' => [],
|
| - |
|
877 |
];
|
| - |
|
878 |
}
|
| - |
|
879 |
|
| - |
|
880 |
if ($action->success === '1') {
|
| - |
|
881 |
$data[$provider][$actionname]['success_count'] += 1;
|
| - |
|
882 |
// Collect AI processing times for averaging.
|
| - |
|
883 |
$data[$provider][$actionname]['times'][] = (int)$action->timecompleted - (int)$action->timecreated;
|
| - |
|
884 |
|
| - |
|
885 |
} else {
|
| - |
|
886 |
$data[$provider][$actionname]['fail_count'] += 1;
|
| - |
|
887 |
// Collect errors for determing the predominant one.
|
| - |
|
888 |
$data[$provider][$actionname]['errors'][] = $action->errorcode;
|
| - |
|
889 |
}
|
| - |
|
890 |
|
| - |
|
891 |
// Collect models used and identify unknown ones.
|
| - |
|
892 |
$model = $action->model ?? 'unknown';
|
| - |
|
893 |
$data[$provider][$actionname]['modelstemp'][] = $model;
|
| - |
|
894 |
}
|
| - |
|
895 |
|
| - |
|
896 |
// Parse the errors, average the times, count the models and then add them to the data.
|
| - |
|
897 |
foreach ($data as $p => $provider) {
|
| - |
|
898 |
foreach ($provider as $a => $actionname) {
|
| - |
|
899 |
if (isset($data[$p][$a]['errors'])) {
|
| - |
|
900 |
// Create an array with the error codes counted.
|
| - |
|
901 |
$errors = array_count_values($data[$p][$a]['errors']);
|
| - |
|
902 |
if (!empty($errors)) {
|
| - |
|
903 |
// Sort values descending and convert to an array of error codes (most predominant will be at start).
|
| - |
|
904 |
arsort($errors);
|
| - |
|
905 |
$errors = array_keys($errors);
|
| - |
|
906 |
$data[$p][$a]['predominant_error'] = $errors[0];
|
| - |
|
907 |
}
|
| - |
|
908 |
unset($data[$p][$a]['errors']);
|
| - |
|
909 |
}
|
| - |
|
910 |
|
| - |
|
911 |
if (isset($data[$p][$a]['times'])) {
|
| - |
|
912 |
$count = count($data[$p][$a]['times']);
|
| - |
|
913 |
if ($count > 0) {
|
| - |
|
914 |
// Average the time to perform the action (seconds).
|
| - |
|
915 |
$totaltime = array_sum($data[$p][$a]['times']);
|
| - |
|
916 |
$data[$p][$a]['average_time'] = round($totaltime / $count);
|
| - |
|
917 |
|
| - |
|
918 |
}
|
| - |
|
919 |
}
|
| - |
|
920 |
unset($data[$p][$a]['times']);
|
| - |
|
921 |
|
| - |
|
922 |
if (isset($data[$p][$a]['modelstemp'])) {
|
| - |
|
923 |
// Create an array with the models counted.
|
| - |
|
924 |
$countedmodels = array_count_values($data[$p][$a]['modelstemp']);
|
| - |
|
925 |
foreach ($countedmodels as $model => $count) {
|
| - |
|
926 |
$data[$p][$a]['models'][$model]['count'] = $count;
|
| - |
|
927 |
}
|
| - |
|
928 |
}
|
| - |
|
929 |
unset($data[$p][$a]['modelstemp']);
|
| - |
|
930 |
}
|
| - |
|
931 |
}
|
| - |
|
932 |
|
| - |
|
933 |
// Include the time range used to help interpret the data.
|
| - |
|
934 |
if (!empty($data)) {
|
| - |
|
935 |
$data['time_range'] = $params;
|
| - |
|
936 |
}
|
| - |
|
937 |
|
| - |
|
938 |
return $data;
|
| - |
|
939 |
}
|
| 611 |
}
|
940 |
}
|