Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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)
Esempio n. 6
0
    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,
        )
Esempio n. 7
0
    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)
Esempio n. 8
0
    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],
        )
Esempio n. 9
0
    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)