Skip to content

Commit

Permalink
issue #108: add realtime processing per rule
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitriim committed Sep 15, 2024
1 parent 9a79f65 commit 7d8c7ee
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 27 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ Rules can be processed by two mechanisms:

### Disabling realtime processing (processing on event)

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.
Each rule can be configured to be processed realtime (if any of the related conditions support processing on event).

There is also a global admin setting that allows administrator to enable or disable realtime rule processing globally overriding per rule configuration.

# Configuration

Expand Down
4 changes: 3 additions & 1 deletion classes/observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ public static function process_event(base $event): void {
if (get_config('tool_dynamic_cohorts', 'realtime')) {
foreach (condition_manager::get_conditions_with_event($event) as $condition) {
foreach (rule_manager::get_rules_with_condition($condition) as $rule) {
rule_manager::process_rule($rule, self::get_userid_from_event($event));
if ($rule->is_realtime()) {
rule_manager::process_rule($rule, self::get_userid_from_event($event));
}
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions classes/reportbuilder/local/entities/rule_entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function initialise(): base {
*/
protected function get_all_columns(): array {
$alias = $this->get_table_alias('tool_dynamic_cohorts');
$globalrealtime = get_config('tool_dynamic_cohorts', 'realtime');

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

$columns[] = (new column(
'realtime',
new lang_string('rule_entity.realtime', 'tool_dynamic_cohorts'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$alias}.realtime")
->add_fields("{$alias}.id, {$alias}.name, {$alias}.realtime")
->set_is_sortable(true)
->add_callback(function ($value, $row) use ($globalrealtime) {
global $OUTPUT;

$rule = new rule(0, $row);
$rulerealtime = $rule->is_realtime();
$string = !empty($rulerealtime) ? get_string('yes') : get_string('no');

if (!empty($rulerealtime) && !$globalrealtime) {
$string .= $OUTPUT->pix_icon('i/warning', get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'));
}

return $string;
});

$columns[] = (new column(
'status',
new lang_string('rule_entity.status', 'tool_dynamic_cohorts'),
Expand Down
1 change: 1 addition & 0 deletions classes/reportbuilder/local/systemreports/rules.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ protected function initialise(): void {
});

$this->add_column_from_entity('rule_entity:bulkprocessing');
$this->add_column_from_entity('rule_entity:realtime');

$this->add_column(new column(
'conditions',
Expand Down
12 changes: 12 additions & 0 deletions classes/rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ protected static function define_properties() {
'type' => PARAM_INT,
'default' => 0,
],
'realtime' => [
'type' => PARAM_INT,
'default' => 1,
],
];
}

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

/**
* Check if this rule should process realtime.
* @return bool
*/
public function is_realtime(): bool {
return (bool) $this->get('realtime');
}

/**
* Return if the rule is broken.
*
Expand Down
22 changes: 22 additions & 0 deletions classes/rule_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class rule_form extends \moodleform {
* Form definition.
*/
protected function definition() {
global $OUTPUT;

$mform = $this->_form;

$mform->addElement('hidden', 'id');
Expand Down Expand Up @@ -99,6 +101,26 @@ protected function definition() {
);
$mform->addHelpButton('bulkprocessing', 'bulkprocessing', 'tool_dynamic_cohorts');

$mform->addElement(
'advcheckbox',
'realtime',
get_string('realtime', 'tool_dynamic_cohorts'),
get_string('enable'),
[],
[0, 1]
);
$mform->addHelpButton('realtime', 'realtime', 'tool_dynamic_cohorts');

if (!get_config('tool_dynamic_cohorts', 'realtime')) {
$mform->freeze(['realtime']);
$realtimeglobal = $OUTPUT->notification(
get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'),
'warning',
false
);
$mform->addElement('static', 'realtimeglobal', '', $realtimeglobal);
}

$mform->addElement('select',
'operator',
get_string('logical_operator', 'tool_dynamic_cohorts'),
Expand Down
1 change: 1 addition & 0 deletions classes/rule_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public static function process_form(\stdClass $formdata): rule {
'description' => $formdata->description,
'bulkprocessing' => $formdata->bulkprocessing,
'operator' => $formdata->operator,
'realtime' => $formdata->realtime,
];

$oldcohortid = 0;
Expand Down
3 changes: 2 additions & 1 deletion db/install.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="admin/tool/dynamic_cohorts/db" VERSION="20240304" COMMENT="XMLDB file for Moodle admin/tool/dynamic_cohorts"
<XMLDB PATH="admin/tool/dynamic_cohorts/db" VERSION="20240913" COMMENT="XMLDB file for Moodle admin/tool/dynamic_cohorts"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
Expand All @@ -14,6 +14,7 @@
<FIELD NAME="bulkprocessing" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should the rule be processed in bulk"/>
<FIELD NAME="broken" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Is this rule broken?"/>
<FIELD NAME="operator" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Logical operator (OR/AND) for all conditions in the rule"/>
<FIELD NAME="realtime" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="Should the rule be processed realtime?"/>
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
Expand Down
15 changes: 15 additions & 0 deletions db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,20 @@ function xmldb_tool_dynamic_cohorts_upgrade($oldversion): bool {
upgrade_plugin_savepoint(true, 2024032501, 'tool', 'dynamic_cohorts');
}

if ($oldversion < 2024051004) {

// Define field realtime to be added to tool_dynamic_cohorts.
$table = new xmldb_table('tool_dynamic_cohorts');
$field = new xmldb_field('realtime', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'operator');

// Conditionally launch add field realtime.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Dynamic_cohorts savepoint reached.
upgrade_plugin_savepoint(true, 2024051004, 'tool', 'dynamic_cohorts');
}

return true;
}
5 changes: 5 additions & 0 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
notification::warning(get_string('brokenruleswarning', 'tool_dynamic_cohorts'));
break;
}

if ($rule->is_realtime() && !get_config('tool_dynamic_cohorts', 'realtime')) {
notification::warning(get_string('realtimedisabledglobally', 'tool_dynamic_cohorts'));
break;
}
}

$report = system_report_factory::create(rules::class, context_system::instance(), 'tool_dynamic_cohorts');
Expand Down
6 changes: 5 additions & 1 deletion lang/en/tool_dynamic_cohorts.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,15 @@
$string['settings:realtime'] = 'Real time processing';
$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.';
$string['settings:releasemembers'] = 'Release members';
$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.';
$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.';
$string['usercreated'] = 'User was created';
$string['usercreatedin'] = 'Users who were created in the last {$a}';
$string['usercreatedtime'] = 'Users who were created {$a->operator} {$a->time}';
$string['usersforrule'] = 'Users matching rule "{$a->rule}" for cohort "{$a->cohort}"';
$string['userlastlogin'] = 'User\'s last login';
$string['haverole'] = 'have role';
$string['donothaverole'] = 'do not have role';
$string['realtime'] = 'Real time processing';
$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.';
$string['rule_entity.realtime'] = 'Realtime processing';
$string['realtimedisabledglobally'] = 'Realtime processing disabled globally';
41 changes: 38 additions & 3 deletions tests/observer_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function setUp(): void {
public function test_user_creation_triggers_rule_processing() {
global $DB;

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

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

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

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

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

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

$condition = condition_base::get_instance(0, (object)[
'classname' => 'tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition\user_profile',
]);

// Condition username starts with user to catch both users.
$condition->set_config_data([
'profilefield' => 'username',
'username_operator' => user_profile::TEXT_STARTS_WITH,
'username_value' => 'user',
]);

$record = $condition->get_record();
$record->set('ruleid', $rule->get('id'));
$record->set('sortorder', 0);
$record->save();

$this->assertEquals(0, $DB->count_records('cohort_members', ['cohortid' => $this->cohort->id]));

$this->getDataGenerator()->create_user(['username' => 'user1']);
$this->getDataGenerator()->create_user(['username' => 'user2']);

$this->assertEquals(0, $DB->count_records('cohort_members', ['cohortid' => $this->cohort->id]));
}

/**
* Test that user creation event doesn't trigger rule processing for that user if realtime processing is disabled for the rule.
*/
public function test_realtime_rule_processing_when_disabled_for_a_rule() {
global $DB;

set_config('realtime', 1, 'tool_dynamic_cohorts');

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

$condition = condition_base::get_instance(0, (object)[
Expand Down
Loading

0 comments on commit 7d8c7ee

Please sign in to comment.