Issues (2128)

main/exercise/ReadingComprehension.php (2 issues)

1
0 ignored issues
show
It is not recommended to output anything before PHP's opening tag in non-template files.
Loading history...
2
<?php
3
4
/* For licensing terms, see /license.txt */
5
6
/**
7
 * Class ReadingComprehension.
8
 *
9
 * This class allows to instantiate an object of type READING_COMPREHENSION
10
 * extending the class question
11
 */
12
class ReadingComprehension extends UniqueAnswer
13
{
14
    public $typePicture = 'reading-comprehension.png';
15
    public $explanationLangVar = 'ReadingComprehension';
16
17
    /**
18
     * Defines the different speeds of scrolling for the reading window,
19
     * in words per minute. If 300 words text in 50w/m, then the moving
20
     * window will progress from top to bottom in 6 minutes.
21
     *
22
     * @var array
23
     */
24
    public static $speeds = [
25
        1 => 50,
26
        2 => 100,
27
        3 => 175,
28
        4 => 300,
29
        5 => 600,
30
    ];
31
32
    /**
33
     * The number of words in the question description (which serves as the
34
     * text to read).
35
     *
36
     * @var int
37
     */
38
    public $wordsCount = 0;
39
40
    /**
41
     * Number of words expected to show per refresh.
42
     *
43
     * @var int
44
     */
45
    public $expectedWordsPerRefresh = 0;
46
47
    /**
48
     * Refresh delay in seconds.
49
     *
50
     * @var int
51
     */
52
    public $refreshTime = 3;
53
    /**
54
     * All speeds (static $speeds + extra speeds defined in configuration.php as 'exercise_question_reading_comprehension_extra_speeds'.
55
     *
56
     * @var array
57
     */
58
    public $allSpeeds = [];
59
60
    /**
61
     * Indicates how show the question list.
62
     * 1 = all in one page; 2 = one per page (default).
63
     *
64
     * @var int
65
     */
66
    private $exerciseType = 2;
67
68
    /**
69
     * Constructor.
70
     */
71
    public function __construct()
72
    {
73
        parent::__construct();
74
        $this->type = READING_COMPREHENSION;
75
        $this->isContent = $this->getIsContent();
76
        $extraSpeeds = api_get_configuration_value('exercise_question_reading_comprehension_extra_speeds');
77
        $customSpeeds = $extraSpeeds['speeds'] ?? [];
78
        $this->allSpeeds = self::$speeds;
79
        if (!empty($customSpeeds) && is_array($customSpeeds)) {
80
            foreach ($customSpeeds as $speed) {
81
                $this->allSpeeds[] = $speed;
82
            }
83
            asort($this->allSpeeds);
84
        }
85
    }
86
87
    public function processText($text)
88
    {
89
        // Refresh is set to 5s, but speed is in words per minute
90
        $wordsPerSecond = $this->allSpeeds[$this->level] / 60;
91
        $this->expectedWordsPerRefresh = intval($wordsPerSecond * $this->refreshTime);
92
93
        if (empty($text)) {
94
            // We have an issue here... how do we treat this case?
95
            // For now, let's define a default case
96
            $text = get_lang('NoExercise');
97
        }
98
        $words = str_word_count($text, 2, '0..9');
99
        $indexes = array_keys($words);
100
101
        $tagEnd = '</span>';
102
        $tagStart = $tagEnd.'<span class="text-highlight">';
103
        $this->wordsCount = count($words);
104
105
        $turns = ceil(
106
            $this->wordsCount / $this->expectedWordsPerRefresh
107
        );
108
109
        $firstIndex = $indexes[0];
110
111
        for ($i = 1; $i <= $turns; $i++) {
112
            $text = substr_replace($text, $tagStart, $firstIndex, 0);
113
114
            if ($i * $this->expectedWordsPerRefresh <= count($words)) {
115
                $newIndex = $i * $this->expectedWordsPerRefresh;
116
                if (isset($indexes[$newIndex])) {
117
                    $nextFirstIndex = $indexes[$newIndex];
118
                    $firstIndex = $nextFirstIndex + (strlen($tagStart) * $i);
119
                }
120
            }
121
        }
122
123
        $pos = strpos($text, $tagEnd);
124
125
        $text = substr_replace($text, '', $pos, strlen($tagEnd));
126
        $text .= $tagEnd;
127
128
        $this->displayReading($this->wordsCount, $turns, $text);
129
    }
130
131
    /**
132
     * Returns total count of words of the text to read.
133
     *
134
     * @return int
135
     */
136
    public function getWordsCount()
137
    {
138
        $words = str_word_count($this->selectDescription(), 2, '0..9');
139
        $this->wordsCount = count($words);
140
141
        return $this->wordsCount;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function createForm(&$form, $exercise)
148
    {
149
        // Categories
150
        $tabCat = TestCategory::getCategoriesIdAndName();
151
        $form->addSelect('questionCategory', get_lang('Category'), $tabCat);
152
        // Advanced parameters
153
        $levels = $this->getReadingSpeeds();
154
        $form->addSelect('questionLevel', get_lang('Difficulty'), $levels);
155
        $form->addElement('hidden', 'answerType', READING_COMPREHENSION);
156
        $form->addTextarea('questionDescription', get_lang('Text'), ['rows' => 20]);
157
        // question name
158
        if (api_get_configuration_value('save_titles_as_html')) {
159
            $editorConfig = ['ToolbarSet' => 'TitleAsHtml'];
160
            $form->addHtmlEditor(
161
                'questionName',
162
                get_lang('Question'),
163
                false,
164
                false,
165
                $editorConfig,
166
                true
167
            );
168
        } else {
169
            $form->addText('questionName', get_lang('Question'), false);
170
        }
171
172
        // hidden values
173
        $form->addRule('questionName', get_lang('GiveQuestion'), 'required');
174
        $isContent = isset($_REQUEST['isContent']) ? (int) $_REQUEST['isContent'] : null;
175
176
        // default values
177
        $defaults = [];
178
        $defaults['questionName'] = $this->question;
179
        $defaults['questionDescription'] = $this->description;
180
        $defaults['questionLevel'] = $this->level;
181
        $defaults['questionCategory'] = $this->category;
182
183
        // Came from he question pool
184
        if (isset($_GET['fromExercise'])) {
185
            $form->setDefaults($defaults);
186
        }
187
188
        if (!isset($_GET['newQuestion']) || $isContent) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $isContent of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
189
            $form->setDefaults($defaults);
190
        }
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public static function get_default_levels()
197
    {
198
        return [
199
            1 => sprintf(get_lang('ReadingComprehensionLevelX'), self::$speeds[1]),
200
            2 => sprintf(get_lang('ReadingComprehensionLevelX'), self::$speeds[2]),
201
            3 => sprintf(get_lang('ReadingComprehensionLevelX'), self::$speeds[3]),
202
            4 => sprintf(get_lang('ReadingComprehensionLevelX'), self::$speeds[4]),
203
            5 => sprintf(get_lang('ReadingComprehensionLevelX'), self::$speeds[5]),
204
        ];
205
    }
206
207
    /**
208
     * Return the augmented speeds (using, if defined, the 'exercise_question_reading_comprehension_extra_speeds' setting.
209
     */
210
    public function getReadingSpeeds(): array
211
    {
212
        $defaultLevels = [];
213
        foreach ($this->allSpeeds as $i => $v) {
214
            $defaultLevels[$i] = sprintf(get_lang('ReadingComprehensionLevelX'), $this->allSpeeds[$i]);
215
        }
216
217
        return $defaultLevels;
218
    }
219
220
    /**
221
     * @param int $type
222
     *
223
     * @return ReadingComprehension
224
     */
225
    public function setExerciseType($type)
226
    {
227
        $this->exerciseType = (int) $type;
228
229
        return $this;
230
    }
231
232
    /**
233
     * @param $wordsCount
234
     * @param $turns
235
     * @param $text
236
     */
237
    private function displayReading($wordsCount, $turns, $text)
238
    {
239
        $view = new Template('', false, false, false, true, false, false);
240
241
        $template = $view->get_template('exercise/reading_comprehension.tpl');
242
243
        $view->assign('id', $this->iid);
244
        $view->assign('text', nl2br($text));
245
        $view->assign('words_count', $wordsCount);
246
        $view->assign('turns', $turns);
247
        $view->assign('refresh_time', $this->refreshTime);
248
        $view->assign('exercise_type', $this->exerciseType);
249
        $view->display($template);
250
    }
251
}
252