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  {
386  $valid = $this->validatePeerReview($row);
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 }
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)
validatePeerReview(array $a_data)
$ilUser
Definition: imgupload.php:18
getPeerReviewsByGiver($a_user_id)
Create styles array
The data for the language used.
global $ilDB
getPeerReviewsByPeerId($a_user_id, $a_only_valid=false)
getPeerReviewValues($a_giver_id, $a_peer_id)
static _getMembers($a_obj_id)
__construct(ilExAssignment $a_assignment)