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