Skip to content

Commit

Permalink
issue #41: add course completed condition
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitriim committed Apr 3, 2024
1 parent d7986bc commit 8e031c4
Show file tree
Hide file tree
Showing 5 changed files with 516 additions and 3 deletions.
2 changes: 1 addition & 1 deletion classes/condition_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected function get_condition(): condition_base {
throw new coding_exception('Condition class name is not set');
}

$conditions = condition_manager::get_all_conditions();
$conditions = condition_manager::get_all_conditions(false);
if (!array_key_exists($this->_customdata['classname'], $conditions)) {
throw new moodle_exception('Condition is broken. Invalid condition class.');
}
Expand Down
244 changes: 244 additions & 0 deletions classes/local/tool_dynamic_cohorts/condition/course_completed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
<?php
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.

namespace tool_dynamic_cohorts\local\tool_dynamic_cohorts\condition;

use completion_info;
use tool_dynamic_cohorts\condition_base;
use tool_dynamic_cohorts\condition_sql;

defined('MOODLE_INTERNAL') || die;

require_once($CFG->libdir . '/completionlib.php');

/**
* Condition based on course completion.
*
* @package tool_dynamic_cohorts
* @copyright 2024 Catalyst IT
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_completed extends condition_base {

/**
* Operator for any completion date.
*/
public const OPERATOR_ANY = 1;

/**
* Operator for completion dates before some date.
*/
public const OPERATOR_BEFORE = 2;

/**
* Operator for completion dates after some date.
*/
public const OPERATOR_AFTER = 3;

/**
* Condition name.
*
* @return string
*/
public function get_name(): string {
return get_string('condition:course_completed', 'tool_dynamic_cohorts');
}

/**
* Gets a list of operators.
*
* @return array A list of operators.
*/
protected function get_operators(): array {
return [
self::OPERATOR_ANY => get_string('any', 'tool_dynamic_cohorts'),
self::OPERATOR_BEFORE => get_string('before', 'tool_dynamic_cohorts'),
self::OPERATOR_AFTER => get_string('after', 'tool_dynamic_cohorts'),
];
}

/**
* Add config form elements.
*
* @param \MoodleQuickForm $mform
*/
public function config_form_add(\MoodleQuickForm $mform): void {
$mform->addElement('course', 'courseid', get_string('course'), ['onlywithcompletion' => true]);
$mform->addRule('courseid', null, 'required', null, 'client');
$mform->setType('courseid', PARAM_INT);

$mform->addElement(
'select',
'operator',
get_string('completiondate', 'tool_dynamic_cohorts'),
$this->get_operators()
);

$mform->addElement('date_time_selector', 'timecompleted');
$mform->hideIf('timecompleted', 'operator', 'eq', self::OPERATOR_ANY);
$mform->setDefault('timecompleted', usergetmidnight(time()));
}

/**
* Validate config form elements.
*
* @param array $data Data to validate.
* @return array
*/
public function config_form_validate(array $data): array {
$errors = [];

if (!isset($data['courseid'])) {
$errors['courseid'] = get_string('required');
return $errors;
}

return $errors;
}

/**
* Gets configured course ID.
*
* @return int
*/
protected function get_courseid_value(): int {
return $this->get_config_data()['courseid'] ?? 0;
}

/**
* Gets operator value.
*
* @return int
*/
protected function get_operator_value(): int {
return $this->get_config_data()['operator'] ?? self::OPERATOR_ANY;
}

/**
* Gets configured completion time.
*
* @return int
*/
protected function get_timecompleted_value(): int {
return $this->get_config_data()['timecompleted'] ?? 0;
}

/**
* Human-readable description of the configured condition.
*
* @return string
*/
public function get_config_description(): string {
global $DB;

$coursename = $DB->get_field('course', 'fullname', ['id' => $this->get_courseid_value()]);
$coursename = format_string($coursename, true, ['context' => \context_system::instance(), 'escape' => false]);

$operatorvalue = $this->get_operator_value();

$operator = $operatorvalue != self::OPERATOR_ANY ? strtolower($this->get_operators()[$operatorvalue]) : '';
$timecompleted = $operatorvalue != self::OPERATOR_ANY ? userdate($this->get_timecompleted_value()) : '';

return get_string('condition:course_completed_description', 'tool_dynamic_cohorts', (object)[
'course' => $coursename,
'operator' => $operator,
'timecompleted' => $timecompleted,
]);
}

/**
* Human readable description of the broken condition.
*
* @return string
*/
public function get_broken_description(): string {
global $DB;

// Check course exists.
if (!$course = $DB->get_record('course', ['id' => $this->get_courseid_value()])) {
return get_string('missingcourse', 'tool_dynamic_cohorts');
}

// Check completion is enabled for a course.
$completion = new completion_info($course);
if (!$completion->is_enabled()) {
return get_string('completionisdisabled', 'tool_dynamic_cohorts');
}

return parent::get_broken_description();
}

