ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilExPeerReview.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
11{
12 protected $assignment; // [$a_assignment]
13 protected $assignment_id; // [int]
14
15 public function __construct(ilExAssignment $a_assignment)
16 {
17 $this->assignment = $a_assignment;
18 $this->assignment_id = $a_assignment->getId();
19 }
20
21 public function hasPeerReviewGroups()
22 {
23 global $ilDB;
24
25 $set = $ilDB->query("SELECT count(*) cnt".
26 " FROM exc_assignment_peer".
27 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer"));
28 $cnt = $ilDB->fetchAssoc($set);
29 return (bool)$cnt["cnt"];
30 }
31
32 protected function getValidPeerReviewUsers()
33 {
34 global $ilDB;
35
36 $user_ids = array();
37
38 // returned / assigned ?!
39 $set = $ilDB->query("SELECT DISTINCT(user_id)".
40 " FROM exc_returned".
41 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer").
42 " AND (filename IS NOT NULL OR atext IS NOT NULL)");
43 while($row = $ilDB->fetchAssoc($set))
44 {
45 $user_ids[] = $row["user_id"];
46 }
47
48 return $user_ids;
49 }
50
51 protected function initPeerReviews()
52 {
53 global $ilDB;
54
55
56 // see #22246
57 if (!$this->assignment->afterDeadlineStrict())
58 {
59 return false;
60 }
61
62 if(!$this->hasPeerReviewGroups())
63 {
64 $user_ids = $this->getValidPeerReviewUsers();
65
66 // forever alone
67 if(sizeof($user_ids) < 2)
68 {
69 return false;
70 }
71
72 $rater_ids = $user_ids;
73 $matrix = array();
74
75 $max = min(sizeof($user_ids)-1, $this->assignment->getPeerReviewMin());
76 for($loop = 0; $loop < $max; $loop++)
77 {
78 $run_ids = array_combine($user_ids, $user_ids);
79
80 foreach($rater_ids as $rater_id)
81 {
82 $possible_peer_ids = $run_ids;
83
84 // may not rate himself
85 unset($possible_peer_ids[$rater_id]);
86
87 // already has linked peers
88 if(array_key_exists($rater_id, $matrix))
89 {
90 $possible_peer_ids = array_diff($possible_peer_ids, $matrix[$rater_id]);
91 }
92
93 // #15665 / #15883
94 if(!sizeof($possible_peer_ids))
95 {
96 // no more possible peers left? start over with all valid users
97 $run_ids = array_combine($user_ids, $user_ids);
98
99 // see above
100 $possible_peer_ids = $run_ids;
101
102 // may not rate himself
103 unset($possible_peer_ids[$rater_id]);
104
105 // already has linked peers
106 if(array_key_exists($rater_id, $matrix))
107 {
108 $possible_peer_ids = array_diff($possible_peer_ids, $matrix[$rater_id]);
109 }
110 }
111
112 // #14947
113 if(sizeof($possible_peer_ids))
114 {
115 $peer_id = array_rand($possible_peer_ids);
116 if(!array_key_exists($rater_id, $matrix))
117 {
118 $matrix[$rater_id] = array();
119 }
120 $matrix[$rater_id][] = $peer_id;
121 }
122
123 // remove peer_id from possible ids in this run
124 unset($run_ids[$peer_id]);
125 }
126 }
127
128 foreach($matrix as $rater_id => $peer_ids)
129 {
130 foreach($peer_ids as $peer_id)
131 {
132 $ilDB->manipulate("INSERT INTO exc_assignment_peer".
133 " (ass_id, giver_id, peer_id)".
134 " VALUES (".$ilDB->quote($this->assignment_id, "integer").
135 ", ".$ilDB->quote($rater_id, "integer").
136 ", ".$ilDB->quote($peer_id, "integer").")");
137 }
138 }
139
140 }
141 return true;
142 }
143
144 public function resetPeerReviews()
145 {
146 global $ilDB;
147
148 $all = array();
149
150 if($this->hasPeerReviewGroups())
151 {
152 foreach($this->getAllPeerReviews(false) as $peer_id => $reviews)
153 {
154 foreach(array_keys($reviews) as $giver_id)
155 {
156 $all[] = $giver_id;
157
158 foreach($this->assignment->getPeerReviewCriteriaCatalogueItems() as $crit)
159 {
160 $crit->setPeerReviewContext($this->assignment, $giver_id, $peer_id);
161 $crit->resetReview();
162 }
163 }
164 }
165
166 // peer groups
167 $ilDB->manipulate("DELETE FROM exc_assignment_peer".
168 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer"));
169 }
170
171 return $all;
172 }
173
174 public function validatePeerReviewGroups()
175 {
176 if($this->hasPeerReviewGroups())
177 {
178 include_once "./Modules/Exercise/classes/class.ilExerciseMembers.php";
179 $all_exc = ilExerciseMembers::_getMembers($this->assignment->getExerciseId());
180 $all_valid = $this->getValidPeerReviewUsers(); // only returned
181
182 $peer_ids = $invalid_peer_ids = $invalid_giver_ids = $all_reviews = array();
183 foreach($this->getAllPeerReviews(false) as $peer_id => $reviews)
184 {
185 $peer_ids[] = $peer_id;
186
187 if(!in_array($peer_id, $all_valid) ||
188 !in_array($peer_id, $all_exc))
189 {
190 $invalid_peer_ids[] = $peer_id;
191 }
192 foreach($reviews as $giver_id => $valid)
193 {
194 if(!in_array($giver_id, $all_valid) ||
195 !in_array($peer_id, $all_exc))
196 {
197 $invalid_giver_ids[] = $giver_id;
198 }
199 else
200 {
201 $all_reviews[$peer_id][$giver_id] = $valid;
202 }
203 }
204 }
205 $invalid_giver_ids = array_unique($invalid_giver_ids);
206
207 $missing_user_ids = array();
208 foreach($all_valid as $user_id)
209 {
210 // a missing peer is also a missing giver
211 if(!in_array($user_id, $peer_ids))
212 {
213 $missing_user_ids[] = $user_id;
214 }
215 }
216
217 $not_returned_ids = array();
218 foreach($all_exc as $user_id)
219 {
220 if(!in_array($user_id, $all_valid))
221 {
222 $not_returned_ids[] = $user_id;
223 }
224 }
225
226 return array(
227 "invalid" => (sizeof($missing_user_ids) ||
228 sizeof($invalid_peer_ids) ||
229 sizeof($invalid_giver_ids)),
230 "missing_user_ids" => $missing_user_ids,
231 "not_returned_ids" => $not_returned_ids,
232 "invalid_peer_ids" => $invalid_peer_ids,
233 "invalid_giver_ids" => $invalid_giver_ids,
234 "reviews" => $all_reviews);
235 }
236 }
237
238 public function getPeerReviewValues($a_giver_id, $a_peer_id)
239 {
240 $peer = null;
241 foreach($this->getPeerReviewsByGiver($a_giver_id) as $item)
242 {
243 if($item["peer_id"] == $a_peer_id)
244 {
245 $peer = $item;
246 }
247 }
248 if(!$peer)
249 {
250 return;
251 }
252 $data = $peer["pcomment"];
253 if($data)
254 {
255 $items = @unserialize($data);
256 if(!is_array($items))
257 {
258 // v1 - pcomment == text
259 $items = array("text"=>$data);
260 }
261 return $items;
262 }
263 }
264
265 public function getPeerReviewsByGiver($a_user_id)
266 {
267 global $ilDB;
268
269 $res = array();
270
271 if($this->initPeerReviews())
272 {
273 $idx = 0;
274 $set = $ilDB->query("SELECT *".
275 " FROM exc_assignment_peer".
276 " WHERE giver_id = ".$ilDB->quote($a_user_id, "integer").
277 " AND ass_id = ".$ilDB->quote($this->assignment_id, "integer").
278 " ORDER BY peer_id");
279 while($row = $ilDB->fetchAssoc($set))
280 {
281 $row["seq"] = ++$idx;
282 $res[] = $row;
283 }
284 }
285
286 return $res;
287 }
288
289 public function getPeerMaskedId($a_giver_id, $a_peer_id)
290 {
291 foreach($this->getPeerReviewsByGiver($a_giver_id) as $idx => $peer)
292 {
293 if($peer["peer_id"] == $a_peer_id)
294 {
295 return $peer["seq"];
296 }
297 }
298 }
299
300 protected function validatePeerReview(array $a_data)
301 {
302 $all_empty = true;
303
304 // see getPeerReviewValues()
305 $values = null;
306 $data = $a_data["pcomment"];
307 if($data)
308 {
309 $values = @unserialize($data);
310 if(!is_array($values))
311 {
312 // v1 - pcomment == text
313 $values = array("text"=>$data);
314 }
315 }
316
317 /* #18491 - values can be empty, text is optional (rating/file values are handled internally in criteria)
318 if(!$values)
319 {
320 return false;
321 }
322 */
323
324 foreach($this->assignment->getPeerReviewCriteriaCatalogueItems() as $crit)
325 {
326 $crit_id = $crit->getId()
327 ? $crit->getId()
328 : $crit->getType();
329 $crit->setPeerReviewContext(
330 $this->assignment,
331 $a_data["giver_id"],
332 $a_data["peer_id"]
333 );
334 if(!$crit->validate($values[$crit_id]))
335 {
336 return false;
337 }
338 if($crit->hasValue($values[$crit_id]))
339 {
340 $all_empty = false;
341 }
342 }
343
344 return !$all_empty;
345 }
346
347 public function getPeerReviewsByPeerId($a_user_id, $a_only_valid = false)
348 {
349 global $ilDB;
350
351 $res = array();
352
353 $idx = 0;
354 $set = $ilDB->query("SELECT *".
355 " FROM exc_assignment_peer".
356 " WHERE peer_id = ".$ilDB->quote($a_user_id, "integer").
357 " AND ass_id = ".$ilDB->quote($this->assignment_id, "integer").
358 " ORDER BY peer_id");
359 while($row = $ilDB->fetchAssoc($set))
360 {
361 if(!$a_only_valid ||
362 $this->validatePeerReview($row))
363 {
364 // this would be correct but rather senseless
365 // $row["seq"] = $this->getPeerMaskedId($row["giver_id"], $a_user_id);
366 $row["seq"] = ++$idx;
367 $res[] = $row;
368 }
369 }
370
371 return $res;
372 }
373
374 public function getAllPeerReviews($a_only_valid = true)
375 {
376 global $ilDB;
377
378 $res = array();
379
380 $set = $ilDB->query("SELECT *".
381 " FROM exc_assignment_peer".
382 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer").
383 " ORDER BY peer_id");
384 while($row = $ilDB->fetchAssoc($set))
385 {
387 if(!$a_only_valid ||
388 $valid)
389 {
390 $res[$row["peer_id"]][$row["giver_id"]] = $valid;
391 }
392 }
393
394 return $res;
395 }
396
397 public function hasPeerReviewAccess($a_peer_id)
398 {
399 global $ilDB, $ilUser;
400
401 $set = $ilDB->query("SELECT ass_id".
402 " FROM exc_assignment_peer".
403 " WHERE giver_id = ".$ilDB->quote($ilUser->getId(), "integer").
404 " AND peer_id = ".$ilDB->quote($a_peer_id, "integer").
405 " AND ass_id = ".$ilDB->quote($this->assignment_id, "integer"));
406 $row = $ilDB->fetchAssoc($set);
407 return (bool)$row["ass_id"];
408 }
409
410 public function updatePeerReviewTimestamp($a_peer_id)
411 {
412 global $ilDB, $ilUser;
413
414 $ilDB->manipulate("UPDATE exc_assignment_peer".
415 " SET tstamp = ".$ilDB->quote(ilUtil::now(), "timestamp").
416 " WHERE giver_id = ".$ilDB->quote($ilUser->getId(), "integer").
417 " AND peer_id = ".$ilDB->quote($a_peer_id, "integer").
418 " AND ass_id = ".$ilDB->quote($this->assignment_id, "integer"));
419 }
420
421 public function updatePeerReview($a_peer_id, array $a_values)
422 {
423 global $ilDB, $ilUser;
424
425 $sql = "UPDATE exc_assignment_peer".
426 " SET tstamp = ".$ilDB->quote(ilUtil::now(), "timestamp").
427 ",pcomment = ".$ilDB->quote(serialize($a_values), "text").
428 " WHERE giver_id = ".$ilDB->quote($ilUser->getId(), "integer").
429 " AND peer_id = ".$ilDB->quote($a_peer_id, "integer").
430 " AND ass_id = ".$ilDB->quote($this->assignment_id, "integer");
431
432 $ilDB->manipulate($sql);
433 }
434
435 public function countGivenFeedback($a_validate = true, $a_user_id = null)
436 {
437 global $ilDB, $ilUser;
438
439 if(!$a_user_id)
440 {
441 $a_user_id = $ilUser->getId();
442 }
443
444 $cnt = 0;
445
446 include_once './Services/Rating/classes/class.ilRating.php';
447
448 $set = $ilDB->query("SELECT *".
449 " FROM exc_assignment_peer".
450 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer").
451 " AND giver_id = ".$ilDB->quote($a_user_id, "integer"));
452 while($row = $ilDB->fetchAssoc($set))
453 {
454 if(!(bool)$a_validate ||
455 $this->validatePeerReview($row))
456 {
457 $cnt++;
458 }
459 }
460
461 return $cnt;
462 }
463
464 protected function getMaxPossibleFeedbacks()
465 {
466 global $ilDB;
467
468 // check if number of returned assignments is lower than assignment peer min
469 $set = $ilDB->query("SELECT COUNT(DISTINCT(user_id)) cnt".
470 " FROM exc_returned".
471 " WHERE ass_id = ".$ilDB->quote($this->assignment_id, "integer"));
472 $cnt = $ilDB->fetchAssoc($set);
473 $cnt = (int)$cnt["cnt"];
474 return $cnt-1;
475 }
476
478 {
479 $max = $this->getMaxPossibleFeedbacks();
480
481 // #16160 - forever alone
482 if(!$max)
483 {
484 return;
485 }
486
487 // are all required or just 1?
488 if(!$this->assignment->getPeerReviewSimpleUnlock())
489 {
490 $needed = $this->assignment->getPeerReviewMin();
491 }
492 else
493 {
494 $needed = 1;
495 }
496
497 // there could be less participants than stated in the min required setting
498 $min = min($max, $needed);
499
500 return max(0, $min-$this->countGivenFeedback());
501 }
502
503 public function isFeedbackValidForPassed($a_user_id)
504 {
505 // peer feedback is not required for passing
506 if($this->assignment->getPeerReviewValid() == ilExAssignment::PEER_REVIEW_VALID_NONE)
507 {
508 return true;
509 }
510
511 // #16227 - no processing before reaching the peer review period
512 if(!$this->assignment->afterDeadlineStrict())
513 {
514 return false;
515 }
516
517 // forever alone - should be valid
518 $max = $this->getMaxPossibleFeedbacks();
519 if(!$max)
520 {
521 return true;
522 }
523
524 $no_of_feedbacks = $this->countGivenFeedback(true, $a_user_id);
525
526 switch($this->assignment->getPeerReviewValid())
527 {
529 return (bool)$no_of_feedbacks;
530
532 // there could be less participants than stated in the min required setting
533 $min = min($max, $this->assignment->getPeerReviewMin());
534
535 return (($min-$no_of_feedbacks) < 1);
536 }
537 }
538}
An exception for terminatinating execution or to throw for unit testing.
Exercise assignment.
getId()
Get assignment id.
Exercise peer review.
countGivenFeedback($a_validate=true, $a_user_id=null)
hasPeerReviewAccess($a_peer_id)
getAllPeerReviews($a_only_valid=true)
isFeedbackValidForPassed($a_user_id)
updatePeerReview($a_peer_id, array $a_values)
getPeerReviewsByGiver($a_user_id)
__construct(ilExAssignment $a_assignment)
getPeerReviewsByPeerId($a_user_id, $a_only_valid=false)
validatePeerReview(array $a_data)
updatePeerReviewTimestamp($a_peer_id)
getPeerMaskedId($a_giver_id, $a_peer_id)
getPeerReviewValues($a_giver_id, $a_peer_id)
static _getMembers($a_obj_id)
static now()
Return current timestamp in Y-m-d H:i:s format.
$valid
global $ilDB
$ilUser
Definition: imgupload.php:18