293 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
	
		
			11 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 with moodle course completion
 | |
|  * @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 core_competency\api as c_api;
 | |
| 
 | |
| use local_treestudyplan\courseinfo;
 | |
| use local_treestudyplan\gradeinfo;
 | |
| use local_treestudyplan\studyitem;
 | |
| use local_treestudyplan\completion;
 | |
| use local_treestudyplan\coursecompetencyinfo;
 | |
| 
 | |
| /**
 | |
|  * Aggregate course results with moodle course completion
 | |
|  */
 | |
| class competency_aggregator extends \local_treestudyplan\aggregator {
 | |
|     /** @var bool */
 | |
|     public const DEPRECATED = false;
 | |
|     /** @var stdClass */
 | |
|     private $agcfg = null;
 | |
| 
 | |
|     /**
 | |
|      * Retrieve or initialize current config object
 | |
|      * @return stdClass
 | |
|      */
 | |
|     private function cfg() {
 | |
|         if (empty($this->agcfg)) {
 | |
|             $this->agcfg = (object)[
 | |
|                 'thresh_completed' => 0.66,           // Minimum fraction that must be completed to aggregate as completed.
 | |
|                 'use_failed' => true,                 // Support failed completion yes/no.
 | |
|             ];
 | |
|         }
 | |
|         return $this->agcfg;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create new instance of aggregation method
 | |
|      * @param string $configstr Aggregation configuration string
 | |
|      */
 | |
|     public function __construct($configstr) {
 | |
|         // Allow public constructor for testing purposes.
 | |
|         $this->initialize($configstr);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Initialize the aggregation method
 | |
|      * @param string $configstr Aggregation configuration string
 | |
|      */
 | |
|     protected function initialize($configstr) {
 | |
|         // First initialize with the defaults.
 | |
|         foreach (["thresh_completed" ] as $key) {
 | |
|             $val = intval(get_config('local_treestudyplan', "competency_{$key}"));
 | |
|             if ($val >= 0 && $val <= 100) {
 | |
|                 $this->cfg()->$key = floatval($val) / 100;
 | |
|             }
 | |
|         }
 | |
|         foreach (["use_failed" ] as $key) {
 | |
|             $this->cfg()->$key = boolval(get_config('local_treestudyplan', "competency_{$key}"));
 | |
|         }
 | |
| 
 | |
|         // Next, decode json.
 | |
|         $config = \json_decode($configstr, true);
 | |
| 
 | |
|         if (is_array($config)) {
 | |
|             // Copy all valid config settings to this item.
 | |
|             foreach (["thresh_completed" ] as $key) {
 | |
|                 if (array_key_exists($key, $config)) {
 | |
|                     $val = $config[$key];
 | |
|                     if ($val >= 0 && $val <= 100) {
 | |
|                         $this->cfg()->$key = floatval($val) / 100;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             foreach (["use_failed" ] as $key) {
 | |
|                 if (array_key_exists($key, $config)) {
 | |
|                     $this->cfg()->$key = boolval($config[$key]);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the current configuration string.
 | |
|      * @return string Configuration string
 | |
|      */
 | |
|     public function config_string() {
 | |
|         return json_encode([
 | |
|             "thresh_completed" => 100 * $this->cfg()->thresh_completed,
 | |
|             "use_failed" => $this->cfg()->use_failed,
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if aggregation method wants to select gradables
 | |
|      * @return bool True if aggregation method needs gradables to be selected
 | |
|      */
 | |
|     public function select_gradables() {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if aggregation method is deprecated
 | |
|      * @return bool True if aggregation method is deprecated
 | |
|      */
 | |
|     public function deprecated() {
 | |
|         return self::DEPRECATED;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if Aggregation method makes use of "required grades" in a course/module.
 | |
|      * @return bool True if Aggregation method makes use of "required grades" in a course/module.
 | |
|      */
 | |
|     public function use_required_grades() {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if aggregation method makes use of required grades
 | |
|      * @return bool True if aggregation method makes use of
 | |
|      */
 | |
|     public function use_item_conditions() {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if the aggregation method uses course competencies,
 | |
|      * @return bool True if the aggregation method uses course competencies,
 | |
|      */
 | |
|     public function use_coursecompetencies() {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return course completion information based on the core completion infromation
 | |
|      * 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) {
 | |
|         // Retrieve the course competencies.
 | |
|         $course = $courseinfo->course();
 | |
|         $cci = new coursecompetencyinfo($course, $studyitem);
 | |
| 
 | |
|         $competencies = $cci->course_competencies();
 | |
|         if (count($competencies) == 0) {
 | |
|             return completion::INCOMPLETE;
 | |
|         }
 | |
|         $count = 0;
 | |
|         $courseproficient = 0;
 | |
|         $proficient = 0;
 | |
|         $requiredmet = 0;
 | |
|         $requiredcount = 0;
 | |
|         foreach ($competencies as $c) {
 | |
|             $count += 1;
 | |
|             $p = $cci->proficiency($c, $userid);
 | |
|             if ($p->proficient) {
 | |
|                 $proficient += 1;
 | |
|             }
 | |
|             if ($p->courseproficient) {
 | |
|                 $courseproficient += 1;
 | |
|             }
 | |
|             if ($cci->is_required(($c))) {
 | |
|                 $requiredcount += 1;
 | |
|                 if ($p->proficient) {
 | |
|                     $requiredmet += 1;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }
 | |
| 
 | |
|         // Determine minimum for.
 | |
|         $limit = $this->cfg()->thresh_completed * $count;
 | |
|         $coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
 | |
| 
 | |
|         if ($proficient >= $count && $requiredmet >= $requiredcount) {
 | |
|             if ($limit < $count) {
 | |
|                 return completion::EXCELLENT;
 | |
|             } else {
 | |
|                 return completion::COMPLETED;
 | |
|             }
 | |
|         } else if ($proficient > $limit && $requiredmet >= $requiredcount) {
 | |
|             return completion::COMPLETED;
 | |
|         } else if ($proficient > 0) {
 | |
|             if ($this->cfg()->use_failed && $coursefinished) {
 | |
|                 return completion::FAILED;
 | |
|             } else {
 | |
|                 return completion::PROGRESS;
 | |
|             }
 | |
|         } else {
 | |
|             if ($this->cfg()->use_failed && $coursefinished) {
 | |
|                 return completion::FAILED;
 | |
|             } else {
 | |
|                 return completion::INCOMPLETE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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 = 0) {
 | |
|         // Aggregate multiple incoming states into one junction or finish.
 | |
|         // Possible states:.
 | |
|         // - completion::EXCELLENT  - All incoming states are excellent.
 | |
|         // - completion::GOOD       - All incoming states are at least good.
 | |
|         // - completion::COMPLETED  - All incoming states are at least completed.
 | |
|         // - completion::FAILED     - All incoming states are failed.
 | |
|         // - completion::INCOMPLETE - All incoming states are incomplete.
 | |
|         // - completion::PROGRESS   - All other states.
 | |
| 
 | |
|         $method = strtoupper($studyitem->conditions()); // One of ANY or ALL.
 | |
| 
 | |
|         // First count all states.
 | |
|         $statecount = completion::count_states($completion);
 | |
|         $total = count($completion);
 | |
| 
 | |
|         if ($method == "ANY") {
 | |
|             if ($statecount[completion::EXCELLENT] >= 1) {
 | |
|                 return completion::EXCELLENT;
 | |
|             } else if ($statecount[completion::GOOD] >= 1) {
 | |
|                 return completion::GOOD;
 | |
|             } else if ($statecount[completion::COMPLETED] >= 1) {
 | |
|                 return completion::COMPLETED;
 | |
|             } else if ($statecount[completion::PROGRESS] >= 1) {
 | |
|                 return completion::PROGRESS;
 | |
|             } else if ($statecount[completion::FAILED] >= 1) {
 | |
|                 return completion::FAILED;
 | |
|             } else {
 | |
|                 return completion::INCOMPLETE;
 | |
|             }
 | |
|         } else { /* default value of ALL */
 | |
|             if ($total == $statecount[completion::EXCELLENT]) {
 | |
|                 return completion::EXCELLENT;
 | |
|             } else if ($total == ( $statecount[completion::EXCELLENT]
 | |
|                                 + $statecount[completion::GOOD]) ) {
 | |
|                 return completion::GOOD;
 | |
|             } else if ($total == ( $statecount[completion::EXCELLENT]
 | |
|                                 + $statecount[completion::GOOD]
 | |
|                                 + $statecount[completion::COMPLETED]) ) {
 | |
|                 return completion::COMPLETED;
 | |
|             } else if ($statecount[completion::FAILED]) {
 | |
|                 return completion::FAILED;
 | |
|             } else if ($total == $statecount[completion::INCOMPLETE]) {
 | |
|                 return completion::INCOMPLETE;
 | |
|             } else {
 | |
|                 return completion::PROGRESS;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * 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) {
 | |
|         // COURSE COMPETENCIES DOESN'T REALLY USE THIS FUNCTION.
 | |
|     }
 | |
| 
 | |
| }
 | 
