Skip to content

Commit f50ebaa

Browse files
author
dustinhuynh
committed
New metric for rolling 12 months active users #180
1 parent 29c3e43 commit f50ebaa

File tree

5 files changed

+179
-9
lines changed

5 files changed

+179
-9
lines changed

classes/metric/manager.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public static function get_frequency_labels(): array {
8484
'newusers' => self::FREQ_DAY,
8585
'onlineusers' => self::FREQ_5MIN,
8686
'dailyusers' => self::FREQ_DAY,
87+
'yearlyactiveusers' => self::FREQ_DAY,
8788
];
8889

8990
/**
@@ -99,6 +100,7 @@ public static function get_metrics(bool $enabledonly = true): array {
99100
'newusers' => new new_users_metric(),
100101
'onlineusers' => new online_users_metric(),
101102
'dailyusers' => new daily_users_metric(),
103+
'yearlyactiveusers' => new yearly_active_users_metric(),
102104
];
103105

104106
// Find metrics from plugins.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace tool_cloudmetrics\metric;
18+
19+
/**
20+
* Metric class for 12 months yearly active users.
21+
*
22+
* @package tool_cloudmetrics
23+
* @author Dustin Huynh <dustinhuynh@catalyst-au.net>
24+
* @copyright 2025, Catalyst IT
25+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26+
*/
27+
class yearly_active_users_metric extends builtin_user_base {
28+
29+
/**
30+
* The metric's name.
31+
*
32+
* @return string
33+
*/
34+
public function get_name(): string {
35+
return 'yearlyactiveusers';
36+
}
37+
38+
/**
39+
* Unique colour to represent the metric
40+
*
41+
* @return string - The colour in RGB hex.
42+
*/
43+
public function get_colour(): string {
44+
return '#FFA500'; // Orange.
45+
}
46+
47+
/**
48+
* Returns true if frequency cannot be changed.
49+
*
50+
* @return bool
51+
*/
52+
public function is_frequency_fixed(): bool {
53+
return true;
54+
}
55+
56+
/**
57+
* The frequency of the metric's sampling.
58+
*
59+
* @return int
60+
*/
61+
public function get_frequency(): int {
62+
// Fixed at one day.
63+
return $this->get_frequency_default();
64+
}
65+
66+
/**
67+
* Set frequency of the metric's sampling.
68+
*
69+
* @param int $freq
70+
*/
71+
public function set_frequency(int $freq) {
72+
// Do nothing.
73+
}
74+
75+
/**
76+
* Generates the metric items from the source data.
77+
*
78+
* Uses $starttime to $finishtime to draw from the source data.
79+
*
80+
* @param int $starttime
81+
* @param int $finishtime
82+
* @return metric_item
83+
*/
84+
public function generate_metric_item(int $starttime, int $finishtime): metric_item {
85+
global $DB;
86+
$lastyear = $finishtime - (60 * 60 * 24 * 365);
87+
$users = $DB->count_records_select(
88+
'user',
89+
'confirmed = 1 AND (lastlogin > ? OR currentlogin > ?)',
90+
[$lastyear, $lastyear]
91+
);
92+
return new metric_item($this->get_name(), $finishtime, $users, $this);
93+
}
94+
}

lang/en/tool_cloudmetrics.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
$string['newusers_desc'] = 'Users who have signed up recently.';
7676
$string['dailyusers'] = 'Daily users';
7777
$string['dailyusers_desc'] = 'Unique users over a day (fixed frequency).';
78+
$string['yearlyactiveusers'] = 'Yearly active users';
79+
$string['yearlyactiveusers_desc'] = 'Users that have been active within the past 365 days from today.';
7880

7981
// User selection labels.
8082
$string['data_empty'] = 'Your database for this metric is empty.';

