MDL-70374 qtype_multichoice: improve alignment of choices
[moodle.git] / question / classes / statistics / questions / calculated_question_summary.php
CommitLineData
29cc5507
RW
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 * 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 */
24
25namespace core_question\statistics\questions;
26defined('MOODLE_INTERNAL') || die();
27
28/**
84140b91 29 * Class calculated_question_summary
29cc5507
RW
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 */
84140b91 40class calculated_question_summary extends calculated {
29cc5507
RW
41
42 /**
43 * @var int only set immediately before display in the table. The order of display in the table.
44 */
45 public $subqdisplayorder;
46
d79fef2a
SR
47 /**
48 * @var calculated[] The instances storing the calculated stats of the questions that are being summarised.
49 */
50 protected $subqstats;
51
52 /**
84140b91 53 * calculated_question_summary constructor.
d79fef2a
SR
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);
61
62 $this->subqstats = $subqstats;
63 $this->subquestions = implode(',', array_column($subqstats, 'questionid'));
64 }
65
29cc5507
RW
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 }
d79fef2a
SR
74
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;
f4bc5587 87 $set = false;
d79fef2a
SR
88
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) {
f4bc5587
SR
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.
d79fef2a 94 $min = $value;
d79fef2a 95 $max = $value;
f4bc5587 96 $set = true;
d79fef2a
SR
97 }
98
f4bc5587
SR
99 $min = $this->min($min, $value);
100 $max = $this->max($max, $value);
d79fef2a
SR
101 }
102 }
103
104 return [$min, $max];
105 }
106 }
107
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;
f4bc5587 114 $set = false;
d79fef2a
SR
115
116 foreach ($this->subqstats as $subqstat) {
117 if (isset($subqstat->sd) && $subqstat->maxmark) {
f4bc5587
SR
118 $value = $subqstat->sd / $subqstat->maxmark;
119 } else {
120 $value = null;
121 }
d79fef2a 122
f4bc5587
SR
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;
d79fef2a 128 }
f4bc5587
SR
129
130 $min = $this->min($min, $value);
131 $max = $this->max($max, $value);
d79fef2a
SR
132 }
133
134 return [$min, $max];
135 }
f4bc5587
SR
136
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;
149
150 $tempmax = max($temp1, $temp2);
151
152 if (!$tempmax && $value1 !== 0 && $value2 !== 0) {
153 $max = null;
154 } else {
155 $max = $tempmax;
156 }
157
158 return $max;
159 }
160
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;
173
174 $tempmin = min($temp1, $temp2);
175
176 if (!$tempmin && $value1 !== 0 && $value2 !== 0) {
177 $min = null;
178 } else {
179 $min = $tempmin;
180 }
181
182 return $min;
183 }
29cc5507 184}