def unregister(self, registration, admin_unregistration_reason=''): """ Pulls the registration, and clears relevant fields. Sets unregistration date. If the user was in a pool, and not in the waiting list, notifies the waiting list that there might be a bump available. """ if self.start_time < timezone.now(): raise EventHasClosed() # Locks unregister so that no user can register before bump is executed. pool_id = registration.pool_id registration.unregister( is_merged=self.is_merged, admin_unregistration_reason=admin_unregistration_reason) if pool_id: if not admin_unregistration_reason and\ self.heed_penalties and self.passed_unregistration_deadline: if not registration.user.penalties.filter( source_event=self).exists(): Penalty.objects.create( user=registration.user, reason=f'Meldte seg av {self.title} for sent.', weight=1, source_event=self) with transaction.atomic(): locked_event = Event.objects.select_for_update().get( pk=self.id) locked_pool = locked_event.pools.get(id=pool_id) locked_event.check_for_bump_or_rebalance(locked_pool) follow_event_item = FollowEvent.objects.filter( follower=registration.user, target=locked_event).first() if follow_event_item: follow_event_item.delete()
def register(self, registration): """ Evaluates a pending registration for the event, and automatically selects the optimal pool for the registration. First checks if there exist any legal pools for the pending registration, raises an exception if not. If there is only one possible pool, checks if the pool is full and registers for the waiting list or the pool accordingly. If the event is merged, and it isn't full, joins any pool. Otherwise, joins the waiting list. If the event isn't merged, checks if the pools that the pending registration can possibly join are full or not. If all are full, a registration for the waiting list is created. If there's only one pool that isn't full, register for it. If there's more than one possible pool that isn't full, calculates the total amount of users that can join each pool, and selects the most exclusive pool. If several pools have the same exclusivity, selects the biggest pool of these. :param registration: The registration that gets evaluated :return: The registration (in the chosen pool) """ user = registration.user penalties = 0 unanswered_surveys = user.unanswered_surveys() if len(unanswered_surveys) > 0: raise UnansweredSurveyException() if self.heed_penalties: penalties = user.number_of_penalties() current_time = timezone.now() if self.registration_close_time < current_time: raise EventHasClosed() all_pools = self.pools.all() possible_pools = self.get_possible_pools( user, all_pools=all_pools, is_admitted=registration.is_admitted) if not self.is_ready: raise EventNotReady() if not possible_pools: raise ValueError('No available pools') if self.get_earliest_registration_time(user, possible_pools, penalties) > current_time: raise ValueError('Not open yet') # Make the user follow the event FollowEvent.objects.get_or_create(follower=user, target=self) if penalties >= 3: return registration.add_to_waiting_list() # If the event is merged or has only one pool we can skip a lot of logic if all_pools.count() == 1: return registration.add_to_pool(possible_pools[0]) if self.is_merged: with transaction.atomic(): locked_event = Event.objects.select_for_update().get( pk=self.id) is_full = locked_event.is_full if not is_full: return registration.add_direct_to_pool(possible_pools[0]) return registration.add_to_waiting_list() # Calculates which pools that are full or open for registration based on capacity full_pools, open_pools = self.calculate_full_pools(possible_pools) if not open_pools: return registration.add_to_waiting_list() if len(open_pools) == 1: return registration.add_to_pool(open_pools[0]) # Returns a list of the pool(s) with the least amount of potential members exclusive_pools = self.find_most_exclusive_pools(open_pools) if len(exclusive_pools) == 1: chosen_pool = exclusive_pools[0] else: chosen_pool = self.select_highest_capacity(exclusive_pools) return registration.add_to_pool(chosen_pool)