moodle_local_treestudyplan/classes/local/aggregators/core_aggregator.php
2023-05-17 21:19:14 +02:00

245 lines
9.7 KiB
PHP

<?php
namespace local_treestudyplan\local\aggregators;
use \local_treestudyplan\courseinfo;
use \local_treestudyplan\corecompletioninfo;
use \local_treestudyplan\gradeinfo;
use \local_treestudyplan\studyitem;
use \local_treestudyplan\completion;
use \local_treestudyplan\debug;
class core_aggregator extends \local_treestudyplan\aggregator {
public const DEPRECATED = false;
private $accept_pending_as_submitted = False; // Also count ungraded but submitted
public function __construct($configstr) {
// allow public constructor for testing purposes
$this->initialize($configstr);
}
protected function initialize($configstr) {
// First initialize with the defaults
foreach(["accept_pending_as_submitted"] as $key){
$this->$key = boolval(get_config('local_treestudyplan', "bistate_{$key}"));
}
// Next, decode json
$config = \json_decode($configstr,true);
if(is_array($config)){
// copy all valid config settings to this item
foreach(["accept_pending_as_submitted"] as $key){
if(array_key_exists($key,$config)){
$this->$key = boolval($config[$key]);
}
}
} else {
debug::msg("ARRAY is NOT CONFIG","");
}
}
// Return active configuration model
public function config_string() {
return json_encode([
"accept_pending_as_submitted" => $this->accept_pending_as_submitted,
]);
}
public function needSelectGradables(){ return False;}
public function isDeprecated() { return self::DEPRECATED;}
public function useRequiredGrades() { return True;}
public function useItemConditions() { return False;}
public function usecorecompletioninfo() { 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 mixed $courseinfo
* @param mixed $studyitem
* @param mixed $userid
* @return void
*/
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
// Retrieve the core completion info from the core
$course = $courseinfo->course();
$completion = new \completion_info($course);
if ($completion->is_enabled() && $completion->is_tracked_user($userid)){
if($completion->is_course_complete($userid)){
// Now, the trick is to determine what constitutes excellent and good completion....
// TODO: Determine excellent and maybe good completion
// Option: Use course end grade to determine that...
// Probably needs a config value in the aggregator....
return completion::COMPLETED;
} else {
// Check if the course is over or not, if it is over, display failed
// Else, return PROGRESS
// Retrieve timing through courseinfo
$timing = courseinfo::coursetiming($course);
// Not met and time is passed, means FAILED
if($timing == "past"){
return completion::FAILED;
}
else {
// Check if any of the requirements are being met?
$completions = $completion->get_completions($userid);
foreach($completions as $c){
if($c->is_complete()){
// If so, return progress
return completion::PROGRESS;
}
}
return completion::INCOMPLETE;
}
}
}
else{
return completion::INCOMPLETE;
}
}
public function aggregate_junction(array $completion, studyitem $studyitem = null, $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
// First count all states
$statecount = completion::count_states($completion);
$total = count($completion);
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;
}
}
public function grade_completion(gradeinfo $gradeinfo, $userid) {
global $DB;
$table = "local_treestudyplan_gradecfg";
$gradeitem = $gradeinfo->getGradeitem();
$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->getGradingscanner()->pending($userid)){
return completion::PENDING;
} else {
return completion::INCOMPLETE;
}
}
else {
// first determine if we have a grade_config for this scale or this maximum grade
$finalgrade = $grade->finalgrade;
$scale = $gradeinfo->getScale();
if( isset($scale)){
$gradecfg = $DB->get_record($table,["scale_id"=>$scale->id]);
}
else if($gradeitem->grademin == 0)
{
$gradecfg = $DB->get_record($table,["grade_points"=>$gradeitem->grademax]);
}
else
{
$gradecfg = null;
}
// for point grades, a provided grade pass overrides the defaults in the gradeconfig
// for scales, the configuration in the gradeconfig is leading
if($gradecfg && (isset($scale) || $gradeitem->gradepass == 0))
{
// if so, we need to know if the grade is
if($finalgrade >= $gradecfg->min_completed){
// return completed if completed
return completion::COMPLETED;
}
else if($this->use_failed && $finalgrade < $gradecfg->min_progress)
{
// return failed if failed is enabled and the grade is less than the minimum grade for progress
return completion::FAILED;
}
else {
return completion::PROGRESS;
}
}
else if($gradeitem->gradepass > 0)
{
$range = floatval($gradeitem->grademax - $gradeitem->grademin);
// if no gradeconfig and gradepass is set, use that one to determine config.
if($finalgrade >= $gradeitem->gradepass){
return completion::COMPLETED;
}
else if($this->use_failed && $gradeitem->gradepass >= 3 && $range >= 3 && $finalgrade == 1)
{
// return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
return completion::FAILED;
}
else {
return completion::PROGRESS;
}
}
else {
// Blind assumptions if nothing is provided
// over 55% of range is completed
// if range >= 3 and failed is enabled, assume that this means failed
$g = floatval($finalgrade - $gradeitem->grademin);
$range = floatval($gradeitem->grademax - $gradeitem->grademin);
$score = $g / $range;
if($score > 0.55){
return completion::COMPLETED;
}
else if($this->use_failed && $range >= 3 && $finalgrade == 1)
{
// return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
return completion::FAILED;
}
else {
return completion::PROGRESS;
}
}
}
}
}