forked from Anastasiya-Potseluico/evol-git
-
Notifications
You must be signed in to change notification settings - Fork 1
/
abstract_analyzer.php
250 lines (222 loc) · 9.23 KB
/
abstract_analyzer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<?php
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/question/type/correctwriting/string_pair.php');
abstract class qtype_correctwriting_abstract_analyzer {
/**
* A reference to the question object with necessary data (language id, answers, threshold etc).
* @var qtype_correctwriting_question
*/
protected $question;
/**
* Language object - contains scaner, parser etc.
* @var block_formal_langs_abstract_language child classes.
*/
protected $language;
/**
* String pair, passed as input data for the analyzer.
* @var qtype_correctwriting_string_pair
*/
protected $basestringpair;
/**
* Best (judging by fitness) string pairs generated as result of analyzer's work.
*
* Analyzer should return several string pairs only if they are equivalent from it's point of view.
* An empty array means error, that don't allow subsequent analyzers to work.
* @var array of qtype_correctwriting_string_pair
*/
protected $resultstringpairs = array();
/**
* Returns analyzer internal name, which can be used as an argument to get_string().
*/
abstract public function name();
/**
* Do all processing and fill resultstringpairs and resultmistakes fields.
*
* You are normally don't want to overload it. Overload analyze() and bypass() instead.
* Passed responsestring could be null, than object used just to find errors in the answers, token count etc...
* When called without params just creates empty object to call analyzer-dependent functions on.
* @throws moodle_exception if invalid number of string pairs
* @param qtype_correctwriting_question $question
* @param qtype_correctwriting_string_pair $basepair a pair, passed as input
* @param block_formal_langs_abstract_language $language a language
* @param bool $bypass false if analyzer should work, true if it should just allow subsequent analyzers to work.
*/
public function __construct($question = null, $basepair = null, $language = null, $bypass = true) {
if ($question === null) {
return;
}
$this->question = $question;
$this->language = $language;
$this->basestringpair = $basepair;
if ($bypass) {
$this->bypass();
} else {
$this->analyze();
if (count($this->resultstringpairs) == 0) {
throw new moodle_exception('There must be at least one output pair in '
. get_string($this->name(), 'qtype_correctwriting')); // TODO - make a language string and normal exception.
}
}
}
/**
* Do real analyzing and fill resultstringpairs and resultmistakes fields.
*
* Passed responsestring could be null, than object used just to find errors in the answers, token count etc...
*/
abstract protected function analyze();
/**
* Fill resultstringpairs with a string pair, that simulates work of this analyzer allowing subsequent analyzers to work.
*
* You are normally would overload this, starting overload with parent function call, then add you work.
* Don't actually analyze something, no mistakes generated: just fill necessary fields in string pair.
*/
protected function bypass() {
$this->resultstringpairs[] = clone $this->basestringpair; //Clone string pair for future use.
}
/**
* Returns resulting string pairs array.
*/
public function result_pairs() {
return $this->resultstringpairs;
}
/**
* Returns a mistake type for a error, used by this analyzer
* @return string
*/
protected function own_mistake_type() {
return 'qtype_correctwriting_response_mistake';
}
/**
* Returns fitness as aggregate measure of how students response fits this particular answer - i.e. more fitness = less mistakes.
* Used to choose best matched answer.
* Fitness is negative or zero (no errors, full match).
* Fitness doesn't necessary equivalent to the number of mistakes as each mistake could have different weight.
* Each analyzer will calculate fitness only for it's own mistakes, ignoring mistakes from other analyzers.
* Dev. comment: since all mistakes have weight, we can have common algorithm as reduction operation
* on this mistakes.
* @param array $mistakes of qtype_correctwriting_response_mistake child classes $mistakes Mistakes to calculate fitness from, can be empty array.
* @return double
*/
public function fitness($mistakes) {
$result = 0;
$mytype = $this->own_mistake_type();
if (count($mistakes)) {
/** qtype_correctwriting_response_mistake $mistake */
foreach($mistakes as $mistake) {
if (is_a($mistake, $mytype)) {
$result += $mistake->weight;
}
}
}
return $result * -1;
}
/**
* Returns an array of hint keys, supported by mistakes from this analyzer.
*/
abstract public function supported_hints();
// Question editing form and DB methods starts there.
/**
* Returns an array of extra_question_fields used by this analyzer.
*/
public function extra_question_fields() {
return array();
}
/**
* Returns array of floating point fields for the form. Subsequent commentaries comments keys:
* 'name' => field name, there should be label as get_string('name', 'qtype_correctwriting') and help as get_string('name_help', 'qtype_correctwriting')
* 'default' => default value for the form field
* 'advanced' => boolean value - whether field is advanced one
* 'min', 'max' => limits for the field value
*/
public function float_form_fields() {
return array();
}
/**
* Called from edit_correctwriting_form::definition_inner() within form section for this analyzer.
* You will typically call parent, then add other fields.
* @param MoodleQuickForm $mform
*/
public function form_section_definition(&$mform) {
foreach ($this->float_form_fields() as $params) {
$mform->addElement('text', $params['name'], get_string($params['name'], 'qtype_correctwriting'), array('size' => 6));
$mform->setType($params['name'], PARAM_FLOAT);
$mform->setDefault($params['name'], $params['default']);
if ($params['required']) {
$mform->addRule($params['name'], null, 'required', null, 'client');
}
$mform->addHelpButton($params['name'], $params['name'], 'qtype_correctwriting');
if ($params['advanced']) {
$mform->setAdvanced($params['name']);
}
}
}
/**
* Called from edit_correctwriting_form::data_preprocessing
*/
public function form_section_data_preprocessing($question) {
return $question;
}
/**
* Called from edit_correctwriting_form::validation
*/
public function form_section_validation ($data, $files) {
$errors = array();
return $errors;
}
/**
* If this analyzer requires some other ones to work, not bypass - return an array of such analyzers names.
*/
public function require_analyzers() {
return array();
}
/**
* Returns if the language is compatible with this analyzer.
* I.e. syntax analyzer compatible only with parser containing languages.
* @param block_formal_langs_abstract_language $lang a language object from block_formal_langs
* @return boolean
*/
public function is_lang_compatible($lang) {
return true; // Accept all by default.
}
/**
* Allows analyzer to replace mistakes from other analyzer.
* For example syntax_analyzer can replace mistakes from sequence_analyzer.
*
* Types of mistakes should be matched against other with replaces_mistake_types.
* @return array
*/
protected function replaces_mistake_types() {
return array();
}
/**
* Whether we should filter mistake from list of mistakes.
* Called if replaces_mistake_types returns one mistake
* @param qtype_correctwriting_response_mistake $mistake
* @return boolean
*/
protected function should_mistake_be_removed($mistake) {
return false;
}
/**
* Analyzer can use this function to filter out all mistakes, thst it does not need
* @param qtype_correctwriting_string_pair $pair a string pair
* @return array of qtype_correctwriting_response_mistake a mistake set
*/
protected function remove_mistake_types_from_mistake_set($pair) {
$types = $this->replaces_mistake_types();
if (count($types)) {
foreach($types as $type) {
$mistakes = $pair->mistakes_by_type($type);
$resultmistakes = array();
if (count($mistakes)) {
foreach($mistakes as $mistake) {
if ($this->should_mistake_be_removed($mistake) == false) {
$resultmistakes[] = $mistake;
}
}
}
$pair->set_mistakes_by_type($type, $resultmistakes);
}
}
}
}