. /** * Aggregate course results based on failed, completed, excellent states for grades * @package local_treestudyplan * @copyright 2023 P.M. Kuipers * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace local_treestudyplan\local\aggregators; use \local_treestudyplan\courseinfo; use \local_treestudyplan\gradeinfo; use \local_treestudyplan\studyitem; use \local_treestudyplan\completion; /** * Aggregate course results based on failed, achieved, completed states for grades * @deprecated This aggregation style is no longer used, but included to support old study plans */ class tristate_aggregator extends \local_treestudyplan\aggregator { /** @var bool */ public const DEPRECATED = true; /** @var string */ private const DEFAULT_CONDITION = "50"; /** * Determine if aggregation method wants to select gradables * @return bool True if aggregation method needs gradables to be selected */ public function select_gradables() { return true; } /** * Determine if aggregation method is deprecated * @return bool True if aggregation method is deprecated */ public function deprecated() { return self::DEPRECATED; } /** * Determine if the aggregation method uses manual activity selection, * @return bool True if the aggregation method uses manual activity selection */ public function use_manualactivityselection() { return true; } /** * Aggregate completions into final result * @param int[] $a List of completion inputs * @param string $condition Condition description [ALL, 67, 50, ANY] * @return int Aggregated completion as completion class constant */ protected function aggregate_completion(array $a, $condition = "50") { if (in_array(strtoupper($condition), ['ALL', '67', '50', 'ANY'])) { // Condition is one of the valid conditions. $ccompleted = 0; $cexcellent = 0; $cprogress = 0; $cpending = 0; $count = count($a); if ($count > 0) { foreach ($a as $c) { $cprogress += ($c >= completion::PROGRESS) ? 1 : 0; $ccompleted += ($c >= completion::COMPLETED) ? 1 : 0; $cexcellent += ($c >= completion::EXCELLENT) ? 1 : 0; $cpending += ($c >= completion::PENDING) ? 1 : 0; } $required = [ 'ALL' => 1.00 * $count, '67' => 0.67 * $count, '50' => 0.50 * $count, 'ANY' => 1, ][$condition]; if ($cexcellent >= $required) { return completion::EXCELLENT; } else if ($ccompleted >= $required) { return completion::COMPLETED; } else { /* Return PROGRESS if one or more completions are COMPLETED or EXCELLENT, but the aggregation margin is not met. State PROGRESS will not carry on if aggregations are chained. */ if ($cprogress > 0) { return completion::PROGRESS; } else if ($cpending > 0) { return completion::PENDING; } else { return completion::INCOMPLETE; } } } else { return completion::INCOMPLETE; } } else { // Indeterminable. return null; } } /** * Aggregate all completions in a course into one final course completion * Possible states: * completion::EXCELLENT - Completed with excellent results * completion::GOOD - Completed with good results * completion::COMPLETED - Completed * completion::PROGRESS - Started, but not completed yey * completion::FAILED - Failed * completion::INCOMPLETE - Not yet started * @param courseinfo $courseinfo Courseinfo object for the course to check * @param studyitem $studyitem Studyitem object for the course to check * @param int $userid Id of user to check this course for * @return int Aggregated completion as completion class constant */ public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) { $condition = $studyitem->conditions(); if (empty($condition)) { $condition = self::DEFAULT_CONDITION; } $list = []; foreach (gradeinfo::list_studyitem_gradables($studyitem) as $gi) { $list[] = $this->grade_completion($gi, $userid); } $completion = self::aggregate_completion($list, $condition); return $completion; } /** * Aggregate juncton/filter inputs into one final junction outcome * @param int[] $completion List of completion inputs * @param studyitem $studyitem Studyitem object for the junction * @param int $userid Id of user to check completion for * @return int Aggregated completion as completion class constant */ public function aggregate_junction(array $completion, studyitem $studyitem, $userid) { $completed = self::aggregate_completion($completion, $studyitem->conditions()); // If null result (conditions are unknown/null) - default to ALL. return isset($completed) ? $completed : (self::aggregate_completion($completion, 'ALL')); } /** * Determine completion for a single grade and user * @param gradeinfo $gradeinfo Gradeinfo object for grade to check * @param mixed $userid Id of user to check completion for * @return int Aggregated completion as completion class constant */ public function grade_completion(gradeinfo $gradeinfo, $userid) { global $DB; $gradeitem = $gradeinfo->get_gradeitem(); $grade = $gradeitem->get_final($userid); if (empty($grade)) { return completion::INCOMPLETE; } else if ($grade->finalgrade === null) { // On assignments, grade NULL means a submission has not yet been graded,. // But on quizes this can also mean a quiz might have been started. // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items. // Since we want old results to be visible until a pending item was graded, we only use this state here. // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model. if ($gradeinfo->get_gradingscanner()->pending($userid)) { return completion::PENDING; } else { return completion::INCOMPLETE; } } else { $finalgrade = $grade->finalgrade; $scale = $gradeinfo->get_scale(); if ($gradeitem->gradepass > 0) { // Base completion off of gradepass (if set). if ($gradeitem->grademax > $gradeitem->gradepass && $finalgrade >= $gradeitem->grademax) { // If gradepass is configured . return completion::EXCELLENT; } else if ($finalgrade >= $gradeitem->gradepass) { return completion::COMPLETED; } else { return completion::PROGRESS; } } else { // Blind assumptions:. // Over 55% of range is completed. // Over 85% of range is excellent. $g = floatval($finalgrade - $gradeitem->grademin); $range = floatval($gradeitem->grademax - $gradeitem->grademin); $score = $g / $range; if ($score > 0.85) { return completion::EXCELLENT; } else if ($score > 0.55) { return completion::COMPLETED; } else { return completion::PROGRESS; } } } } }