tests/tool_cloudmetrics_collect_metrics_test.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class helper_collect_metrics_task extends collect_metrics_task {
6161
/** @var mock_receiver Mock receiver */
6262
private $mock;
6363

64+
/**
65+
* @var object mock variable
66+
*/
67+
public $mock;
68+
6469
/**
6570
* Constructer for helper_collect_metrics_task
6671
*
@@ -112,7 +117,7 @@ protected function setUp(): void {
112117
* @covers \tool_cloudmetrics\task\collect_metrics_task
113118
* @throws Exception
114119
*/
115-
public function test_execute(string $timestr, array $meta, array $expected) {
120+
public function test_execute(string $timestr, array $meta, array $expected): void {
116121
$tz = \core_date::get_server_timezone_object();
117122
$this->disable_metrics($expected);
118123
foreach ($meta as $metric => $data) {
@@ -169,6 +174,7 @@ public static function execute_provider(): array {
169174
'online_users_metric' => [manager::FREQ_5MIN, 'midnight +75 minutes'],
170175
'active_users_metric' => [manager::FREQ_HOUR, 'midnight'],
171176
'daily_users_metric' => [manager::FREQ_DAY, 'midnight'],
177+
'yearly_active_users_metric' => [manager::FREQ_DAY, 'midnight'],
172178
],
173179
['activeusers', 'newusers'],
174180
],
@@ -179,6 +185,7 @@ public static function execute_provider(): array {
179185
'online_users_metric' => [manager::FREQ_DAY, '2020-03-01T00:00:00'],
180186
'active_users_metric' => [manager::FREQ_MONTH, '2020-03-01T00:00:00'],
181187
'daily_users_metric' => [manager::FREQ_DAY, '2020-03-01T00:00:00'],
188+
'yearly_active_users_metric' => [manager::FREQ_DAY, '2020-03-01T00:00:00'],
182189
],
183190
[],
184191
],
@@ -189,8 +196,9 @@ public static function execute_provider(): array {
189196
'online_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
190197
'active_users_metric' => [manager::FREQ_MONTH, '2020-02-01T00:00:00'],
191198
'daily_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
199+
'yearly_active_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
192200
],
193-
['activeusers', 'dailyusers', 'newusers', 'onlineusers'],
201+
['activeusers', 'dailyusers', 'newusers', 'onlineusers', 'yearlyactiveusers'],
194202
],
195203
[
196204
'2020-02-02T00:02:00',
@@ -199,13 +207,14 @@ public static function execute_provider(): array {
199207
'online_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
200208
'active_users_metric' => [manager::FREQ_MONTH, '2020-02-01T00:00:00'],
201209
'daily_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
210+
'yearly_active_users_metric' => [manager::FREQ_DAY, '2020-02-01T00:00:00'],
202211
],
203-
['dailyusers', 'newusers', 'onlineusers'],
212+
['dailyusers', 'newusers', 'onlineusers', 'yearlyactiveusers'],
204213
],
205214
[
206215
'2020-02-02T00:03:00',
207216
['new_users_metric' => [manager::FREQ_5MIN, null]],
208-
['activeusers', 'dailyusers', 'newusers', 'onlineusers'],
217+
['activeusers', 'dailyusers', 'newusers', 'onlineusers', 'yearlyactiveusers'],
209218
],
210219
];
211220
}

tests/tool_cloudmetrics_users_test.php

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use tool_cloudmetrics\metric\new_users_metric;
2121
use tool_cloudmetrics\metric\active_users_metric;
2222
use tool_cloudmetrics\metric\online_users_metric;
23+
use tool_cloudmetrics\metric\yearly_active_users_metric;
2324

