Passed
Push — 1.11.x ( 1d3f0a...51bc2c )
by Yannick
09:54
created

ReadingComprehension::displayReading()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 13
rs 9.9666
1
0 ignored issues
show
Security Bug introduced by
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
    /**
55
     * Indicates how show the question list.
56
     * 1 = all in one page; 2 = one per page (default).
57
     *
58
     * @var int
59
     */
60
    private $exerciseType = 2;
61
    /**
62
     * All speeds (static $speeds + extra speeds defined in configuration.php as 'exercise_question_reading_comprehension_extra_speeds'
63
     *
64
     * @var array
65
     */
66
    public $allSpeeds = [];
67
68
    /**
69
     * Constructor.
70
     */
71
    public function __construct()
72
    {
73
        parent::__construct();
74
        $this->type = READING_COMPREHENSION;
75
        $this->isContent = $this->getIsContent();
76
        $customSpeeds = api_get_configuration_value('exercise_question_reading_comprehension_extra_speeds')['speeds'];
77
        if (!empty($customSpeeds) && is_array($customSpeeds)) {
78
            $this->allSpeeds = self::$speeds;
79
            foreach ($customSpeeds as $speed) {
80
                $this->allSpeeds[] = $speed;
81
            }
82
            asort($this->allSpeeds);
83
        }
84
    }
85
86
    public function processText($text)
87
    {
88
        // Refresh is set to 5s, but speed is in words per minute
89
        $wordsPerSecond = $this->allSpeeds[$this->level] / 60;
90
        $this->expectedWordsPerRefresh = intval($wordsPerSecond * $this->refreshTime);
91
92
        if (empty($text)) {
93
            // We have an issue here... how do we treat this case?
94
            // For now, let's define a default case
95
            $text = get_lang('NoExercise');
96
        }
97
        $words = str_word_count($text, 2, '0..9');
98
        $indexes = array_keys($words);
99
100
        $tagEnd = '</span>';
101
        $tagStart = $tagEnd.'<span class="text-highlight">';
102
        $this->wordsCount = count($words);
103
104
        $turns = ceil(
105
            $this->wordsCount / $this->expectedWordsPerRefresh
106
        );
107
108
        $firstIndex = $indexes[0];
109
110
        for ($i = 1; $i <= $turns; $i++) {
111
            $text = substr_replace($text, $tagStart, $firstIndex, 0);
112
113
            if ($i * $this->expectedWordsPerRefresh <= count($words)) {
114
                $newIndex = $i * $this->expectedWordsPerRefresh;
115
                if (isset($indexes[$newIndex])) {
116
                    $nextFirstIndex = $indexes[$newIndex];
117
                    $firstIndex = $nextFirstIndex + (strlen($tagStart) * $i);
118
                }
119
            }
120
        }
121
122
        $pos = strpos($text, $tagEnd);
123
124
        $text = substr_replace($text, '', $pos, strlen($tagEnd));
125
        $text .= $tagEnd;
126
127
        $this->displayReading($this->wordsCount, $turns, $text);
128
    }
129
130
    /**
131
     * Returns total count of words of the text to read.
132
     *
133
     * @return int
134
     */
135
    public function getWordsCount()
136
    {
137
        $words = str_word_count($this->selectDescription(), 2, '0..9');
138
        $this->wordsCount = count($words);
139
140
        return $this->wordsCount;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146
    public function createForm(&$form, $exercise)
147
    {
148
        // Categories
149
        $tabCat = TestCategory::getCategoriesIdAndName();
150
        $form->addSelect('questionCategory', get_lang('Category'), $tabCat);
151
        // Advanced parameters
152
        $levels = $this->getReadingSpeeds();
153
        $form->addSelect('questionLevel', get_lang('Difficulty'), $levels);
154
        $form->addElement('hidden', 'answerType', READING_COMPREHENSION);
155
        $form->addTextarea('questionDescription', get_lang('Text'), ['rows' => 20]);
156
        // question name
157
        if (api_get_configuration_value('save_titles_as_html')) {
158
            $editorConfig = ['ToolbarSet' => 'TitleAsHtml'];
159
            $form->addHtmlEditor(
160
                'questionName',
161
                get_lang('Question'),
162
                false,
163
                false,
164
                $editorConfig,
165
                true
166
            );
167
        } else {
168
            $form->addText('questionName', get_lang('Question'), false);
169
        }
170
171
        // hidden values
172
        $form->addRule('questionName', get_lang('GiveQuestion'), 'required');
173
        $isContent = isset($_REQUEST['isContent']) ? (int) $_REQUEST['isContent'] : null;
174
175
        // default values
176
        $defaults = [];
177
        $defaults['questionName'] = $this->question;
178
        $defaults['questionDescription'] = $this->description;
179
        $defaults['questionLevel'] = $this->level;
180
        $defaults['questionCategory'] = $this->category;
181
182
        // Came from he question pool
183
        if (isset($_GET['fromExercise'])) {
184
            $form->setDefaults($defaults);
185
        }
186
187
        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...
188
            $form->setDefaults($defaults);
189
        }
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public static function get_default_levels()
196
    {
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
     * @return array
210
     */
211
    public function getReadingSpeeds(): array
212
    {
213
        $defaultLevels = [];
214
        foreach ($this->allSpeeds as $i => $v) {
215
            $defaultLevels[$i] = sprintf(get_lang('ReadingComprehensionLevelX'), $this->allSpeeds[$i]);
216
        }
217
218
        return $defaultLevels;
219
    }
220
221
    /**
222
     * @param int $type
223
     *
224
     * @return ReadingComprehension
225
     */
226
    public function setExerciseType($type)
227
    {
228
        $this->exerciseType = (int) $type;
229
230
        return $this;
231
    }
232
233
    /**
234
     * @param $wordsCount
235
     * @param $turns
236
     * @param $text
237
     */
238
    private function displayReading($wordsCount, $turns, $text)
239
    {
240
        $view = new Template('', false, false, false, true, false, false);
241
242
        $template = $view->get_template('exercise/reading_comprehension.tpl');
243
244
        $view->assign('id', $this->iid);
245
        $view->assign('text', nl2br($text));
246
        $view->assign('words_count', $wordsCount);
247
        $view->assign('turns', $turns);
248
        $view->assign('refresh_time', $this->refreshTime);
249
        $view->assign('exercise_type', $this->exerciseType);
250
        $view->display($template);
251
    }
252
}
253