Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Getting the minimum grade to pass target.
  19   *
  20   * @package   core_course
  21   * @copyright 2019 Victor Deniz <victor@moodle.com>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_course\analytics\target;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  
  30  /**
  31   * Getting the minimum grade to pass target.
  32   *
  33   * @package   core_course
  34   * @copyright 2019 Victor Deniz <victor@moodle.com>
  35   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class course_gradetopass extends course_enrolments {
  38  
  39      /**
  40       * Courses grades to pass.
  41       * @var mixed[]
  42       */
  43      protected $coursesgradetopass = array();
  44  
  45      /**
  46       * Courses grades.
  47       * @var mixed[]
  48       */
  49      protected $coursesgrades = array();
  50  
  51      /**
  52       * Returns the grade to pass a course.
  53       *
  54       * Save the value in $coursesgradetopass array to prevent new accesses to the database.
  55       *
  56       * @param int $courseid The course id.
  57       * @return array The courseitem id and the required grade to pass the course.
  58       */
  59      protected function get_course_gradetopass($courseid) {
  60          if (!isset($this->coursesgradetopass[$courseid])) {
  61              // Get course grade_item.
  62              $courseitem = \grade_item::fetch_course_item($courseid);
  63  
  64              $ci = array();
  65              $ci['courseitemid'] = $courseitem->id;
  66  
  67              if ($courseitem->gradetype == GRADE_TYPE_VALUE && grade_floats_different($courseitem->gradepass, 0.0)) {
  68                  $ci['gradetopass'] = $courseitem->gradepass;
  69              } else {
  70                  $ci['gradetopass'] = null;
  71              }
  72              $this->coursesgradetopass[$courseid] = $ci;
  73          }
  74  
  75          return $this->coursesgradetopass[$courseid];
  76      }
  77  
  78      /**
  79       * Returns the grade of a user in a course.
  80       *
  81       * Saves the grades of all course users in $coursesgrades array to prevent new accesses to the database.
  82       *
  83       * @param  int $courseitemid The course item id.
  84       * @param  int $userid the user whose grade is requested.
  85       * @return array The courseitem id and the required grade to pass the course.
  86       */
  87      protected function get_user_grade($courseitemid, $userid) {
  88          // If the user grade for this course is not available, get all the grades for the course.
  89          if (!isset($this->coursesgrades[$courseitemid])) {
  90              // Ony a course is cached to avoid high memory usage.
  91              unset($this->coursesgrades);
  92              $gg = new \grade_grade(null, false);
  93              $usersgrades = $gg->fetch_all(array('itemid' => $courseitemid));
  94  
  95              if ($usersgrades) {
  96                  foreach ($usersgrades as $ug) {
  97                      $this->coursesgrades[$courseitemid][$ug->userid] = $ug->finalgrade;
  98                  }
  99              }
 100          }
 101  
 102          if (!isset($this->coursesgrades[$courseitemid][$userid])) {
 103              $this->coursesgrades[$courseitemid][$userid] = null;
 104          }
 105  
 106          return $this->coursesgrades[$courseitemid][$userid];
 107      }
 108  
 109      /**
 110       * Returns the name.
 111       *
 112       * If there is a corresponding '_help' string this will be shown as well.
 113       *
 114       * @return \lang_string
 115       */
 116      public static function get_name() : \lang_string {
 117          return new \lang_string('target:coursegradetopass', 'course');
 118      }
 119  
 120      /**
 121       * Returns descriptions for each of the values the target calculation can return.
 122       *
 123       * @return string[]
 124       */
 125      protected static function classes_description() {
 126          return array(
 127              get_string('targetlabelstudentgradetopassno', 'course'),
 128              get_string('targetlabelstudentgradetopassyes', 'course')
 129          );
 130      }
 131  
 132      /**
 133       * Discards courses that are not yet ready to be used for training or prediction.
 134       *
 135       * Only courses with "value" grade type and grade to pass set are valid.
 136       *
 137       * @param \core_analytics\analysable $course
 138       * @param bool $fortraining
 139       * @return true|string
 140       */
 141      public function is_valid_analysable(\core_analytics\analysable $course, $fortraining = true) {
 142          $isvalid = parent::is_valid_analysable($course, $fortraining);
 143  
 144          if (is_string($isvalid)) {
 145              return $isvalid;
 146          }
 147  
 148          $courseitem = $this->get_course_gradetopass ($course->get_id());
 149          if (is_null($courseitem['gradetopass'])) {
 150              return get_string('gradetopassnotset', 'course');
 151          }
 152  
 153          return true;
 154      }
 155  
 156      /**
 157       * The user's grade in the course sets the target value.
 158       *
 159       * @param int $sampleid
 160       * @param \core_analytics\analysable $course
 161       * @param int $starttime
 162       * @param int $endtime
 163       * @return float|null 0 -> course grade to pass achieved, 1 -> course grade to pass not achieved
 164       */
 165      protected function calculate_sample($sampleid, \core_analytics\analysable $course, $starttime = false, $endtime = false) {
 166  
 167          if (!$this->enrolment_active_during_analysis_time($sampleid, $starttime, $endtime)) {
 168              // We should not use this sample as the analysis results could be misleading.
 169              return null;
 170          }
 171  
 172          $userenrol = $this->retrieve('user_enrolments', $sampleid);
 173  
 174          // Get course grade to pass.
 175          $courseitem = $this->get_course_gradetopass($course->get_id());
 176  
 177          // Get the user grade.
 178          $usergrade = $this->get_user_grade($courseitem['courseitemid'], $userenrol->userid);
 179  
 180          if ($usergrade >= $courseitem['gradetopass']) {
 181              return 0;
 182          }
 183  
 184          return 1;
 185      }
 186  }