@@ -59,7 +59,7 @@ class qtype_drawlines_question extends question_graded_automatically {
59
59
public function get_expected_data () {
60
60
$ expecteddata = [];
61
61
foreach ($ this ->lines as $ line ) {
62
- $ expecteddata [$ this ->choice ($ line ->number - 1 )] = PARAM_RAW ;
62
+ $ expecteddata [$ this ->field ($ line ->number - 1 )] = PARAM_RAW ;
63
63
}
64
64
return $ expecteddata ;
65
65
}
@@ -75,8 +75,8 @@ public function is_complete_response(array $response): bool {
75
75
return false ;
76
76
}
77
77
foreach ($ this ->lines as $ key => $ line ) {
78
- if (isset ($ response [$ this ->choice ($ key )]) &&
79
- !line::are_response_coordinates_valid ($ response [$ this ->choice ($ key )], $ line ->type )) {
78
+ if (isset ($ response [$ this ->field ($ key )]) &&
79
+ !line::are_response_coordinates_valid ($ response [$ this ->field ($ key )], $ line ->type )) {
80
80
return false ;
81
81
}
82
82
}
@@ -87,7 +87,7 @@ public function is_complete_response(array $response): bool {
87
87
public function get_correct_response () {
88
88
$ response = [];
89
89
foreach ($ this ->lines as $ key => $ line ) {
90
- $ response [$ this ->choice ($ key )] = line::get_coordinates ($ line ->zonestart ) . ' '
90
+ $ response [$ this ->field ($ key )] = line::get_coordinates ($ line ->zonestart ) . ' '
91
91
. line::get_coordinates ($ line ->zoneend );
92
92
}
93
93
return $ response ;
@@ -98,14 +98,14 @@ public function summarise_response(array $response): ?string {
98
98
$ responsewords = [];
99
99
$ answers = [];
100
100
foreach ($ this ->lines as $ key => $ line ) {
101
- if (array_key_exists ($ this ->choice ($ key ), $ response ) && $ response [$ this ->choice ($ key )] != '' ) {
102
- $ coordinates = explode (' ' , $ response [$ this ->choice ($ key )]);
101
+ if (array_key_exists ($ this ->field ($ key ), $ response ) && $ response [$ this ->field ($ key )] != '' ) {
102
+ $ coordinates = explode (' ' , $ response [$ this ->field ($ key )]);
103
103
if ($ line ->type == 'lineinfinite ' && count ($ coordinates ) == 4 ) {
104
- $ coordinates = explode (' ' , $ response [$ this ->choice ($ key )]);
104
+ $ coordinates = explode (' ' , $ response [$ this ->field ($ key )]);
105
105
$ answers [] = 'Line ' . $ line ->number . ': ' . $ coordinates [1 ] . ' ' . $ coordinates [2 ];
106
106
continue ;
107
107
}
108
- $ answers [] = 'Line ' . $ line ->number . ': ' . $ response [$ this ->choice ($ key )];
108
+ $ answers [] = 'Line ' . $ line ->number . ': ' . $ response [$ this ->field ($ key )];
109
109
}
110
110
}
111
111
if (count ($ answers ) > 0 ) {
@@ -115,9 +115,12 @@ public function summarise_response(array $response): ?string {
115
115
}
116
116
117
117
#[\Override]
118
- public function is_gradable_response (array $ response ) {
118
+ public function is_gradable_response (array $ response ): bool {
119
+ if (!isset ($ response )) {
120
+ return false ;
121
+ }
119
122
foreach ($ this ->lines as $ key => $ line ) {
120
- if (array_key_exists ($ this ->choice ($ key ), $ response )) {
123
+ if (array_key_exists ($ this ->field ($ key ), $ response )) {
121
124
return true ;
122
125
}
123
126
}
@@ -127,75 +130,79 @@ public function is_gradable_response(array $response) {
127
130
#[\Override]
128
131
public function is_same_response (array $ prevresponse , array $ newresponse ) {
129
132
foreach ($ this ->lines as $ key => $ line ) {
130
- $ fieldname = $ this ->choice ($ key );
133
+ $ fieldname = $ this ->field ($ key );
131
134
if (!question_utils::arrays_same_at_key_missing_is_blank ($ prevresponse , $ newresponse , $ fieldname )) {
132
135
return false ;
133
136
}
134
137
}
135
138
return true ;
136
139
}
137
140
138
- #[\Override]
139
- public function grade_response (array $ response ): array {
140
- // Retrieve the number of right responses and the total number of responses.
141
- if ($ this ->grademethod == 'partial ' ) {
142
- [$ numright , $ total ] = $ this ->get_num_parts_right_grade_partialt ($ response );
143
- } else {
144
- [$ numright , $ total ] = $ this ->get_num_parts_right_grade_allornone ($ response );
145
- }
146
- $ fraction = $ numright / $ total ;
147
- return [$ fraction , question_state::graded_state_for_fraction ($ fraction )];
148
- }
149
-
150
141
/**
151
142
* Get the number of correct choices selected in the response, for 'Give partial credit' grade method.
152
143
*
153
144
* @param array $response The response list.
154
145
* @return array The array of number of correct lines (start, end or both points of lines).
155
146
*/
156
- public function get_num_parts_right_grade_partialt (array $ response ): array {
147
+ public function get_num_parts_right_grade_partial (array $ response ): array {
157
148
if (!$ response ) {
158
149
return [0 , 0 ];
159
150
}
151
+ $ rightstart = 0 ;
152
+ $ rightend = 0 ;
153
+
160
154
$ numpartright = 0 ;
155
+ $ numpartrightarray = [];
161
156
foreach ($ this ->lines as $ key => $ line ) {
162
- if (array_key_exists ($ this ->choice ($ key ), $ response ) && $ response [$ this ->choice ($ key )] !== '' ) {
163
- $ coords = explode (' ' , $ response [$ this ->choice ($ key )]);
164
- if ($ line ->type == ' lineinfinite ' ) {
157
+ if (array_key_exists ($ this ->field ($ key ), $ response ) && $ response [$ this ->field ($ key )] !== '' ) {
158
+ $ coords = explode (' ' , $ response [$ this ->field ($ key )]);
159
+ if ($ line ->type === line:: TYPE_LINE_INFINITE ) {
165
160
if (count ($ coords ) == 2 ) {
166
161
// Response with 2 coordinates (x1,y1 x2,y2).
167
162
if (line::is_item_positioned_correctly_on_axis (
168
163
$ coords [0 ], $ line ->zonestart , $ line ->zoneend , 'start ' )) {
169
- $ numpartright ++;
164
+ $ rightstart ++;
170
165
}
171
166
if (line::is_item_positioned_correctly_on_axis (
172
167
$ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'end ' )) {
173
- $ numpartright ++;
168
+ $ rightend ++;
174
169
}
175
170
} else {
176
171
// Response has 4 coordinates(x1,y1 x2,y2 x3,y3 x4,y4).
177
172
// Here we need to consider x2,y2 x3,y3 for calculation.
178
173
if (line::is_item_positioned_correctly_on_axis (
179
174
$ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'start ' )) {
180
- $ numpartright ++;
175
+ $ rightstart ++;
181
176
}
182
177
if (line::is_item_positioned_correctly_on_axis (
183
178
$ coords [2 ], $ line ->zonestart , $ line ->zoneend , 'end ' )) {
184
- $ numpartright ++;
179
+ $ rightend ++;
185
180
}
186
181
}
187
182
} else {
188
183
if (line::is_dragitem_in_the_right_place ($ coords [0 ], $ line ->zonestart )) {
189
- $ numpartright ++;
184
+ $ numpartrightarray [$ line ->number ]['start ' ] = 1 ;
185
+ $ rightstart ++;
186
+ } else {
187
+ $ numpartrightarray [$ line ->number ]['start ' ] = 0 ;
190
188
}
191
189
if (line::is_dragitem_in_the_right_place ($ coords [1 ], $ line ->zoneend )) {
192
- $ numpartright ++;
190
+ $ numpartrightarray [$ line ->number ]['end ' ] = 1 ;
191
+ $ rightend ++;
192
+ } else {
193
+ $ numpartrightarray [$ line ->number ]['end ' ] = 0 ;
194
+
193
195
}
196
+ $ numpartright = $ rightstart + $ rightend ;
194
197
}
195
198
}
196
199
}
197
- $ total = count ($ this ->lines ) * 2 ;
198
- return [$ numpartright , $ total ];
200
+ $ numtotal = count ($ this ->lines ) * 2 ;
201
+ if (!empty ($ numpartrightarray )) {
202
+
203
+ return [$ numpartrightarray , $ numpartright , $ numtotal ];
204
+ }
205
+ return [$ numpartright , $ numtotal ];
199
206
}
200
207
201
208
/**
@@ -210,25 +217,25 @@ public function get_num_parts_right_grade_allornone(array $response): array {
210
217
}
211
218
$ numright = 0 ;
212
219
foreach ($ this ->lines as $ key => $ line ) {
213
- if (array_key_exists ($ this ->choice ($ key ), $ response ) && $ response [$ this ->choice ($ key )] !== '' ) {
214
- $ coords = explode (' ' , $ response [$ this ->choice ($ key )]);
215
- if ($ line ->type == ' lineinfinite ' ) {
220
+ if (array_key_exists ($ this ->field ($ key ), $ response ) && $ response [$ this ->field ($ key )] !== '' ) {
221
+ $ coords = explode (' ' , $ response [$ this ->field ($ key )]);
222
+ if ($ line ->type == line:: TYPE_LINE_INFINITE ) {
216
223
if (count ($ coords ) == 2 ) {
217
224
// Response with 2 coordinates (x1,y1 x2,y2 x3,y3 x4,y4).
218
225
$ isstartrightplace = line::is_item_positioned_correctly_on_axis (
219
- $ coords [0 ], $ line ->zonestart , $ line ->zoneend , 'start '
226
+ $ coords [0 ], $ line ->zonestart , $ line ->zoneend , 'start '
220
227
);
221
228
$ isendrightplace = line::is_item_positioned_correctly_on_axis (
222
- $ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'end '
229
+ $ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'end '
223
230
);
224
231
} else {
225
232
// Response has 4 coordinates(x1,y1 x2,y2 x3,y3 x4,y4).
226
233
// Here we need to consider x2,y2 x3,y3 for calculation.
227
234
$ isstartrightplace = line::is_item_positioned_correctly_on_axis (
228
- $ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'start '
235
+ $ coords [1 ], $ line ->zonestart , $ line ->zoneend , 'start '
229
236
);
230
237
$ isendrightplace = line::is_item_positioned_correctly_on_axis (
231
- $ coords [2 ], $ line ->zonestart , $ line ->zoneend , 'end '
238
+ $ coords [2 ], $ line ->zonestart , $ line ->zoneend , 'end '
232
239
);
233
240
}
234
241
if ($ isstartrightplace && $ isendrightplace ) {
@@ -274,15 +281,15 @@ public function get_validation_error(array $response): string {
274
281
public function classify_response (array $ response ) {
275
282
$ classifiedresponse = [];
276
283
foreach ($ this ->lines as $ key => $ line ) {
277
- if (array_key_exists ($ this ->choice ($ key ), $ response ) && $ response [$ this ->choice ($ key )] !== '' ) {
284
+ if (array_key_exists ($ this ->field ($ key ), $ response ) && $ response [$ this ->field ($ key )] !== '' ) {
278
285
if ($ this ->grademethod == 'partial ' ) {
279
286
$ fraction = 0.5 ;
280
287
} else {
281
288
$ fraction = 1 ;
282
289
}
283
290
$ classifiedresponse [$ key ] = new question_classified_response (
284
291
$ line ->number ,
285
- 'Line ' . $ line ->number . ': ' . $ response [$ this ->choice ($ key )],
292
+ 'Line ' . $ line ->number . ': ' . $ response [$ this ->field ($ key )],
286
293
$ fraction );
287
294
} else {
288
295
$ classifiedresponse [$ key ] = question_classified_response::no_response ();
@@ -291,6 +298,49 @@ public function classify_response(array $response) {
291
298
return $ classifiedresponse ;
292
299
}
293
300
301
+ #[\Override]
302
+ public function grade_response (array $ response ): array {
303
+ // Retrieve the number of right responses and the total number of responses.
304
+ [$ numright , $ numtotal ] = $ this ->retrieve_numright_numtotal ($ response );
305
+ $ fraction = $ numright / $ numtotal ;
306
+ return [$ fraction , question_state::graded_state_for_fraction ($ fraction )];
307
+ }
308
+
309
+ /**
310
+ * Return number of right responses and the total number of answers.
311
+ *
312
+ * @param array $response The respnse array
313
+ * @return array|int[] The array containing number of correct responses and the total.
314
+ */
315
+ public function retrieve_numright_numtotal (array $ response ): array {
316
+ if ($ this ->grademethod == 'partial ' ) {
317
+ [$ notused , $ numright , $ numtotal ] = $ this ->get_num_parts_right_grade_partial ($ response );
318
+ } else {
319
+ [$ numright , $ numtotal ] = $ this ->get_num_parts_right_grade_allornone ($ response );
320
+ }
321
+ return [$ numright , $ numtotal ];
322
+ }
323
+
324
+ /**
325
+ * Return the final garde for the question.
326
+ *
327
+ * @param array $responses The respnses array
328
+ * @return array
329
+ */
330
+ public function get_tries_responses (array $ responses ): array {
331
+ $ allgrades = [];
332
+ foreach ($ responses as $ trynumber => $ response ) {
333
+ if ($ this ->grademethod == 'partial ' ) {
334
+ [$ array , $ numpartright , $ numtotal ] = $ this ->get_num_parts_right_grade_partial ($ response );
335
+ $ allgrades [$ trynumber ] = $ array ;
336
+ } else {
337
+ [$ numright , $ numtotal ] = $ this ->get_num_parts_right_grade_allornone ($ response );
338
+ $ allgrades [$ trynumber ] = $ numright ;
339
+ }
340
+ }
341
+ return $ allgrades ;
342
+ }
343
+
294
344
/**
295
345
* Work out a final grade for this attempt, taking into account
296
346
* all the tries the student made and return the grade value.
@@ -302,26 +352,47 @@ public function classify_response(array $response) {
302
352
* @param int $totaltries The maximum number of tries allowed.
303
353
*
304
354
* @return float the fraction that should be awarded for this
305
- * sequence of response .
355
+ * sequence of responses .
306
356
*/
307
- public function compute_final_grade (array $ responses , int $ totaltries ): float {
308
- // TODO: To incorporate the question penalty for interactive with multiple tries behaviour.
309
-
357
+ public function compute_final_grade (array $ responses , int $ totaltries ): int |float {
358
+ $ penalties = 0 ;
310
359
$ grade = 0 ;
311
- foreach ($ responses as $ response ) {
360
+ $ allgrades = $ this ->get_tries_responses ($ responses );
361
+ ; foreach ($ responses as $ trynumber => $ response ) {
312
362
[$ fraction , $ state ] = $ this ->grade_response ($ response );
313
- $ grade += $ fraction ;
363
+ if ($ state ->is_graded () === true ) {
364
+ if ($ totaltries === 1 ) {
365
+ return $ fraction ;
366
+ }
367
+ [$ numright , $ numtotal ] = $ this ->retrieve_numright_numtotal ($ response );
368
+ $ grade = max (0 , $ fraction - $ penalties );
369
+ if ($ state ->get_feedback_class () === 'correct ' ) {
370
+ return $ grade ;
371
+ }
372
+ if ($ state ->get_feedback_class () === 'incorrect ' ) {
373
+ $ penalties += $ this ->penalty ;
374
+ }
375
+ if ($ state ->get_feedback_class () === 'partiallycorrect ' ) {
376
+ $ trynumright = 0 ;
377
+ foreach ($ this ->lines as $ line ) {
378
+ $ trynumright += $ allgrades [$ trynumber ][$ line ->number ]['start ' ] +
379
+ $ allgrades [$ trynumber ][$ line ->number ]['end ' ];
380
+ }
381
+ $ partpenaly = (($ numtotal - $ trynumright ) * $ this ->penalty / $ numtotal );
382
+ $ penalties += min ($ this ->penalty , $ partpenaly );
383
+ }
384
+ }
314
385
}
315
386
return $ grade ;
316
387
}
317
388
318
389
/**
319
- * Get a choice identifier
390
+ * Get a choice index identifier
320
391
*
321
- * @param int $choice stem number
392
+ * @param int $choice
322
393
* @return string the question-type variable name.
323
394
*/
324
- public function choice ($ choice ) {
395
+ public function field ($ choice ): string {
325
396
return 'c ' . $ choice ;
326
397
}
327
398
}
0 commit comments