Skip to content

Commit aa11da4

Browse files
committed
issue #108: add realtime processing per rule
1 parent 6b8570d commit aa11da4

14 files changed

+154
-27
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ Rules can be processed by two mechanisms:
8787

8888
### Disabling realtime processing (processing on event)
8989

90-
There is a global admin setting that allows administrator to enable or disable realtime rule processing. This may be useful if processing taking too long and blocking the user interface.
90+
Each rule can be configured to be processed realtime (if any of the related conditions support processing on event).
91+
92+
There is also a global admin setting that allows administrator to enable or disable realtime rule processing globally overriding per rule configuration.
9193

9294
# Configuration
9395

classes/observer.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public static function process_event(base $event): void {
3737
if (get_config('tool_dynamic_cohorts', 'realtime')) {
3838
foreach (condition_manager::get_conditions_with_event($event) as $condition) {
3939
foreach (rule_manager::get_rules_with_condition($condition) as $rule) {
40-
rule_manager::process_rule($rule, self::get_userid_from_event($event));
40+
if ($rule->is_realtime()) {
41+
rule_manager::process_rule($rule, self::get_userid_from_event($event));
42+
}
4143
}
4244
}
4345
}

classes/reportbuilder/local/entities/rule_entity.php

+25
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public function initialise(): base {
6767
*/
6868
protected function get_all_columns(): array {
6969
$alias = $this->get_table_alias('tool_dynamic_cohorts');
70+
$globalrealtime = get_config('tool_dynamic_cohorts', 'realtime');
7071

7172
$columns[] = (new column(
7273
'id',
@@ -117,6 +118,30 @@ protected function get_all_columns(): array {
117118
return !empty($rule->is_bulk_processing()) ? get_string('yes') : get_string('no');
118119
});
119120

121+
$columns[] = (new column(
122+
'realtime',
123+
new lang_string('rule_entity.realtime', 'tool_dynamic_cohorts'),
124+
$this->get_entity_name()
125+
))
126+
->add_joins($this->get_joins())
127+
->set_type(column::TYPE_TEXT)
128+
->add_field("{$alias}.realtime")
129+
->add_fields("{$alias}.id, {$alias}.name, {$alias}.realtime")
130+
->set_is_sortable(true)
131+
->add_callback(function ($value, $row) use ($globalrealtime) {
132+
global $OUTPUT;
133+
134+
$rule = new rule(0, $row);
135+
$rulerealtime = $rule->is_realtime();
136+
$string = !empty($rulerealtime) ? get_string('yes') : get_string('no');
137+
138+
if (!empty($rulerealtime) && !$globalrealtime) {
139+
$string .= $OUTPUT->pix_icon('i/warning', get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'));
140+
}
141+
142+
return $string;
143+
});
144+
120145
$columns[] = (new column(
121146
'status',
122147
new lang_string('rule_entity.status', 'tool_dynamic_cohorts'),

classes/reportbuilder/local/systemreports/rules.php

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ protected function initialise(): void {
8181
});
8282

8383
$this->add_column_from_entity('rule_entity:bulkprocessing');
84+
$this->add_column_from_entity('rule_entity:realtime');
8485

8586
$this->add_column(new column(
8687
'conditions',

classes/rule.php

+12
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ protected static function define_properties() {
6969
'type' => PARAM_INT,
7070
'default' => 0,
7171
],
72+
'realtime' => [
73+
'type' => PARAM_INT,
74+
'default' => 1,
75+
],
7276
];
7377
}
7478

@@ -110,6 +114,14 @@ public function is_bulk_processing(): bool {
110114
return (bool) $this->get('bulkprocessing');
111115
}
112116

117+
/**
118+
* Check if this rule should process realtime.
119+
* @return bool
120+
*/
121+
public function is_realtime(): bool {
122+
return (bool) $this->get('realtime');
123+
}
124+
113125
/**
114126
* Return if the rule is broken.
115127
*

classes/rule_form.php

+22
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class rule_form extends \moodleform {
3737
* Form definition.
3838
*/
3939
protected function definition() {
40+
global $OUTPUT;
41+
4042
$mform = $this->_form;
4143

4244
$mform->addElement('hidden', 'id');
@@ -99,6 +101,26 @@ protected function definition() {
99101
);
100102
$mform->addHelpButton('bulkprocessing', 'bulkprocessing', 'tool_dynamic_cohorts');
101103

104+
$mform->addElement(
105+
'advcheckbox',
106+
'realtime',
107+
get_string('realtime', 'tool_dynamic_cohorts'),
108+
get_string('enable'),
109+
[],
110+
[0, 1]
111+
);
112+
$mform->addHelpButton('realtime', 'realtime', 'tool_dynamic_cohorts');
113+
114+
if (!get_config('tool_dynamic_cohorts', 'realtime')) {
115+
$mform->freeze(['realtime']);
116+
$realtimeglobal = $OUTPUT->notification(
117+
get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'),
118+
'warning',
119+
false
120+
);
121+
$mform->addElement('static', 'realtimeglobal', '', $realtimeglobal);
122+
}
123+
102124
$mform->addElement('select',
103125
'operator',
104126
get_string('logical_operator', 'tool_dynamic_cohorts'),

classes/rule_manager.php

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public static function process_form(\stdClass $formdata): rule {
132132
'description' => $formdata->description,
133133
'bulkprocessing' => $formdata->bulkprocessing,
134134
'operator' => $formdata->operator,
135+
'realtime' => $formdata->realtime,
135136
];
136137

137138
$oldcohortid = 0;

db/install.xml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
2-
<XMLDB PATH="admin/tool/dynamic_cohorts/db" VERSION="20240304" COMMENT="XMLDB file for Moodle admin/tool/dynamic_cohorts"
2+
<XMLDB PATH="admin/tool/dynamic_cohorts/db" VERSION="20240913" COMMENT="XMLDB file for Moodle admin/tool/dynamic_cohorts"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
55
>
@@ -14,6 +14,7 @@
1414
<FIELD NAME="bulkprocessing" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should the rule be processed in bulk"/>
1515
<FIELD NAME="broken" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Is this rule broken?"/>
1616
<FIELD NAME="operator" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Logical operator (OR/AND) for all conditions in the rule"/>
17+
<FIELD NAME="realtime" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Should the rule be processed realtime?"/>
1718
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
1819
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
1920
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>

db/upgrade.php

+15
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,20 @@ function xmldb_tool_dynamic_cohorts_upgrade($oldversion): bool {
4444
upgrade_plugin_savepoint(true, 2024032501, 'tool', 'dynamic_cohorts');
4545
}
4646

47+
if ($oldversion < 2024091300) {
48+
49+
// Define field realtime to be added to tool_dynamic_cohorts.
50+
$table = new xmldb_table('tool_dynamic_cohorts');
51+
$field = new xmldb_field('realtime', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'operator');
52+
53+
// Conditionally launch add field realtime.
54+
if (!$dbman->field_exists($table, $field)) {
55+
$dbman->add_field($table, $field);
56+
}
57+
58+
// Dynamic_cohorts savepoint reached.
59+
upgrade_plugin_savepoint(true, 2024091300, 'tool', 'dynamic_cohorts');
60+
}
61+
4762
return true;
4863
}

index.php

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
notification::warning(get_string('brokenruleswarning', 'tool_dynamic_cohorts'));
4141
break;
4242
}
43+
44+
if ($rule->is_realtime() && !get_config('tool_dynamic_cohorts', 'realtime')) {
45+
notification::warning(get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'));
46+
break;
47+
}
4348
}
4449

4550
$report = system_report_factory::create(rules::class, context_system::instance(), 'tool_dynamic_cohorts');

lang/en/tool_dynamic_cohorts.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,15 @@
152152
$string['settings:realtime'] = 'Real time processing';
153153
$string['settings:realtime_desc'] = 'When enabled, rules with conditions that support triggering on the event will be processed synchronously as part of the event. Use caution when enabling as long running rule processing will block the user interface.';
154154
$string['settings:releasemembers'] = 'Release members';
155-
$string['settings:releasemembers_desc'] = 'If enabled all members will be removed from a cohort once it\'s not managed by the plugin (e.g a rule is deleted or cohort for a rule is changed). <br/> Please note: no cohort_member_removed events will be triggered when members are released from a cohort.';
155+
$string['settings:releasemembers_desc'] = 'If enabled all members will be removed from a cohort once it\'s not managed by the plugin (e.g a rule is deleted or cohort for a rule is changed). <br/> Please note: no cohort_member_removed events will be triggered when members are released from a cohort. Otherwise, the rule will be processed via cron.';
156156
$string['usercreated'] = 'User was created';
157157
$string['usercreatedin'] = 'Users who were created in the last {$a}';
158158
$string['usercreatedtime'] = 'Users who were created {$a->operator} {$a->time}';
159159
$string['usersforrule'] = 'Users matching rule "{$a->rule}" for cohort "{$a->cohort}"';
160160
$string['userlastlogin'] = 'User\'s last login';
161161
$string['haverole'] = 'have role';
162162
$string['donothaverole'] = 'do not have role';
163+
$string['realtime'] = 'Real time processing';
164+
$string['realtime_help'] = 'If enabled, the rule will be processed synchronously as part of the event (if conditions support triggering on the event). Use caution when enabling as long running rule processing will block the user interface.';
165+
$string['rule_entity.realtime'] = 'Realtime processing';
166+
$string['realtimedisabledglobally'] = 'Realtime processing disabled globally';

tests/observer_test.php

+38-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function setUp(): void {
5151
public function test_user_creation_triggers_rule_processing() {
5252
global $DB;
5353

54-
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id]);
54+
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id, 'realtime' => 1]);
5555
$rule->save();
5656

5757
$condition = condition_base::get_instance(0, (object)[
@@ -87,7 +87,7 @@ public function test_user_updating_triggers_rule_processing() {
8787
$user1 = $this->getDataGenerator()->create_user(['username' => 'user1']);
8888
$user2 = $this->getDataGenerator()->create_user(['username' => 'user2']);
8989

90-
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id]);
90+
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id, 'realtime' => 1]);
9191
$rule->save();
9292

9393
$condition = condition_base::get_instance(0, (object)[
@@ -123,7 +123,42 @@ public function test_realtime_rule_processing_when_disabled_globally() {
123123

124124
set_config('realtime', 0, 'tool_dynamic_cohorts');
125125

126-
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id]);
126+
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'cohortid' => $this->cohort->id, 'realtime' => 1]);
127+
$rule->save();
128+
129+
$condition = condition_base::get_instance(0, (object)[
130+
'classname' => 'tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition\user_profile',
131+
]);
132+
133+
// Condition username starts with user to catch both users.
134+
$condition->set_config_data([
135+
'profilefield' => 'username',
136+
'username_operator' => user_profile::TEXT_STARTS_WITH,
137+
'username_value' => 'user',
138+
]);
139+
140+
$record = $condition->get_record();
141+
$record->set('ruleid', $rule->get('id'));
142+
$record->set('sortorder', 0);
143+
$record->save();
144+
145+
$this->assertEquals(0, $DB->count_records('cohort_members', ['cohortid' => $this->cohort->id]));
146+
147+
$this->getDataGenerator()->create_user(['username' => 'user1']);
148+
$this->getDataGenerator()->create_user(['username' => 'user2']);
149+
150+
$this->assertEquals(0, $DB->count_records('cohort_members', ['cohortid' => $this->cohort->id]));
151+
}
152+
153+
/**
154+
* Test that user creation event doesn't trigger rule processing for that user if realtime processing is disabled for the rule.
155+
*/
156+
public function test_realtime_rule_processing_when_disabled_for_a_rule() {
157+
global $DB;
158+
159+
set_config('realtime', 1, 'tool_dynamic_cohorts');
160+
161+
$rule = new rule(0, (object)['name' => 'Test rule 1', 'enabled' => 1, 'realtime' => 0, 'cohortid' => $this->cohort->id]);
127162
$rule->save();
128163

129164
$condition = condition_base::get_instance(0, (object)[

0 commit comments

Comments
 (0)