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  {
379  $valid = $this->validatePeerReview($row);
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.
updatePeerReview($a_peer_id, array $a_values)
getId()
Get assignment id.
countGivenFeedback($a_validate=true, $a_user_id=null)
$valid
isFeedbackValidForPassed($a_user_id)
static now()
Return current timestamp in Y-m-d H:i:s format.
hasPeerReviewAccess($a_peer_id)
getPeerMaskedId($a_giver_id, $a_peer_id)
getAllPeerReviews($a_only_valid=true)
Exercise peer review.
updatePeerReviewTimestamp($a_peer_id)
$data
validatePeerReview(array $a_data)
getPeerReviewsByGiver($a_user_id)
global $ilUser
Definition: imgupload.php:15
global $ilDB
getPeerReviewsByPeerId($a_user_id, $a_only_valid=false)
getPeerReviewValues($a_giver_id, $a_peer_id)
__construct(ilExAssignment $a_assignment)