def new_fcfs_signup(sender, instance, created, raw, using, update_fields, **kwargs): """Handles first-come, first-serve signups: When a participant tries to sign up, put them on the trip, or its waiting list. """ if created and not getattr(instance, 'skip_signals', False): trip_or_wait(instance)
def new_fcfs_signup(sender, instance, created, raw, using, update_fields, **kwargs): """ Handles first-come, first-serve signups: When a participant tries to sign up, put them on the trip, or its waiting list. """ if created and not getattr(instance, 'skip_signals', False): trip_or_wait(instance)
def post(self, request, *args, **kwargs): """Add signup to trip or waitlist, if applicable. Used if the participant has signed up, but wasn't placed. """ signup = self.get_participant_signup() signup_utils.trip_or_wait(signup, self.request, trip_must_be_open=True) return self.get(request)
def update_signups(self, signups, trip): """ Mark all signups as not on trip, then add signups in order. """ for signup, remove in signups: signup.on_trip = False signup.skip_signals = True # Skip the waitlist-bumping behavior signup.save() for order, (signup, remove) in enumerate(signups): if remove: signup.delete() else: signup_utils.trip_or_wait(signup, trip_must_be_open=False) signup_utils.next_in_order(signup, order)
def update_signups(self, signup_list, trip): """Mark all signups as not on trip, then add signups in order.""" keep_on_trip, to_delete = self.signups_to_update(signup_list, trip) # Clear the trip first (delete removals, set others to not on trip) # Both methods (update and skip_signals) ignore waitlist-bumping keep_on_trip.update(on_trip=False) for kill_signup in to_delete: kill_signup.skip_signals = True kill_signup.delete() # `all()` hits the db, will fetch current (`on_trip=False`) signups ordered_signups = keep_on_trip.all() for order, signup in enumerate(ordered_signups): signup_utils.trip_or_wait(signup, trip_must_be_open=False) signup_utils.next_in_order(signup, order)
def post(self, request, *args, **kwargs): """Process the participant & trip, create or update signup as needed. This method handles two main cases: - Participant has never signed up for the trip, will be placed - Participant has signed up before, but is not on the trip """ postdata = json.loads(self.request.body) par_pk = postdata.get('participant_id') notes = postdata.get('notes', '') try: par = models.Participant.objects.get(pk=par_pk) except models.Participant.DoesNotExist: return JsonResponse({'message': "No participant found"}, status=404) trip = self.get_object() signup, created = models.SignUp.objects.get_or_create( trip=trip, participant=par, defaults={'notes': notes}) if not created: # (SignUp exists, but participant may not be on trip) try: already_on_trip = signup.on_trip or signup.waitlistsignup except models.WaitListSignup.DoesNotExist: already_on_trip = False if already_on_trip: queue = "trip" if signup.on_trip else "waitlist" return JsonResponse( {'message': f"{par.name} is already on the {queue}"}, status=409) signup = signup_utils.trip_or_wait(signup) trip_participants = { s.participant for s in trip.on_trip_or_waitlisted.select_related('participant') } other_trips_by_par = dict( trip.other_trips_by_participant(for_participants=[par])) other_trips = other_trips_by_par[par.pk] # signup: descriptor, agnostic of presence on the trip or waiting list # on_trip: a boolean to place this signup in the right place # (either at the bottom of the trip list or waiting list) return JsonResponse( { 'signup': self.describe_signup(signup, trip_participants, other_trips), 'on_trip': signup.on_trip, }, status=201 if (created or signup.on_trip) else 200, )
def test_full_trip_shrinking(self): trip = factories.TripFactory.create(algorithm='fcfs', maximum_participants=2) one = factories.SignUpFactory.create(trip=trip) two = factories.SignUpFactory.create(trip=trip) # First two participants placed signup_utils.trip_or_wait(one) signup_utils.trip_or_wait(two) self.assertTrue(one.on_trip) self.assertTrue(two.on_trip) # Update, explicitly avoiding signals, then update queues. models.Trip.objects.filter(pk=trip.pk).update(maximum_participants=1) trip.refresh_from_db() signup_utils.update_queues_if_trip_open(trip) # Last participant on the trip is bumped two.refresh_from_db() self.assertFalse(two.on_trip) wl_signup = models.WaitListSignup.objects.get(signup__trip=trip) self.assertEqual(wl_signup.signup, two)
def test_full_trip_expanding(self): """If a full trip expands, we pull participants from the waitlist!""" trip = factories.TripFactory.create(algorithm='fcfs', maximum_participants=2) self.assertTrue(trip.signups_open) one, two, three = (factories.SignUpFactory.create(trip=trip) for i in range(3)) # First two participants placed signup_utils.trip_or_wait(one) signup_utils.trip_or_wait(two) self.assertTrue(one.on_trip) self.assertTrue(two.on_trip) # Third participant waitlisted signup_utils.trip_or_wait(three) self.assertFalse(three.on_trip) self.assertTrue(three.waitlistsignup) # An additional participant has the last spot on the waitlist. stays_on_wl = factories.SignUpFactory.create(trip=trip) signup_utils.trip_or_wait(stays_on_wl) # Update, explicitly avoiding signals models.Trip.objects.filter(pk=trip.pk).update(maximum_participants=3) trip.refresh_from_db() # Update the queues, our third participant can be on the trip now! signup_utils.update_queues_if_trip_open(trip) three.refresh_from_db() self.assertTrue(three.on_trip) # The third participant is pulled off the waitlist, the last stays on the trip. self.assertCountEqual( models.WaitListSignup.objects.filter(signup__trip=trip), [stays_on_wl.waitlistsignup], )
def post(self, request, *args, **kwargs): """ Process the participant & trip, create or update signup as neeeded. This method handles two main cases: - Participant has never signed up for the trip, will be placed - Participant has signed up before, but is not on the trip """ postdata = json.loads(self.request.body) par_pk = postdata.get('participant_id') try: par = models.Participant.objects.get(pk=par_pk) except ObjectDoesNotExist: return JsonResponse({'message': "No participant found"}, status=404) trip = self.get_object() signup, created = models.SignUp.objects.get_or_create(trip=trip, participant=par) if signup.on_trip and not created: # Other cases: Exists but not on trip, or exists but on waitlist # (trip_or_wait will handle both of those cases) msg = "{} is already signed up".format(signup.participant.name) return JsonResponse({'message': msg}, status=409) signup.notes = postdata.get('notes', '') signup = signup_utils.trip_or_wait(signup) # signup: descriptor, agnostic of presence on the trip or waiting list # on_trip: a boolean to place this signup in the right place # (either at the bottom of the trip list or waiting list) return JsonResponse( { 'signup': self.describe_signup(signup), 'on_trip': signup.on_trip }, status=201)