MDL-70374 qtype_multichoice: improve alignment of choices
[moodle.git] / question / classes / statistics / questions / calculated_question_summary.php
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/>.
17 /**
18  * Question statistics calculations class. Used in the quiz statistics report.
19  *
20  * @package    core_question
21  * @copyright  2018 Ryan Wyllie <ryan@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace core_question\statistics\questions;
26 defined('MOODLE_INTERNAL') || die();
28 /**
29  * Class calculated_question_summary
30  *
31  * This class is used to indicate the statistics for a random question slot should
32  * be rendered with a link to a summary of the displayed questions.
33  *
34  * It's used in the limited view of the statistics calculation in lieu of adding
35  * the stats for each subquestion individually.
36  *
37  * @copyright 2018 Ryan Wyllie <ryan@moodle.com>
38  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class calculated_question_summary extends calculated {
42     /**
43      * @var int only set immediately before display in the table. The order of display in the table.
44      */
45     public $subqdisplayorder;
47     /**
48      * @var calculated[] The instances storing the calculated stats of the questions that are being summarised.
49      */
50     protected $subqstats;
52     /**
53      * calculated_question_summary constructor.
54      *
55      * @param \stdClass $question
56      * @param int $slot
57      * @param calculated[] $subqstats The instances of the calculated stats of the questions that are being summarised.
58      */
59     public function __construct($question, $slot, $subqstats) {
60         parent::__construct($question, $slot);
62         $this->subqstats = $subqstats;
63         $this->subquestions = implode(',', array_column($subqstats, 'questionid'));
64     }
66     /**
67      * This is a summary stat so never breakdown by variant.
68      *
69      * @return bool
70      */
71     public function break_down_by_variant() {
72         return false;
73     }
75     /**
76      * Returns the minimum and maximum values of the given attribute in the summarised calculated stats.
77      *
78      * @param string $attribute The attribute that we are looking for its extremums.
79      * @return array An array of [min,max]
80      */
81     public function get_min_max_of($attribute) {
82         $getmethod = 'get_min_max_of_' . $attribute;
83         if (method_exists($this, $getmethod)) {
84             return $this->$getmethod();
85         } else {
86             $min = $max = null;
87             $set = false;
89             // We cannot simply use min or max functions because, in theory, some attributes might be non-scalar.
90             foreach (array_column($this->subqstats, $attribute) as $value) {
91                 if (is_scalar($value) || is_null($value)) {
92                     if (!$set) {    // It is not good enough to check if (!isset($min)),
93                                     // because $min might have been set to null in an earlier iteration.
94                         $min = $value;
95                         $max = $value;
96                         $set = true;
97                     }
99                     $min  = $this->min($min, $value);
100                     $max  = $this->max($max, $value);
101                 }
102             }
104             return [$min, $max];
105         }
106     }
108     /**
109      * Returns the minimum and maximum values of the standard deviation in the summarised calculated stats.
110      * @return array An array of [min,max]
111      */
112     protected function get_min_max_of_sd() {
113         $min = $max = null;
114         $set = false;
116         foreach ($this->subqstats as $subqstat) {
117             if (isset($subqstat->sd) && $subqstat->maxmark) {
118                 $value = $subqstat->sd / $subqstat->maxmark;
119             } else {
120                 $value = null;
121             }
123             if (!$set) {    // It is not good enough to check if (!isset($min)),
124                             // because $min might have been set to null in an earlier iteration.
125                 $min = $value;
126                 $max = $value;
127                 $set = true;
128             }
130             $min = $this->min($min, $value);
131             $max = $this->max($max, $value);
132         }
134         return [$min, $max];
135     }
137     /**
138      * Find higher value.
139      * A zero value is almost considered equal to zero in comparisons. The only difference is that when being compared to zero,
140      * zero is higher than null.
141      *
142      * @param float|null $value1
143      * @param float|null $value2
144      * @return float|null
145      */
146     protected function max(float $value1 = null, float $value2 = null) {
147         $temp1 = $value1 ?: 0;
148         $temp2 = $value2 ?: 0;
150         $tempmax = max($temp1, $temp2);
152         if (!$tempmax && $value1 !== 0 && $value2 !== 0) {
153             $max = null;
154         } else {
155             $max = $tempmax;
156         }
158         return $max;
159     }
161     /**
162      * Find lower value.
163      * A zero value is almost considered equal to zero in comparisons. The only difference is that when being compared to zero,
164      * zero is lower than null.
165      *
166      * @param float|null $value1
167      * @param float|null $value2
168      * @return mixed|null
169      */
170     protected function min(float $value1 = null, float $value2 = null) {
171         $temp1 = $value1 ?: 0;
172         $temp2 = $value2 ?: 0;
174         $tempmin = min($temp1, $temp2);
176         if (!$tempmin && $value1 !== 0 && $value2 !== 0) {
177             $min = null;
178         } else {
179             $min = $tempmin;
180         }
182         return $min;
183     }