218 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| // This file is part of the Studyplan plugin for Moodle
 | |
| //
 | |
| // 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/>.
 | |
| 
 | |
| /**
 | |
|  * 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 = 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;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 | 
