ILIAS  release_7 Revision v7.30-3-g800a261c036
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilBookingPreferencesManager.php
Go to the documentation of this file.
1 <?php
2 
3 /* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
11 {
13 
17  protected $pool;
18 
22  protected $current_time;
23 
27  protected $bookings_per_user;
28 
32  protected $book_repo;
33 
39  public function __construct(
42  int $current_time = null,
43  $bookings_per_user = self::BOOKINGS_PER_USER_DEFAULT
44  ) {
45  $this->current_time = ($current_time > 0)
47  : time();
48  $this->pool = $pool;
49  $this->bookings_per_user = $bookings_per_user;
50  $this->book_repo = $book_repo;
51  }
52 
58  public function isGivingPreferencesPossible()
59  {
60  if ($this->pool->getScheduleType() == ilObjBookingPool::TYPE_NO_SCHEDULE_PREFERENCES &&
61  $this->pool->getPreferenceDeadline() > $this->current_time) {
62  return true;
63  }
64  return false;
65  }
66 
72  public function isPreferenceDeadlineReached()
73  {
74  if ($this->pool->getScheduleType() == ilObjBookingPool::TYPE_NO_SCHEDULE_PREFERENCES &&
75  $this->pool->getPreferenceDeadline() < $this->current_time) {
76  return true;
77  }
78  return false;
79  }
80 
88  public function storeBookings($preferences, $booking_object_ids = null)
89  {
90  $bookings = $this->calculateBookings($preferences, $booking_object_ids);
91  $this->book_repo->storeBookings($this->pool->getId(), $bookings);
92  }
93 
99  public function readBookings()
100  {
101  $booking_object_ids = array_map(function ($i) {
102  return $i["booking_object_id"];
103  }, ilBookingObject::getList($this->pool->getId()));
104  return $this->book_repo->getBookings($booking_object_ids);
105  }
106 
107 
115  public function calculateBookings(
116  ilBookingPreferences $preferences,
117  $booking_object_ids = null,
118  $availability = null
119  ) {
120  $preferences = $preferences->getPreferences();
121 
122  // we calculate if a) any preferences are given and b) the deadline is reached
123  /*if (!is_array($preferences) || count($preferences) == 0) {
124  throw new ilBookingCalculationException("No preferences given.");
125  }*/
126  if (!$this->isPreferenceDeadlineReached()) {
127  throw new ilBookingCalculationException("Preference deadline not reached.");
128  }
129 
130  if ($booking_object_ids == null) {
131  $booking_object_ids = array_map(function ($i) {
132  return $i["booking_object_id"];
133  }, ilBookingObject::getList($this->pool->getId()));
134  }
135 
136  if ($availability == null) {
137  $availability = [];
138  foreach ($booking_object_ids as $book_obj_id) {
139  $availability[$book_obj_id] = ilBookingReservation::getNumAvailablesNoSchedule($book_obj_id);
140  }
141  }
142 
143  // remove all objects from the preferences
144  // that are already not available anymore
145  // see bug 30204 (a tutor booked an object already before and made it unavailable)
146  foreach ($availability as $book_obj_id => $cnt) {
147  if ($cnt == 0) {
148  $preferences = $this->removeObjectFromPreferences($book_obj_id, $preferences);
149  }
150  }
151 
152  $bookings = [];
153 
154  $end_phase_one = false;
155 
156  // phase one: assign lowest popular items to random user
157  while (!$end_phase_one) {
158  $popularity = $this->calculatePopularity($booking_object_ids, $preferences);
159 
160  $low_pop_book_obj_id = $this->getObjectWithLowestPopularity($popularity, $availability);
161  if ($low_pop_book_obj_id > 0) {
162  $user_ids = $this->getUsersForObject($preferences, $low_pop_book_obj_id);
163  if (count($user_ids) > 0) {
164  $user_id = $this->selectRandomEntry($user_ids);
165  $this->addBooking($bookings, $preferences, $availability, $user_id, $low_pop_book_obj_id);
166  }
167  } else {
168  $end_phase_one = true;
169  }
170  }
171 
172  $end_phase_two = false;
173 
174  // choose random user from and assign currently rarely assigned objects
175  while (!$end_phase_two) {
176  $random_user_id = $this->chooseRandomUserFromPreferences($preferences);
177  if ($random_user_id > 0) {
178  $rare_assigned_book_obj_id = $this->getMinimalAssignedEntryForUser($booking_object_ids, $bookings, $preferences[$random_user_id], $availability);
179  if ($rare_assigned_book_obj_id > 0) {
180  $this->addBooking($bookings, $preferences, $availability, $random_user_id, $rare_assigned_book_obj_id);
181  } else {
182  $preferences = $this->removeUserFromPreferences($random_user_id, $preferences);
183  }
184  } else {
185  $end_phase_two = true;
186  }
187  }
188  return $bookings;
189  }
190 
200  protected function addBooking(&$bookings, &$preferences, &$availability, $user_id, $book_obj_id)
201  {
202  $bookings[$user_id][] = $book_obj_id;
203  $availability[$book_obj_id]--;
204  if (count($bookings[$user_id]) >= $this->bookings_per_user) {
205  $preferences = $this->removeUserFromPreferences($user_id, $preferences);
206  } else {
207  $preferences = $this->removePreference($user_id, $book_obj_id, $preferences);
208  }
209  if ($availability[$book_obj_id] <= 0) {
210  $preferences = $this->removeObjectFromPreferences($book_obj_id, $preferences);
211  }
212  }
213 
214 
221  protected function selectRandomEntry($items)
222  {
223  $nr = rand(0, count($items) - 1);
224  return $items[$nr];
225  }
226 
227 
235  protected function getUsersForObject($preferences, $sel_obj_id)
236  {
237  $user_ids = [];
238  foreach ($preferences as $user_id => $obj_ids) {
239  foreach ($obj_ids as $obj_id) {
240  if ($obj_id == $sel_obj_id) {
241  $user_ids[] = $user_id;
242  }
243  }
244  }
245  return $user_ids;
246  }
247 
248 
256  protected function calculatePopularity(array $booking_object_ids, array $preferences)
257  {
258  $popularity = [];
259  foreach ($booking_object_ids as $book_obj_id) {
260  $popularity[$book_obj_id] = 0;
261  }
262  foreach ($preferences as $user_id => $bobj_ids) {
263  foreach ($bobj_ids as $bobj_id) {
264  $popularity[$bobj_id] += 1;
265  }
266  }
267 
268  return $popularity;
269  }
270 
271 
278  protected function getObjectWithLowestPopularity($popularity, $availability)
279  {
280  asort($popularity, SORT_NUMERIC);
281  foreach ($popularity as $obj_id => $pop) {
282  if ($pop > 0 && $availability[$obj_id] > 0) {
283  return (int) $obj_id;
284  }
285  }
286  return 0;
287  }
288 
297  protected function removePreference($user_id, $obj_id, $preferences)
298  {
299  if (is_array($preferences[$user_id])) {
300  $preferences[$user_id] = array_filter($preferences[$user_id], function ($i) use ($obj_id) {
301  return ($i != $obj_id);
302  });
303  }
304  return $preferences;
305  }
306 
315  protected function removeObjectFromPreferences($obj_id, $preferences)
316  {
317  $new_preferences = [];
318  foreach ($preferences as $user_id => $obj_ids) {
319  $new_preferences[$user_id] = array_filter($preferences[$user_id], function ($i) use ($obj_id) {
320  return ($i != $obj_id);
321  });
322  }
323  return $new_preferences;
324  }
325 
333  protected function removeUserFromPreferences($user_id, $preferences)
334  {
335  if (is_array($preferences[$user_id])) {
336  unset($preferences[$user_id]);
337  }
338  return $preferences;
339  }
340 
347  protected function chooseRandomUserFromPreferences($preferences)
348  {
349  $user_ids = array_keys($preferences);
350  return $this->selectRandomEntry($user_ids);
351  }
352 
362  protected function getMinimalAssignedEntryForUser($booking_object_ids, $bookings, $user_preferences, $availability)
363  {
364  // count the assignments per object
365  $count_assignments = [];
366  foreach ($booking_object_ids as $obj_id) {
367  $count_assignments[$obj_id] = 0;
368  }
369  foreach ($bookings as $user => $obj_ids) {
370  foreach ($obj_ids as $obj_id) {
371  $count_assignments[$obj_id]++;
372  }
373  }
374 
375  // sort the objects by number of assignments, return the first one being found in the user preferences
376  asort($count_assignments, SORT_NUMERIC);
377  foreach ($count_assignments as $obj_id => $cnt) {
378  // if no preferences left for user, even assign object outside preferences
379  // otherwise choose object from preferences
380  if ((count($user_preferences) == 0 || in_array($obj_id, $user_preferences))
381  && $availability[$obj_id] > 0) {
382  return (int) $obj_id;
383  }
384  }
385  return 0;
386  }
387 
388  public function hasRun() : bool
389  {
390  return $this->book_repo->hasRun($this->pool->getId());
391  }
392 
393  public function resetRun() : void
394  {
395  $this->book_repo->resetRun($this->pool->getId());
396  }
397 }
isGivingPreferencesPossible()
Can participants hand in preferences.
getObjectWithLowestPopularity($popularity, $availability)
Get an availabe object with lowest popularity > 0.
static getList($a_pool_id, $a_title=null)
Get list of booking objects for given type.
removePreference($user_id, $obj_id, $preferences)
Remove a preference from the preference array.
selectRandomEntry($items)
Select a random entry of an array.
chooseRandomUserFromPreferences($preferences)
Choose random user from the preference array.
calculatePopularity(array $booking_object_ids, array $preferences)
Calculate popularity (number of preferences each object got from users)
getUsersForObject($preferences, $sel_obj_id)
Get users for object.
addBooking(&$bookings, &$preferences, &$availability, $user_id, $book_obj_id)
Add booking.
removeObjectFromPreferences($obj_id, $preferences)
Remove an object from the preference array.
Class ilObjBookingPool.
calculateBookings(ilBookingPreferences $preferences, $booking_object_ids=null, $availability=null)
Calculate bookings.
storeBookings($preferences, $booking_object_ids=null)
Calculate and store bookings.
getMinimalAssignedEntryForUser($booking_object_ids, $bookings, $user_preferences, $availability)
Get an available object within the preferences (if no preferences left, even outside of preferences) ...
Booking preferences business logic.
Manages the booking storage of the preference based calculated bookings.
getPreferences()
Get user preferences.
__construct(ilObjBookingPool $pool, ilBookingPrefBasedBookGatewayRepository $book_repo, int $current_time=null, $bookings_per_user=self::BOOKINGS_PER_USER_DEFAULT)
Constructor.
removeUserFromPreferences($user_id, $preferences)
Remove user from preference array.
static getNumAvailablesNoSchedule($a_obj_id)
$i
Definition: metadata.php:24
isPreferenceDeadlineReached()
Can participants hand in preferences.