2425
/**
2526
* Unit tests to test the builtin user metric types.
@@ -29,7 +30,7 @@
2930
* @copyright 2022, Catalyst IT
3031
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3132
*/
32-
class tool_cloudmetrics_users_test extends \advanced_testcase {
33+
final class tool_cloudmetrics_users_test extends \advanced_testcase {
3334

3435
/**
3536
* Set up before each test
@@ -38,6 +39,8 @@ protected function setUp(): void {
3839
parent::setUp();
3940
$this->resetAfterTest();
4041
}
42+
/** @var integer Seconds in a day. */
43+
public const SECOND_IN_DAY = 86400;
4144

4245
/** @var array[] Sample user DB data to be used in tests. */
4346
public const USER_DATA = [
@@ -53,6 +56,20 @@ protected function setUp(): void {
5356
['username' => 'j', 'firstaccess' => 13000, 'lastaccess' => 12950, 'lastlogin' => 12500],
5457
];
5558

59+
/** @var array[] Sample active user DB data to be used in tests. */
60+
public const ACTIVE_USER_DATA = [
61+
['username' => 'a', 'confirmed' => 1 , 'lastlogin' => self::SECOND_IN_DAY + 1000],
62+
['username' => 'b', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 2) + 1000],
63+
['username' => 'c', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 2) + 2000],
64+
['username' => 'd', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 2) + 3000],
65+
['username' => 'e', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 3) + 1000],
66+
['username' => 'f', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 3) + 2000],
67+
['username' => 'g', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 4) + 1000],
68+
['username' => 'h', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 4) + 2000],
69+
['username' => 'i', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 5) + 1000],
70+
['username' => 'j', 'confirmed' => 1 , 'lastlogin' => (self::SECOND_IN_DAY * 5) + 2000],
71+
];
72+
5673
/**
5774
* Tests generate_metric_items() for the builtin user metrics.
5875
*
@@ -62,7 +79,7 @@ protected function setUp(): void {
6279
* @param array $expected List of metric items that expect to be generated.
6380
* @throws \dml_exception
6481
*/
65-
public function test_generate_metrics(string $metricname, int $frequency, array $expected) {
82+
public function test_generate_metrics(string $metricname, int $frequency, array $expected): void {
6683
global $DB;
6784

6885
foreach (self::USER_DATA as $row) {
@@ -85,6 +102,33 @@ public function test_generate_metrics(string $metricname, int $frequency, array
85102
$this->assertEquals($expected, $items);
86103
}
87104

105+
106+
/**
107+
* Tests test_generate_yearly_active_users_metric() for the builtin user metrics.
108+
*
109+
* @dataProvider data_for_test_generate_yearly_active_users_metric
110+
* @param string $metricname The name of the metric to be tested.
111+
* @param array $expected List of metric items that expect to be generated.
112+
* @throws \dml_exception
113+
*/
114+
public function test_generate_yearly_active_users_metric(string $metricname, array $expected): void {
115+
global $DB;
116+
117+
foreach (self::ACTIVE_USER_DATA as $row) {
118+
$DB->insert_record('user', (object) $row);
119+
}
120+
$time = self::SECOND_IN_DAY * 366;
121+
$endtime = $time + (4 * lib::FREQ_TIMES[metric\manager::FREQ_DAY]);
122+
$metrictypes = metric\manager::get_metrics(false);
123+
$metric = $metrictypes[$metricname];
124+
$this->assertEquals($metricname, $metric->get_name());
125+
while ($time <= $endtime) {
126+
$items[] = $metric->generate_metric_item(0, $time);
127+
$time = lib::get_next_time($time, metric\manager::FREQ_DAY);
128+
}
129+
$this->assertEquals($expected, $items);
130+
131+
}
88132
/**
89133
* Data provider for test_generate_metrics.
90134
*
@@ -101,24 +145,43 @@ public function data_for_test_generate_metrics(): array {
101145
new metric_item('newusers', 12400, 0, $newusersmetric),
102146
new metric_item('newusers', 12700, 3, $newusersmetric),
103147
new metric_item('newusers', 13000, 2, $newusersmetric),
104-
]
148+
],
105149
],
106150
[ 'activeusers', metric\manager::FREQ_MIN, [
107151
new metric_item('activeusers', 12760, 2, $activeusersmetric),
108152
new metric_item('activeusers', 12820, 3, $activeusersmetric),
109153
new metric_item('activeusers', 12880, 3, $activeusersmetric),
110154
new metric_item('activeusers', 12940, 5, $activeusersmetric),
111155
new metric_item('activeusers', 13000, 6, $activeusersmetric),
112-
]
156+
],
113157
],
114158
[ 'onlineusers', metric\manager::FREQ_15MIN, [
115159
new metric_item('onlineusers', 9400, 0, $onlineusersmetric),
116160
new metric_item('onlineusers', 10300, 1, $onlineusersmetric),
117161
new metric_item('onlineusers', 11200, 1, $onlineusersmetric),
118162
new metric_item('onlineusers', 12100, 0, $onlineusersmetric),
119163
new metric_item('onlineusers', 13000, 2, $onlineusersmetric),
120-
]
164+
],
121165
],
122166
];
123167
}
168+
169+
/**
170+
* Data provider for test_generate_yearly_active_users_metric.
171+
*
172+
* @return array[]
173+
*/
174+
public function data_for_test_generate_yearly_active_users_metric(): array {
175+
$yearlyactiveusers = new yearly_active_users_metric();
176+
177+
return [
178+
['yearlyactiveusers', [
179+
new metric_item('yearlyactiveusers', self::SECOND_IN_DAY * 366 , 10, $yearlyactiveusers),
180+
new metric_item('yearlyactiveusers', self::SECOND_IN_DAY * 367, 9, $yearlyactiveusers),
181+
new metric_item('yearlyactiveusers', self::SECOND_IN_DAY * 368, 6, $yearlyactiveusers),
182+
new metric_item('yearlyactiveusers', self::SECOND_IN_DAY * 369, 4, $yearlyactiveusers),
183+
new metric_item('yearlyactiveusers', self::SECOND_IN_DAY * 370, 2, $yearlyactiveusers)],
184+
],
185+
];
186+
}
124187
}

0 commit comments

Comments
 (0)