/**
* Gets SQL data for building SQL.
*
* @return condition_sql
*/
public function get_sql(): condition_sql {
$sql = new condition_sql('', '1=0', []);

if (!$this->is_broken()) {
$params = [];

$completiontable = condition_sql::generate_table_alias();
$join = "JOIN {course_completions} $completiontable ON ($completiontable.userid = u.id)";

$courseid = $this->get_courseid_value();
$courseidparam = condition_sql::generate_param_alias();
$params[$courseidparam] = $courseid;

$timecompleted = $this->get_timecompleted_value();
$timecompletedparam = condition_sql::generate_param_alias();

$operator = $this->get_operator_value();

if ($operator != self::OPERATOR_ANY && $timecompleted > 0) {
$operator = $operator == self::OPERATOR_BEFORE ? '<' : '>';
$where = "$completiontable.course = :$courseidparam
AND $completiontable.timecompleted $operator :$timecompletedparam";
$params[$timecompletedparam] = $timecompleted;
} else {
$where = "$completiontable.course = :$courseidparam
AND $completiontable.timecompleted IS NOT NULL";
}

$sql = new condition_sql($join, $where, $params);
}

return $sql;
}

/**
* Is condition broken.
*
* @return bool
*/
public function is_broken(): bool {
global $DB;

// Check course exists.
if (!$course = $DB->get_record('course', ['id' => $this->get_courseid_value()])) {
return true;
}

// Check completion is enabled for a course.
$completion = new completion_info($course);
if (!$completion->is_enabled()) {
return true;
}

return false;
}
}
8 changes: 8 additions & 0 deletions lang/en/tool_dynamic_cohorts.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
$string['addcondition'] = 'Add a condition';
$string['addrule'] = 'Add a new rule';
$string['add_rule'] = 'Add new rule';
$string['after'] = 'After';
$string['any'] = 'Any';
$string['backtolistofrules'] = 'Back to the list of rules';
$string['before'] = 'Before';
$string['brokenruleswarning'] = 'There are some broken rules require your attention. <br /> To fix a broken rule you should remove all broken conditions. <br />Sometimes a rule becomes broken when matching users SQL failed. In this case all condition are ok, but the rule is marked as broken. You should check Moodle logs for "Matching users failed" event and related SQL errors. <br />Please note, that in any case you have to re-save the rule to mark it as unbroken.';
$string['bulkprocessing'] = 'Bulk processing';
$string['bulkprocessing_help'] = 'If this option is enabled, users will be added and removed from cohort in bulk. This will significantly improve processing performance. However, using this option will suppress triggering events when users added or removed from cohort.';
Expand All @@ -38,6 +41,8 @@
$string['cohortid'] = 'Cohort';
$string['cohortswith'] = 'Cohort(s) with field';
$string['cohortid_help'] = 'A cohort to manage as part of this rule. Only cohorts that are not managed by other plugins are displayed in this list.';
$string['completiondate'] = 'Completion date';
$string['completionisdisabled'] = 'Completion is disabled for configured course';
$string['condition'] = 'Condition';
$string['conditions'] = 'Conditions';
$string['conditionstext'] = '{$a->conditions} ( logical {$a->operator} )';
Expand All @@ -50,6 +55,8 @@
$string['condition:cohort_membership_broken_description'] = 'Condition is broken. Using the same cohort that the given rule is configured to manage to.';
$string['condition:cohort_field'] = 'Cohort field';
$string['condition:cohort_field_description'] = 'A user {$a->operator} cohorts with field \'{$a->field}\' {$a->fieldoperator} {$a->fieldvalue}';
$string['condition:course_completed'] = 'Course completed';
$string['condition:course_completed_description'] = 'A user has completed course "{$a->course}" {$a->operator} {$a->timecompleted}';
$string['condition:profile_field_description'] = '{$a->field} {$a->fieldoperator} {$a->fieldvalue}';
$string['condition:user_profile'] = 'User standard profile field';
$string['condition:user_custom_profile'] = 'User custom profile field';
Expand Down Expand Up @@ -89,6 +96,7 @@
$string['managerules'] = 'Manage rules';
$string['managecohorts'] = 'Manage cohorts';
$string['matchingusers'] = 'Matching users';
$string['missingcourse'] = 'Missing course';
$string['name'] = 'Rule name';
$string['name_help'] = 'A human readable name of this rule.';
$string['operator'] = 'Operator';
Expand Down
Loading

0 comments on commit 8e031c4

Please sign in to comment.