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   * Discrete values target.
  19   *
  20   * @package   core_analytics
  21   * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_analytics\local\target;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Discrete values target.
  31   *
  32   * @package   core_analytics
  33   * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  abstract class discrete extends base {
  37  
  38      /**
  39       * Are this target calculations linear values?
  40       *
  41       * @return bool
  42       */
  43      public function is_linear() {
  44          // Not supported yet.
  45          throw new \coding_exception('Sorry, this version\'s prediction processors only support targets with binary values.' .
  46              ' You can write your own and overwrite this method though.');
  47      }
  48  
  49      /**
  50       * Is the provided class one of this target valid classes?
  51       *
  52       * @param mixed $class
  53       * @return bool
  54       */
  55      protected static function is_a_class($class) {
  56          return (in_array($class, static::get_classes(), false));
  57      }
  58  
  59      /**
  60       * get_display_value
  61       *
  62       * @param float $value
  63       * @param string $ignoredsubtype
  64       * @return string
  65       */
  66      public function get_display_value($value, $ignoredsubtype = false) {
  67  
  68          if (!self::is_a_class($value)) {
  69              throw new \moodle_exception('errorpredictionformat', 'analytics');
  70          }
  71  
  72          // To discard any possible weird keys devs used.
  73          $classes = array_values(static::get_classes());
  74          $descriptions = array_values(static::classes_description());
  75  
  76          if (count($classes) !== count($descriptions)) {
  77              throw new \coding_exception('You need to describe all your classes (' . json_encode($classes) .
  78                  ') in self::classes_description');
  79          }
  80  
  81          $key = array_search($value, $classes);
  82          if ($key === false) {
  83              throw new \coding_exception('You need to describe all your classes (' . json_encode($classes) .
  84                  ') in self::classes_description');
  85          }
  86  
  87          return $descriptions[$key];
  88      }
  89  
  90      /**
  91       * get_calculation_outcome
  92       *
  93       * @param float $value
  94       * @param string $ignoredsubtype
  95       * @return int
  96       */
  97      public function get_calculation_outcome($value, $ignoredsubtype = false) {
  98  
  99          if (!self::is_a_class($value)) {
 100              throw new \moodle_exception('errorpredictionformat', 'analytics');
 101          }
 102  
 103          if (in_array($value, $this->ignored_predicted_classes(), false)) {
 104              // Just in case, if it is ignored the prediction should not even be recorded.
 105              return self::OUTCOME_OK;
 106          }
 107  
 108          debugging('Please overwrite \core_analytics\local\target\discrete::get_calculation_outcome, all your target ' .
 109              'classes are styled the same way otherwise', DEBUG_DEVELOPER);
 110          return self::OUTCOME_OK;
 111      }
 112  
 113      /**
 114       * Returns all the possible values the target calculation can return.
 115       *
 116       * Only useful for targets using discrete values, must be overwriten if it is the case.
 117       *
 118       * @return array
 119       */
 120      public static function get_classes() {
 121          // Coding exception as this will only be called if this target have non-linear values.
 122          throw new \coding_exception('Overwrite get_classes() and return an array with the different values the ' .
 123              'target calculation can return');
 124      }
 125  
 126      /**
 127       * Returns descriptions for each of the values the target calculation can return.
 128       *
 129       * The array indexes should match self::get_classes indexes.
 130       *
 131       * @return array
 132       */
 133      protected static function classes_description() {
 134          throw new \coding_exception('Overwrite classes_description() and return an array with a description for each of the ' .
 135              'different values the target calculation can return. Indexes should match self::get_classes indexes');
 136      }
 137  
 138      /**
 139       * Returns the predicted classes that will be ignored.
 140       *
 141       * Better be keen to add more than less classes here, the callback is always able to discard some classes. As an example
 142       * a target with classes 'grade 0-3', 'grade 3-6', 'grade 6-8' and 'grade 8-10' is interested in flagging both 'grade 6-8'
 143       * and 'grade 8-10' as ignored. On the other hand, a target like dropout risk with classes 'yes', 'no' may just be
 144       * interested in 'yes'.
 145       *
 146       * @return array List of values that will be ignored (array keys are ignored).
 147       */
 148      public function ignored_predicted_classes() {
 149          // Coding exception as this will only be called if this target have non-linear values.
 150          throw new \coding_exception('Overwrite ignored_predicted_classes() and return an array with the classes that should not ' .
 151              'trigger the callback');
 152      }
 153  
 154      /**
 155       * This method determines if a prediction is interesing for the model or not.
 156       *
 157       * This method internally calls ignored_predicted_classes to skip classes
 158       * flagged by the target as not important for users.
 159       *
 160       * @param mixed $predictedvalue
 161       * @param float $predictionscore
 162       * @return bool
 163       */
 164      public function triggers_callback($predictedvalue, $predictionscore) {
 165  
 166          if (!parent::triggers_callback($predictedvalue, $predictionscore)) {
 167              return false;
 168          }
 169  
 170          if (in_array($predictedvalue, $this->ignored_predicted_classes())) {
 171              return false;
 172          }
 173  
 174          return true;
 175      }
 176  }