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.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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   * Single insight view page.
  19   *
  20   * @package    report_insights
  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 report_insights\output;
  26  
  27  use core_analytics\prediction;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  /**
  32   * Single insight view page.
  33   *
  34   * @package    report_insights
  35   * @copyright  2017 David Monllao {@link http://www.davidmonllao.com}
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class insight implements \renderable, \templatable {
  39  
  40      /**
  41       * @var \core_analytics\model
  42       */
  43      protected $model;
  44  
  45      /**
  46       * @var \core_analytics\prediction
  47       */
  48      protected $prediction;
  49  
  50      /**
  51       * @var bool
  52       */
  53      protected $includedetailsaction = false;
  54  
  55      /**
  56       * @var \context
  57       */
  58      protected $context;
  59  
  60      /**
  61       * Constructor
  62       *
  63       * @param \core_analytics\prediction $prediction
  64       * @param \core_analytics\model $model
  65       * @param bool $includedetailsaction
  66       * @param \context $context
  67       * @return void
  68       */
  69      public function __construct(\core_analytics\prediction $prediction, \core_analytics\model $model, $includedetailsaction,
  70              \context $context) {
  71  
  72          $this->prediction = $prediction;
  73          $this->model = $model;
  74          $this->includedetailsaction = $includedetailsaction;
  75          $this->context = $context;
  76      }
  77  
  78      /**
  79       * Exports the data.
  80       *
  81       * @param \renderer_base $output
  82       * @return \stdClass
  83       */
  84      public function export_for_template(\renderer_base $output) {
  85          // Get the prediction data.
  86          $predictiondata = $this->prediction->get_prediction_data();
  87  
  88          $target = $this->model->get_target();
  89  
  90          $data = new \stdClass();
  91          $data->modelid = $this->model->get_id();
  92          $data->contextid = $this->context->id;
  93          $data->predictionid = $predictiondata->id;
  94  
  95          $targetname = $target->get_name();
  96          $data->insightname = format_string($targetname);
  97  
  98          $targetinfostr = $targetname->get_identifier() . 'info';
  99          if (get_string_manager()->string_exists($targetinfostr, $targetname->get_component())) {
 100              $data->insightdescription = get_string($targetinfostr, $targetname->get_component());
 101          }
 102  
 103          $data->showpredictionheading = true;
 104          if (!$target->is_linear()) {
 105              $nclasses = count($target::get_classes());
 106              $nignoredclasses = count($target->ignored_predicted_classes());
 107              if ($nclasses - $nignoredclasses <= 1) {
 108                  // Hide the prediction heading if there is only 1 class displayed. Otherwise it is redundant with the insight name.
 109                  $data->showpredictionheading = false;
 110              }
 111          }
 112  
 113          // Get the details.
 114          $data->timecreated = userdate($predictiondata->timecreated);
 115          $data->timerange = '';
 116  
 117          if (!empty($predictiondata->timestart) && !empty($predictiondata->timeend)) {
 118              $timerange = new \stdClass();
 119              $timerange->timestart = userdate($predictiondata->timestart);
 120              $timerange->timeend = userdate($predictiondata->timeend);
 121              $data->timerange = get_string('timerangewithdata', 'report_insights', $timerange);
 122          }
 123  
 124          // Sample info (determined by the analyser).
 125          list($data->sampledescription, $samplerenderable) = $this->model->prediction_sample_description($this->prediction);
 126  
 127          // Sampleimage is a renderable we should pass it to HTML.
 128          if ($samplerenderable) {
 129              $data->sampleimage = $output->render($samplerenderable);
 130          }
 131  
 132          // Prediction info.
 133          $predictedvalue = $predictiondata->prediction;
 134          $data->predictiondisplayvalue = $target->get_display_value($predictedvalue);
 135          list($data->style, $data->outcomeicon) = self::get_calculation_display($target,
 136              floatval($predictedvalue), $output);
 137  
 138          $data->actions = actions_exporter::add_prediction_actions($target, $output, $this->prediction,
 139              $this->includedetailsaction);
 140          $data->bulkactions = actions_exporter::add_bulk_actions($target, $output, [$this->prediction], $this->context);
 141  
 142          // Calculated indicators values.
 143          $data->calculations = array();
 144          $calculations = $this->prediction->get_calculations();
 145          foreach ($calculations as $calculation) {
 146  
 147              // Hook for indicators with extra features that should not be displayed (e.g. discrete indicators).
 148              if (!$calculation->indicator->should_be_displayed($calculation->value, $calculation->subtype)) {
 149                  continue;
 150              }
 151  
 152              if ($calculation->value === null) {
 153                  // We don't show values that could not be calculated.
 154                  continue;
 155              }
 156  
 157              $obj = new \stdClass();
 158              $obj->name = call_user_func(array($calculation->indicator, 'get_name'));
 159              $obj->displayvalue = $calculation->indicator->get_display_value($calculation->value, $calculation->subtype);
 160              list($obj->style, $obj->outcomeicon) = self::get_calculation_display($calculation->indicator,
 161                  floatval($calculation->value), $output, $calculation->subtype);
 162  
 163              $identifier = $calculation->indicator->get_name()->get_identifier() . 'def';
 164              $component = $calculation->indicator->get_name()->get_component();
 165              if (get_string_manager()->string_exists($identifier, $component)) {
 166                  $obj->outcomehelp = (new \help_icon($identifier, $component))->export_for_template($output);
 167              }
 168              $data->calculations[] = $obj;
 169          }
 170  
 171          if (empty($data->calculations)) {
 172              $data->nocalculations = (object)array(
 173                  'message' => get_string('nodetailsavailable', 'report_insights'),
 174                  'closebutton' => false
 175              );
 176          }
 177  
 178          // This is only rendered in report_insights/insight_details template for predictions with no action.
 179          // We need it to automatically enable the bulk action buttons in report/insights/prediction.php.
 180          $filtered = [
 181              \core_analytics\prediction::ACTION_FIXED,
 182              \core_analytics\prediction::ACTION_NOT_USEFUL,
 183              \core_analytics\prediction::ACTION_USEFUL,
 184              \core_analytics\prediction::ACTION_NOT_APPLICABLE,
 185              \core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED,
 186          ];
 187          if (!$this->prediction->get_executed_actions($filtered)) {
 188              $toggleall = new \core\output\checkbox_toggleall('insight-bulk-action-' . $predictedvalue, true, [
 189                  'id' => 'id-toggle-all-' . $predictedvalue,
 190                  'name' => 'toggle-all-' . $predictedvalue,
 191                  'classes' => 'hidden',
 192                  'label' => get_string('selectall'),
 193                  'labelclasses' => 'sr-only',
 194                  'checked' => false,
 195              ]);
 196              $data->hiddencheckboxtoggleall = $output->render($toggleall);
 197  
 198              $toggle = new \core\output\checkbox_toggleall('insight-bulk-action-' . $predictedvalue, false, [
 199                  'id' => 'id-select-' . $data->predictionid,
 200                  'name' => 'select-' . $data->predictionid,
 201                  'label' => get_string('selectprediction', 'report_insights', $data->sampledescription),
 202                  'labelclasses' => 'accesshide',
 203              ]);
 204              $data->toggleslave = $output->render($toggle);
 205          }
 206  
 207          return $data;
 208      }
 209  
 210      /**
 211       * Returns display info for the calculated value outcome.
 212       *
 213       * @param \core_analytics\calculable $calculable
 214       * @param float $value
 215       * @param \renderer_base $output
 216       * @param string|false $subtype
 217       * @return array The style as 'success', 'info', 'warning' or 'danger' and pix_icon
 218       */
 219      public static function get_calculation_display(\core_analytics\calculable $calculable, $value, $output, $subtype = false) {
 220          $outcome = $calculable->get_calculation_outcome($value, $subtype);
 221          switch ($outcome) {
 222              case \core_analytics\calculable::OUTCOME_NEUTRAL:
 223                  $style = '';
 224                  $text = get_string('outcomeneutral', 'report_insights');
 225                  $icon = 't/check';
 226                  break;
 227              case \core_analytics\calculable::OUTCOME_VERY_POSITIVE:
 228                  $style = 'success';
 229                  $text = get_string('outcomeverypositive', 'report_insights');
 230                  $icon = 't/approve';
 231                  break;
 232              case \core_analytics\calculable::OUTCOME_OK:
 233                  $style = 'info';
 234                  $text = get_string('outcomeok', 'report_insights');
 235                  $icon = 't/check';
 236                  break;
 237              case \core_analytics\calculable::OUTCOME_NEGATIVE:
 238                  $style = 'warning';
 239                  $text = get_string('outcomenegative', 'report_insights');
 240                  $icon = 'i/warning';
 241                  break;
 242              case \core_analytics\calculable::OUTCOME_VERY_NEGATIVE:
 243                  $style = 'danger';
 244                  $text = get_string('outcomeverynegative', 'report_insights');
 245                  $icon = 'i/warning';
 246                  break;
 247              default:
 248                  throw new \coding_exception('The outcome returned by ' . get_class($calculable) . '::get_calculation_outcome is ' .
 249                      'not one of the accepted values. Please use \core_analytics\calculable::OUTCOME_VERY_POSITIVE, ' .
 250                      '\core_analytics\calculable::OUTCOME_OK, \core_analytics\calculable::OUTCOME_NEGATIVE, ' .
 251                      '\core_analytics\calculable::OUTCOME_VERY_NEGATIVE or \core_analytics\calculable::OUTCOME_NEUTRAL');
 252          }
 253          $icon = new \pix_icon($icon, $text);
 254          return array($style, $icon->export_for_template($output));
 255      }
 256  
 257      /**
 258       * Model getter.
 259       *
 260       * @return \core_analytics\model
 261       */
 262      public function get_model(): \core_analytics\model {
 263          return $this->model;
 264      }
 265  }