Skip to content

Commit

Permalink
Merge pull request #74 from catalyst/issue41
Browse files Browse the repository at this point in the history
issue #41: add course completed and course_not_completed conditions
  • Loading branch information
tuanngocnguyen authored Apr 4, 2024
2 parents d7986bc + bac5a24 commit af5a6ed
Show file tree
Hide file tree
Showing 7 changed files with 824 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
246 changes: 246 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,246 @@
<?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;

if ($this->get_config_data()) {
// 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?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 tool_dynamic_cohorts\condition_sql;

/**
* 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_not_completed extends course_completed {

/**
* Condition name.
*
* @return string
*/
public function get_name(): string {
return get_string('condition:course_not_completed', '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);
}

/**
* 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]);

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

/**
* 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();

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

$join = "LEFT JOIN {course_completions} $completiontable
ON ($completiontable.userid = u.id AND $completiontable.course = :$courseidparam)";

$where = "$completiontable.timecompleted IS NULL OR $completiontable.id IS NULL";

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

return $sql;
}
}
10 changes: 10 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,10 @@
$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:course_not_completed'] = 'Course not completed';
$string['condition:course_not_completed_description'] = 'A user has not completed course "{$a->course}"';
$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 +98,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 af5a6ed

Please sign in to comment.