ILIAS  release_7 Revision v7.30-3-g800a261c036
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
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
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
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}
An exception for terminatinating execution or to throw for unit testing.
static getList($a_pool_id, $a_title=null)
Get list of booking objects for given type.
Manages the booking storage of the preference based calculated bookings.
Booking preferences business logic.
storeBookings($preferences, $booking_object_ids=null)
Calculate and store bookings.
__construct(ilObjBookingPool $pool, ilBookingPrefBasedBookGatewayRepository $book_repo, int $current_time=null, $bookings_per_user=self::BOOKINGS_PER_USER_DEFAULT)
Constructor.
chooseRandomUserFromPreferences($preferences)
Choose random user from the preference array.
getObjectWithLowestPopularity($popularity, $availability)
Get an availabe object with lowest popularity > 0.
isPreferenceDeadlineReached()
Can participants hand in preferences.
removeObjectFromPreferences($obj_id, $preferences)
Remove an object from the preference array.
removeUserFromPreferences($user_id, $preferences)
Remove user from preference array.
selectRandomEntry($items)
Select a random entry of an array.
removePreference($user_id, $obj_id, $preferences)
Remove a preference from the preference array.
getMinimalAssignedEntryForUser($booking_object_ids, $bookings, $user_preferences, $availability)
Get an available object within the preferences (if no preferences left, even outside of preferences) ...
calculatePopularity(array $booking_object_ids, array $preferences)
Calculate popularity (number of preferences each object got from users)
isGivingPreferencesPossible()
Can participants hand in preferences.
calculateBookings(ilBookingPreferences $preferences, $booking_object_ids=null, $availability=null)
Calculate bookings.
getUsersForObject($preferences, $sel_obj_id)
Get users for object.
addBooking(&$bookings, &$preferences, &$availability, $user_id, $book_obj_id)
Add booking.
getPreferences()
Get user preferences.
static getNumAvailablesNoSchedule($a_obj_id)
Class ilObjBookingPool.
$i
Definition: metadata.php:24