forked from moodle/moodle
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MDL-82120 gradepenalty_duedate: add new plugin
- Loading branch information
Nathan Nguyen
committed
Jun 24, 2024
1 parent
56de64b
commit bebf2a2
Showing
15 changed files
with
1,412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?php | ||
// This file is part of Moodle - http://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 <http://www.gnu.org/licenses/>. | ||
|
||
namespace gradepenalty_duedate; | ||
|
||
use context_module; | ||
use context_course; | ||
use context_system; | ||
use core_grades\local\penalty\grade_penalty; | ||
|
||
/** | ||
* Calculate penalty. | ||
* | ||
* @package gradepenalty_duedate | ||
* @copyright 2024 Catalyst IT Australia Pty Ltd | ||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||
*/ | ||
class gradepenalty_duedate extends grade_penalty { | ||
|
||
/** | ||
* Mark will be deducted from student grade. | ||
* | ||
* @param float $finalgrade Final grade. | ||
* @return float the penalty. | ||
*/ | ||
public function calculate_penalty($finalgrade): float { | ||
$penalty = 0.0; | ||
|
||
// Calculate the difference between the submission date and the due date. | ||
$diff = $this->submissiondate - $this->duedate; | ||
|
||
// If the submission date is after the due date, calculate the penalty. | ||
if ($diff > 0) { | ||
// Get all penalty rules, ordered by the highest penalty first. | ||
$penaltyrules = $this->find_effective_penalty_rules(); | ||
|
||
// Check each rule to see which rule will apply. | ||
if (!empty($penaltyrules)) { | ||
foreach ($penaltyrules as $penaltyrule) { | ||
if ($diff >= $penaltyrule->get('latefor')) { | ||
$penalty = $penaltyrule->get('penalty'); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Calculate the deducted grade. | ||
return $finalgrade * $penalty / 100; | ||
} | ||
|
||
/** | ||
* Find effective penalty rule. | ||
* | ||
* @return array | ||
*/ | ||
public function find_effective_penalty_rules(): array { | ||
// Course module context id. | ||
$modulecontext = context_module::instance($this->cm->id); | ||
|
||
// Get all penalty rules, ordered by the highest penalty first. | ||
$penaltyrules = penalty_rule::get_records(['contextid' => $modulecontext->id], 'sortorder DESC'); | ||
|
||
// If there is no penalty rule, go to the course context. | ||
if (empty($penaltyrules)) { | ||
// Find course content. | ||
$course = get_course($this->cm->course); | ||
$coursecontext = context_course::instance($course->id); | ||
|
||
$penaltyrules = penalty_rule::get_records(['contextid' => $coursecontext->id], 'sortorder DESC'); | ||
} | ||
|
||
// If there is no penalty rule, go to the system context. | ||
if (empty($penaltyrules)) { | ||
$systemcontext = context_system::instance(); | ||
$penaltyrules = penalty_rule::get_records(['contextid' => $systemcontext->id], 'sortorder DESC'); | ||
} | ||
|
||
return $penaltyrules; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
<?php | ||
// This file is part of Moodle - http://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 <http://www.gnu.org/licenses/>. | ||
|
||
namespace gradepenalty_duedate; | ||
|
||
defined('MOODLE_INTERNAL') || die(); | ||
|
||
require_once(__DIR__ . '/../../../lib.php'); | ||
|
||
/** | ||
* Helper for grade penalty | ||
* | ||
* @package gradepenalty_duedate | ||
* @copyright 2024 Catalyst IT Australia Pty Ltd | ||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||
*/ | ||
class helper { | ||
/** | ||
* Determine min and max value for latefor and penalty for a rule when updating or inserting. | ||
* If updating, the ruleid belongs to the rule we are updating. | ||
* If inserting, the ruleid belong to the rule which we will insert new rule before or after it | ||
* | ||
* @param int $ruleid the rule id we are updating or inserting | ||
* @param int $action we are updating or inserting | ||
* @param string $field which is 'latefor' or 'penalty' | ||
* @param int $defaultmin default min value | ||
* @param int $defaultmax default max value | ||
* @return array | ||
*/ | ||
public static function calculate_min_max_values($ruleid, $action, $field, $defaultmin, $defaultmax) { | ||
global $DB; | ||
|
||
$minlatefor = $defaultmin; | ||
$maxlatefor = $defaultmax; | ||
|
||
if ($ruleid !== 0) { | ||
// Current rule. | ||
$currentrule = $DB->get_record('gradepenalty_duedate_rule', ['id' => $ruleid]); | ||
|
||
// Get the previous rule. | ||
$previousrule = $DB->get_record('gradepenalty_duedate_rule', [ | ||
'sortorder' => $currentrule->sortorder - 1, | ||
'contextid' => $currentrule->contextid, | ||
]); | ||
|
||
// Get the next rule. | ||
$nextrule = $DB->get_record('gradepenalty_duedate_rule', [ | ||
'sortorder' => $currentrule->sortorder + 1, | ||
'contextid' => $currentrule->contextid, | ||
]); | ||
|
||
if ($action === GRADEPENALTY_DUEDATE_ACTION_INSERT_ABOVE) { | ||
// We will insert new rule above the current rule. | ||
$minlatefor = $previousrule ? $previousrule->$field + 1 : $defaultmin; | ||
$maxlatefor = $currentrule->$field - 1; | ||
} else if ($action === GRADEPENALTY_DUEDATE_ACTION_INSERT_BELOW) { | ||
// We will insert new rule below the current rule. | ||
$minlatefor = $currentrule->$field + 1; | ||
$maxlatefor = $nextrule ? $nextrule->$field - 1 : $defaultmax; | ||
} else if ($action === GRADEPENALTY_DUEDATE_ACTION_UPDATE) { | ||
// We are updating the rule, so we need to check the min and max value for the rule. | ||
$minlatefor = $previousrule ? $previousrule->$field + 1 : $defaultmin; | ||
$maxlatefor = $nextrule ? $nextrule->$field - 1 : $defaultmax; | ||
} | ||
} | ||
|
||
return [$minlatefor, $maxlatefor]; | ||
} | ||
|
||
/** | ||
* Whether we can insert rule above or below. | ||
* | ||
* @param int $ruleid the rule id which we want to insert above or below. | ||
* @param int $action insert above or below. | ||
* | ||
* @return bool | ||
*/ | ||
public static function can_insert_rule(int $ruleid, int $action): bool { | ||
global $DB; | ||
|
||
if ($ruleid === 0) { | ||
return false; | ||
} | ||
|
||
$currentrule = $DB->get_record('gradepenalty_duedate_rule', ['id' => $ruleid]); | ||
|
||
if ($action === GRADEPENALTY_DUEDATE_ACTION_INSERT_ABOVE) { | ||
// Get the previous rule. | ||
$previousrule = $DB->get_record('gradepenalty_duedate_rule', [ | ||
'sortorder' => $currentrule->sortorder - 1, | ||
'contextid' => $currentrule->contextid, | ||
]); | ||
$previouslatefor = $previousrule ? $previousrule->latefor : GRADEPENALTY_DUEDATE_MIN_LATEFOR; | ||
$previouspenalty = $previousrule ? $previousrule->penalty : GRADEPENALTY_DUEDATE_MIN_PENALTY; | ||
// Check if we still have space for insertion (at least 1 second and 1 percent). | ||
return $currentrule->latefor > ($previouslatefor + 1) && $currentrule->penalty > ($previouspenalty + 1); | ||
} else if ($action === GRADEPENALTY_DUEDATE_ACTION_INSERT_BELOW) { | ||
// Get the next rule. | ||
$nextrule = $DB->get_record('gradepenalty_duedate_rule', [ | ||
'sortorder' => $currentrule->sortorder + 1, | ||
'contextid' => $currentrule->contextid, | ||
]); | ||
$nextlatefor = $nextrule ? $nextrule->latefor : GRADEPENALTY_DUEDATE_MAX_LATEFOR; | ||
$nextpenalty = $nextrule ? $nextrule->penalty : GRADEPENALTY_DUEDATE_MAX_PENALTY; | ||
// Check if we still have space for insertion (at least 1 second and 1 percent). | ||
return $currentrule->latefor < ($nextlatefor - 1) && $currentrule->penalty < ($nextpenalty - 1); | ||
} | ||
|
||
return false; | ||
} | ||
} |
153 changes: 153 additions & 0 deletions
153
grade/penalty/duedate/classes/output/form/penalty_rule_form.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php | ||
// This file is part of Moodle - http://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 <http://www.gnu.org/licenses/>. | ||
|
||
namespace gradepenalty_duedate\output\form; | ||
|
||
defined('MOODLE_INTERNAL') || die(); | ||
|
||
require_once($CFG->libdir . '/formslib.php'); | ||
require_once(__DIR__ . '/../../../lib.php'); | ||
|
||
use gradepenalty_duedate\helper; | ||
use gradepenalty_duedate\penalty_rule; | ||
use moodleform; | ||
|
||
/** | ||
* Form to set up the penalty rules for the gradepenalty_duedate plugin. | ||
* | ||
* @package gradepenalty_duedate | ||
* @copyright 2024 Catalyst IT Australia Pty Ltd | ||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||
*/ | ||
class penalty_rule_form extends moodleform { | ||
/** @var int Min latefor value */ | ||
protected $minlatefor = GRADEPENALTY_DUEDATE_MIN_LATEFOR; | ||
|
||
/** @var int Max latefor value */ | ||
protected $maxlatefor = GRADEPENALTY_DUEDATE_MAX_LATEFOR; | ||
|
||
/** @var int Min penalty value */ | ||
protected $minpenalty = GRADEPENALTY_DUEDATE_MIN_PENALTY; | ||
|
||
/** @var int Max penalty value */ | ||
protected $maxpenalty = GRADEPENALTY_DUEDATE_MAX_PENALTY; | ||
|
||
/** @var int ruleid */ | ||
protected $ruleid = 0; | ||
|
||
/** @var int contextid */ | ||
protected $contextid = 0; | ||
|
||
/** @var int action */ | ||
protected $action = GRADEPENALTY_DUEDATE_ACTION_CREATE; | ||
|
||
/** | ||
* Define the form. | ||
* | ||
* @return void | ||
*/ | ||
public function definition() { | ||
$mform = $this->_form; | ||
|
||
// Set up min/max for latefor and penalty. | ||
$this->ruleid = $this->_customdata['ruleid'] ?? 0; | ||
$this->contextid = $this->_customdata['contextid'] ?? 0; | ||
$this->action = $this->_customdata['action'] ?? GRADEPENALTY_DUEDATE_ACTION_CREATE; | ||
|
||
// Calculate min/max value for latefor. | ||
list($this->minlatefor, $this->maxlatefor) = helper::calculate_min_max_values($this->ruleid, $this->action, 'latefor', | ||
GRADEPENALTY_DUEDATE_MIN_LATEFOR, GRADEPENALTY_DUEDATE_MAX_LATEFOR); | ||
// And for penalty. | ||
list($this->minpenalty, $this->maxpenalty) = helper::calculate_min_max_values($this->ruleid, $this->action, 'penalty', | ||
GRADEPENALTY_DUEDATE_MIN_PENALTY, GRADEPENALTY_DUEDATE_MAX_PENALTY); | ||
|
||
// Hidden context id, value is stored in $mform. | ||
$mform->addElement('hidden', 'contextid'); | ||
$mform->setType('contextid', PARAM_INT); | ||
$mform->setDefault('contextid', $this->contextid); | ||
|
||
// Hidden rule id, value is stored in $mform. | ||
$mform->addElement('hidden', 'ruleid'); | ||
$mform->setType('ruleid', PARAM_INT); | ||
$mform->setDefault('ruleid', $this->ruleid); | ||
|
||
// Hidden action, value is stored in $mform. | ||
$mform->addElement('hidden', 'action'); | ||
$mform->setType('action', PARAM_INT); | ||
$mform->setDefault('action', $this->action); | ||
|
||
// If ruleid is not 0, then we are editing an existing rule. | ||
$rule = new penalty_rule($this->ruleid); | ||
|
||
// Latefor field. | ||
$mform->addElement('duration', 'latefor', get_string('latefor', 'gradepenalty_duedate'), ['defaultunit' => DAYSECS]); | ||
$mform->setType('latefor', PARAM_INT); | ||
// Default value. If we are updating a rule, use the current value. | ||
$mform->setDefault('latefor', $this->action === GRADEPENALTY_DUEDATE_ACTION_UPDATE ? | ||
$rule->get('latefor') : $this->minlatefor); | ||
// Required rule. | ||
$mform->addRule('latefor', get_string('required'), 'required'); | ||
// Help button. | ||
$mform->addHelpButton('latefor', 'latefor', 'gradepenalty_duedate'); | ||
|
||
// Penalty field. | ||
$mform->addElement('text', 'penalty', get_string('penalty', 'gradepenalty_duedate')); | ||
$mform->setType('penalty', PARAM_INT); | ||
// Default value. If we are updating a rule, use the current value. | ||
$mform->setDefault('penalty', $this->action === GRADEPENALTY_DUEDATE_ACTION_UPDATE ? | ||
$rule->get('penalty') : $this->minpenalty); | ||
// Required rule. | ||
$mform->addRule('penalty', get_string('required'), 'required'); | ||
// Help button. | ||
$mform->addHelpButton('penalty', 'penalty', 'gradepenalty_duedate'); | ||
|
||
// Add buttons. | ||
$this->add_action_buttons(); | ||
} | ||
|
||
/** | ||
* Make sure the latefor and penalty values are within the min/max values. | ||
* | ||
* @param array $data array of data from the form. | ||
* @param array $files array of files from the form. | ||
* @return array | ||
*/ | ||
public function validation($data, $files) { | ||
$errors = parent::validation($data, $files); | ||
|
||
// Validate latefor. | ||
// Min value. | ||
if ($data['latefor'] < $this->minlatefor) { | ||
$errors['latefor'] = get_string('error_latefor_minvalue', 'gradepenalty_duedate', $this->minlatefor); | ||
} | ||
// Max value. | ||
if ($data['latefor'] > $this->maxlatefor) { | ||
$errors['latefor'] = get_string('error_latefor_maxvalue', 'gradepenalty_duedate', $this->maxlatefor); | ||
} | ||
|
||
// Validate penalty. | ||
// Min value. | ||
if ($data['penalty'] < $this->minpenalty) { | ||
$errors['penalty'] = get_string('error_penalty_minvalue', 'gradepenalty_duedate', $this->minpenalty); | ||
} | ||
// Max value. | ||
if ($data['penalty'] > $this->maxpenalty) { | ||
$errors['penalty'] = get_string('error_penalty_maxvalue', 'gradepenalty_duedate', $this->maxpenalty); | ||
} | ||
|
||
return $errors; | ||
} | ||
} |
Oops, something went wrong.