Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
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
}