Merge branch 'MDL-84296-500' of https://github.com/davewoloszyn/moodle into MOODLE_500_STABLE
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
$string['activeparticipantnumberaverage'] = 'Average number of recently active participants ({$a})';
|
||||
$string['activeusersnumber'] = 'Number of recently active users ({$a})';
|
||||
$string['aiusagedata'] = 'AI usage data';
|
||||
$string['aiusagestats'] = 'AI usage stats ({$a->timefrom} - {$a->timeto})';
|
||||
$string['analyticsactions'] = 'Number of actions taken on generated predictions ({$a})';
|
||||
$string['analyticsactionsnotuseful'] = 'Number of actions marking a prediction as not useful ({$a})';
|
||||
@@ -34,6 +35,7 @@ $string['audience_help'] = 'Select the intended audience for the course.';
|
||||
$string['audienceeducators'] = 'Educators';
|
||||
$string['audiencestudents'] = 'Students';
|
||||
$string['audienceadmins'] = 'Moodle administrators';
|
||||
$string['average_time'] = 'Average processing time ({$a})';
|
||||
$string['badgesnumber'] = 'Number of badges ({$a})';
|
||||
$string['communityremoved'] = 'That course link has been removed from your list';
|
||||
$string['confirmregistration'] = 'Confirm registration';
|
||||
@@ -66,6 +68,7 @@ $string['errorwstokenreset'] = '{$a}. Registration token on this site has been r
|
||||
$string['errorregistrationupdate'] = 'An error occurred during registration update ({$a})';
|
||||
$string['experttipsandinsights'] = 'Expert tips and insights';
|
||||
$string['experttipsandinsightsdesc'] = 'Yes, sign me up for exclusive tips, strategies, and insights from Moodle experts and Certified Partners. Update your preferences or unsubscribe any time, as covered in our <a href="{$a}">Privacy Notice</a>.';
|
||||
$string['fail_count'] = 'Fail count ({$a})';
|
||||
$string['geolocation'] = 'Geolocation';
|
||||
$string['geolocation_help'] = 'In future we may provide location-based searching. If you want to specify the location for your course use a latitude/longitude value here (eg: -31.947884,115.871285). One way to find this is to use Google Maps.';
|
||||
$string['imageurl'] = 'Image URL';
|
||||
@@ -80,6 +83,7 @@ $string['licence_link'] = 'licenses';
|
||||
$string['logourl'] = 'Logo URL';
|
||||
$string['mobilenotificationsenabled'] = 'Mobile notifications enabled ({$a})';
|
||||
$string['mobileservicesenabled'] = 'Mobile services enabled ({$a})';
|
||||
$string['models'] = 'Models';
|
||||
$string['modulenumberaverage'] = 'Average number of course modules ({$a})';
|
||||
$string['mustselectsubject'] = 'You must select a subject';
|
||||
$string['name'] = 'Name';
|
||||
@@ -87,6 +91,7 @@ $string['name_help'] = 'This name will be showing in the course listing.';
|
||||
$string['neverchecked'] = 'Never checked';
|
||||
$string['next'] = 'Next';
|
||||
$string['no'] = 'No';
|
||||
$string['noaiusagedata'] = 'AI usage data (none)';
|
||||
$string['nocheckstatusfromunreghub'] = 'The site is not registered on the hub so the status can not be checked.';
|
||||
$string['nohubselected'] = 'No hub selected';
|
||||
$string['none'] = 'None';
|
||||
@@ -98,6 +103,7 @@ $string['policyagreeddesc'] = 'I agree to the <a href="{$a}" target="_blank">Pri
|
||||
$string['postaladdress'] = 'Postal address';
|
||||
$string['postaladdress_help'] = 'Postal address of this site, or of the entity represented by this site.';
|
||||
$string['postsnumber'] = 'Number of posts ({$a})';
|
||||
$string['predominant_error'] = 'Predominant HTTP error ({$a})';
|
||||
$string['previousregistrationdeleted'] = 'The previous registration has been deleted from {$a}. You can restart the registration process. Thank you.';
|
||||
$string['primaryauthtype'] = 'Primary authentication type ({$a})';
|
||||
$string['questionsnumber'] = 'Number of questions ({$a})';
|
||||
@@ -184,8 +190,12 @@ $string['siteurl_help'] = 'The URL is the address of this site. If privacy sett
|
||||
$string['siteversion'] = 'Moodle version';
|
||||
$string['siteversion_help'] = 'The Moodle version of this site.';
|
||||
$string['skipregistration'] = 'Skip';
|
||||
$string['success_count'] = 'Success count ({$a})';
|
||||
$string['subject'] = 'Subject';
|
||||
$string['subject_help'] = 'Select the main subject area which the course covers.';
|
||||
$string['timefrom'] = 'Time from ({$a})';
|
||||
$string['timeto'] = 'Time to ({$a})';
|
||||
$string['time_range'] = 'Time range';
|
||||
$string['type'] = 'Shared';
|
||||
$string['unregister'] = 'Unregister';
|
||||
$string['unregistrationerror'] = 'An error occurred while attempting to unregister the site: {$a}';
|
||||
|
||||
@@ -239,6 +239,8 @@ class registration {
|
||||
* @return string
|
||||
*/
|
||||
public static function get_stats_summary($siteinfo) {
|
||||
global $OUTPUT;
|
||||
|
||||
$fieldsneedconfirm = self::get_new_registration_fields();
|
||||
$summary = html_writer::tag('p', get_string('sendfollowinginfo_help', 'hub')) .
|
||||
html_writer::start_tag('ul');
|
||||
@@ -284,7 +286,7 @@ class registration {
|
||||
'sitetheme' => get_string('sitetheme', 'hub', $siteinfo['sitetheme']),
|
||||
'primaryauthtype' => get_string('primaryauthtype', 'hub', $siteinfo['primaryauthtype']),
|
||||
'pluginusage' => get_string('pluginusagedata', 'hub', $pluginusagelinks),
|
||||
'aiusage' => get_string('aiusagestats', 'hub', self::get_ai_usage_time_range(true)),
|
||||
'aiusage' => $OUTPUT->render_from_template('core/ai_usage_data', ['aiusagedata' => self::show_ai_usage()]),
|
||||
];
|
||||
|
||||
foreach ($senddata as $key => $str) {
|
||||
@@ -725,6 +727,97 @@ class registration {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays AI usage data for all providers.
|
||||
*
|
||||
* @return array Array containing usage data, grouped by provider
|
||||
*/
|
||||
public static function show_ai_usage(): array {
|
||||
// Initialize aiusage collection.
|
||||
$aiusage = [];
|
||||
|
||||
// Process each provider's data.
|
||||
foreach (self::get_ai_usage_data() as $provider => $actions) {
|
||||
if ($provider === 'time_range') {
|
||||
$aiusage['timerange'] = [
|
||||
'label' => get_string($provider, 'hub'),
|
||||
'values' => self::format_ai_usage_actions($actions),
|
||||
];
|
||||
} else {
|
||||
// Initialize provider data structure.
|
||||
$aiusage['providers'][] = [
|
||||
'providername' => get_string('pluginname', $provider),
|
||||
'aiactions' => self::format_ai_usage_actions($actions),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $aiusage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats individual actions for a provider.
|
||||
*
|
||||
* @param array $actions Raw actions data
|
||||
* @return array Formatted action data
|
||||
*/
|
||||
private static function format_ai_usage_actions(array $actions): array {
|
||||
$formattedactions = [];
|
||||
|
||||
foreach ($actions as $action => $values) {
|
||||
if (in_array($action, ['timefrom', 'timeto'])) {
|
||||
$formattedactions[] = get_string($action, 'hub', userdate($values));
|
||||
} else {
|
||||
$formattedactions[] = [
|
||||
'actionname' => get_string("action_$action", 'core_ai'),
|
||||
'aiactionvalues' => self::format_ai_usage_action_values($values),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $formattedactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats action values into formatted strings.
|
||||
*
|
||||
* @param array $values Action values to format
|
||||
* @return array Formatted action values
|
||||
*/
|
||||
private static function format_ai_usage_action_values(array $values): array {
|
||||
$formattedvalues = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (get_string_manager()->string_exists($key, 'hub')) {
|
||||
if ($key === 'models') {
|
||||
$formattedvalues[] = [
|
||||
'label' => get_string($key, 'hub'),
|
||||
'models' => self::format_ai_usage_model_values($value),
|
||||
];
|
||||
} else {
|
||||
$formattedvalues[]['values'] = get_string($key, 'hub', $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $formattedvalues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats model values with their counts.
|
||||
*
|
||||
* @param array $values Formatted model values
|
||||
*/
|
||||
private static function format_ai_usage_model_values(array $values): array {
|
||||
$modelvalues = [];
|
||||
|
||||
foreach ($values as $model => $count) {
|
||||
$modelvalues[] = "$model ($count[count])";
|
||||
}
|
||||
|
||||
return $modelvalues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AI usage data.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core/ai_usage_data
|
||||
|
||||
Template for AI usage data.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"aiusagedata": [{
|
||||
"providers": [{
|
||||
"providerName": "AI provider name",
|
||||
"aiactions": [{
|
||||
"actionName": "AI action name",
|
||||
"aiactionvalues": [{
|
||||
"success_count": "1",
|
||||
"fail_count": "2",
|
||||
"average_time": "15",
|
||||
"predominant_error": "403",
|
||||
"models": [{
|
||||
"gpt-4o": "1"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
"timerange": [{
|
||||
"label": "Time range for stats",
|
||||
"values": [{
|
||||
"timefrom": "Time from: Friday, 2 May 2025, 1:22 AM",
|
||||
"timeto": "Time from: Friday, 3 May 2025, 1:22 AM"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}}
|
||||
|
||||
{{#aiusagedata}}
|
||||
{{#str}} aiusagedata, hub {{/str}}:
|
||||
<ul>
|
||||
{{! AI providers. }}
|
||||
{{#providers}}
|
||||
<li>{{providername}}:</li>
|
||||
{{#aiactions}}
|
||||
<ul>
|
||||
<li>{{actionname}}:</li>
|
||||
{{#aiactionvalues}}
|
||||
<ul>
|
||||
{{#values}}
|
||||
<li>{{values}}</li>
|
||||
{{/values}}
|
||||
|
||||
{{^values}}
|
||||
<li>{{label}}:</li>
|
||||
{{#models}}
|
||||
<ul>
|
||||
<li>{{.}}</li>
|
||||
</ul>
|
||||
{{/models}}
|
||||
{{/values}}
|
||||
</ul>
|
||||
{{/aiactionvalues}}
|
||||
</ul>
|
||||
{{/aiactions}}
|
||||
{{/providers}}
|
||||
|
||||
{{! Time range for stats. }}
|
||||
{{#timerange}}
|
||||
<li>{{label}}:</li>
|
||||
{{#values}}
|
||||
<ul>
|
||||
<li>{{.}}</li>
|
||||
</ul>
|
||||
{{/values}}
|
||||
{{/timerange}}
|
||||
</ul>
|
||||
{{/aiusagedata}}
|
||||
{{^aiusagedata}}
|
||||
{{#str}} noaiusagedata, hub {{/str}}
|
||||
{{/aiusagedata}}
|
||||
@@ -90,16 +90,44 @@ final class registration_test extends \advanced_testcase {
|
||||
* Test the AI usage data is calculated correctly.
|
||||
*/
|
||||
public function test_get_ai_usage(): void {
|
||||
global $CFG, $DB;
|
||||
$this->resetAfterTest();
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
$clock = $this->mock_clock_with_frozen(1700000000);
|
||||
$this->generate_ai_usage_data();
|
||||
|
||||
// Get our site info and check the expected calculations are correct.
|
||||
$siteinfo = registration::get_site_info();
|
||||
$aisuage = json_decode($siteinfo['aiusage']);
|
||||
// Check generated text.
|
||||
$this->assertEquals(1, $aisuage->aiprovider_openai->generate_text->success_count);
|
||||
$this->assertEquals(0, $aisuage->aiprovider_openai->generate_text->fail_count);
|
||||
// Check generated images.
|
||||
$this->assertEquals(2, $aisuage->aiprovider_openai->generate_image->success_count);
|
||||
$this->assertEquals(3, $aisuage->aiprovider_openai->generate_image->fail_count);
|
||||
$this->assertEquals(15, $aisuage->aiprovider_openai->generate_image->average_time);
|
||||
$this->assertEquals(403, $aisuage->aiprovider_openai->generate_image->predominant_error);
|
||||
// Check time range is set correctly.
|
||||
$this->assertEquals($clock->time() - WEEKSECS, $aisuage->time_range->timefrom);
|
||||
$this->assertEquals($clock->time(), $aisuage->time_range->timeto);
|
||||
// Check model counts.
|
||||
$gpt4omodel = 'gpt-4o';
|
||||
$dalle3model = 'dall-e-3';
|
||||
$this->assertEquals(1, $aisuage->aiprovider_openai->generate_text->models->{$gpt4omodel}->count);
|
||||
$this->assertEquals(2, $aisuage->aiprovider_openai->generate_image->models->{$dalle3model}->count);
|
||||
$this->assertEquals(3, $aisuage->aiprovider_openai->generate_image->models->unknown->count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create some dummy AI usage data.
|
||||
*/
|
||||
private function generate_ai_usage_data(): void {
|
||||
global $DB;
|
||||
|
||||
$clock = $this->mock_clock_with_frozen(1700000000);
|
||||
|
||||
// Record some generated text.
|
||||
$record = new \stdClass();
|
||||
$record->provider = 'openai';
|
||||
$record->provider = 'aiprovider_openai';
|
||||
$record->actionname = 'generate_text';
|
||||
$record->actionid = 1;
|
||||
$record->userid = 1;
|
||||
@@ -107,14 +135,14 @@ final class registration_test extends \advanced_testcase {
|
||||
$record->success = true;
|
||||
$record->timecreated = $clock->time() - 5;
|
||||
$record->timecompleted = $clock->time();
|
||||
$record->model = $gpt4omodel;
|
||||
$record->model = 'gpt-4o';
|
||||
$DB->insert_record('ai_action_register', $record);
|
||||
|
||||
// Record a generated image.
|
||||
$record->actionname = 'generate_image';
|
||||
$record->actionid = 111;
|
||||
$record->timecreated = $clock->time() - 20;
|
||||
$record->model = $dalle3model;
|
||||
$record->model = 'dall-e-3';
|
||||
$DB->insert_record('ai_action_register', $record);
|
||||
// Record another image.
|
||||
$record->actionid = 222;
|
||||
@@ -134,24 +162,38 @@ final class registration_test extends \advanced_testcase {
|
||||
$record->actionid = 6;
|
||||
$record->errorcode = 404;
|
||||
$DB->insert_record('ai_action_register', $record);
|
||||
}
|
||||
|
||||
// Get our site info and check the expected calculations are correct.
|
||||
$siteinfo = registration::get_site_info();
|
||||
$aisuage = json_decode($siteinfo['aiusage']);
|
||||
// Check generated text.
|
||||
$this->assertEquals(1, $aisuage->openai->generate_text->success_count);
|
||||
$this->assertEquals(0, $aisuage->openai->generate_text->fail_count);
|
||||
// Check generated images.
|
||||
$this->assertEquals(2, $aisuage->openai->generate_image->success_count);
|
||||
$this->assertEquals(3, $aisuage->openai->generate_image->fail_count);
|
||||
$this->assertEquals(15, $aisuage->openai->generate_image->average_time);
|
||||
$this->assertEquals(403, $aisuage->openai->generate_image->predominant_error);
|
||||
// Check time range is set correctly.
|
||||
$this->assertEquals($clock->time() - WEEKSECS, $aisuage->time_range->timefrom);
|
||||
$this->assertEquals($clock->time(), $aisuage->time_range->timeto);
|
||||
// Check model counts.
|
||||
$this->assertEquals(1, $aisuage->openai->generate_text->models->{$gpt4omodel}->count);
|
||||
$this->assertEquals(2, $aisuage->openai->generate_image->models->{$dalle3model}->count);
|
||||
$this->assertEquals(3, $aisuage->openai->generate_image->models->unknown->count);
|
||||
/**
|
||||
* Test the show AI usage data.
|
||||
*/
|
||||
public function test_show_ai_usage(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Init the registration class.
|
||||
$registration = new registration();
|
||||
|
||||
// There should be no data to show yet.
|
||||
$aisuagedata = $registration->show_ai_usage();
|
||||
$this->assertTrue(empty($aisuagedata));
|
||||
|
||||
// After generating some data, there should now be some data to show.
|
||||
$this->generate_ai_usage_data();
|
||||
$aisuagedata = $registration->show_ai_usage();
|
||||
$this->assertTrue(!empty($aisuagedata));
|
||||
|
||||
foreach ($aisuagedata['providers'] as $provider) {
|
||||
$this->assertEquals('OpenAI API provider', $provider['providername']);
|
||||
$this->assertTrue(!empty($provider['aiactions']));
|
||||
|
||||
foreach ($provider['aiactions'] as $action) {
|
||||
$actionname = $action['actionname'];
|
||||
$this->assertTrue(!empty($actionname));
|
||||
}
|
||||
}
|
||||
|
||||
$timerange = $aisuagedata['timerange'];
|
||||
$this->assertEquals(get_string('time_range', 'hub'), $timerange['label']);
|
||||
$this->assertTrue(!empty($timerange['values']));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user