コード例 #1
0
    def try_to_place(self, signup):
        """ Try to place participant (and partner) on the trip.

        Returns if successful.
        """
        trip = signup.trip
        if trip.open_slots >= self.slots_needed:
            self.place_all_on_trip(signup)
            return True
        elif self.is_driver and not trip.open_slots and not self.paired:
            # A driver may displace somebody else
            # (but a couple with a driver cannot displace two people)
            if self.count_drivers_on_trip(trip) < self.min_drivers:
                self.logger.info(
                    f"{trip} is full, but doesn't have {self.min_drivers} drivers"
                )
                self.logger.info(
                    f"Adding {signup} to '{trip}', as they're a driver")
                par_to_bump = self.runner.participant_to_bump(trip)
                add_to_waitlist(par_to_bump, prioritize=True)
                self.logger.info(
                    f"Moved {par_to_bump} to the top of the waitlist")
                # TODO: Try to move the bumped participant to a preferred, open trip!
                signup.on_trip = True
                signup.save()
                return True
        return False
コード例 #2
0
    def _place_or_waitlist(self, future_signups, desired_signups):
        # JSON-serializable object we can use to analyze outputs.
        info = {
            'participant_pk': self.participant.pk,
            'paired_with_pk': self.paired_par and self.paired_par.pk,
            'is_paired': bool(self.paired),
            'affiliation': self.participant.affiliation,
            'ranked_trips': [signup.trip_id for signup in future_signups],
            'placed_on_choice': None,  # One-indexed rank
            'waitlisted': False,
        }

        if not future_signups:
            self.logger.info("%s did not choose any trips this week",
                             self._par_text)
            return info
        if not desired_signups:
            # This can happen if the Participant paired, but their partner ranked no trips.
            self.logger.info("%s has no remaining desired trips",
                             self._par_text)
            return info

        # Try to place participants on their first choice available trip
        skipped_to_avoid_driver_bump: List[Tuple[int, models.SignUp]] = []
        for rank, signup in enumerate(future_signups, start=1):
            if signup not in desired_signups:
                self.logger.debug("Ignoring undesired signup %s", signup)
                continue
            trip_name = signup.trip.name
            if self._placement_would_jeopardize_driver_bump(signup):
                self.logger.debug("Placing on %r risks bump from a driver",
                                  trip_name)
                skipped_to_avoid_driver_bump.append((rank, signup))
                continue
            if self._try_to_place(signup):
                self.logger.debug(
                    f"Placed on trip #{rank} of {len(future_signups)}")
                return {**info, 'placed_on_choice': rank}
            self.logger.info("Can't place %s on %r", self._par_text, trip_name)

        # At this point, there were no trips that could take the participant or pair
        # It's possible that some were skipped because few spaces remained & a driver may bump.
        # If any potential placements remain, take those & risk a future bump.
        for rank, signup in skipped_to_avoid_driver_bump:
            if self._try_to_place(signup):
                self.logger.debug(
                    f"Placed on trip #{rank} of {len(future_signups)}")
                return {**info, 'placed_on_choice': rank}

        self.logger.info(f"None of {self._par_text}'s desired trips are open.")
        favorite_trip = desired_signups.first().trip
        for participant in self.to_be_placed:
            favorite_signup = models.SignUp.objects.get(
                participant=participant, trip=favorite_trip)
            add_to_waitlist(favorite_signup)
            with_email = f"{self._par_text} ({participant.email})"
            self.logger.info(
                f"Waitlisted {with_email} on {favorite_trip.name}")

        return {**info, 'waitlisted': True}
コード例 #3
0
ファイル: lottery.py プロジェクト: chaoranxie/mitoc-trips
    def place_participant(self):
        if self.paired:
            logger.debug("{} is paired with {}".format(self.participant, self.paired_par))
            if not self.runner.handled(self.paired_par):
                logger.debug("Will handle signups when {} comes".format(self.paired_par))
                self.runner.mark_handled(self.participant)
                return
        if not self.future_signups:
            logger.debug("{} did not choose any trips this week".format(self.par_text))
            self.runner.mark_handled(self.participant)
            return

        # Try to place participants on their first choice available trip
        for signup in self.future_signups:
            if self.try_to_place(signup):
                break
            else:
                logger.info("Can't place {} on {}".format(self.par_text, signup.trip))

        else:  # No trips are open
            logger.info("None of {}'s trips are open.".format(self.par_text))
            favorite_trip = self.future_signups.first().trip
            for participant in self.to_be_placed:
                find_signup = Q(participant=participant, trip=favorite_trip)
                favorite_signup = models.SignUp.objects.get(find_signup)
                add_to_waitlist(favorite_signup)

        self.runner.mark_handled(self.participant)
コード例 #4
0
    def place_participant(self):
        if self.paired:
            self.logger.info(
                f"{self.participant} is paired with {self.paired_par}")
            if not self.runner.handled(self.paired_par):
                self.logger.info(
                    f"Will handle signups when {self.paired_par} comes")
                self.runner.mark_handled(self.participant)
                return
        if not self.future_signups:
            self.logger.info(
                f"{self.par_text} did not choose any trips this week")
            self.runner.mark_handled(self.participant)
            return

        # Try to place participants on their first choice available trip
        for signup in self.future_signups:
            if self.try_to_place(signup):
                break
            else:
                self.logger.info(
                    f"Can't place {self.par_text} on {signup.trip}")

        else:  # No trips are open
            self.logger.info(f"None of {self.par_text}'s trips are open.")
            favorite_trip = self.future_signups.first().trip
            for participant in self.to_be_placed:
                find_signup = Q(participant=participant, trip=favorite_trip)
                favorite_signup = models.SignUp.objects.get(find_signup)
                add_to_waitlist(favorite_signup)
                with_email = f"{self.par_text} ({participant.email})"
                self.logger.info(
                    f"Waitlisted {with_email} on {favorite_signup.trip.name}")

        self.runner.mark_handled(self.participant)
コード例 #5
0
ファイル: lottery.py プロジェクト: chaoranxie/mitoc-trips
    def place_participant(self):
        if self.paired:
            logger.info("{} is paired with {}".format(self.participant, self.paired_par))
            if not self.runner.handled(self.paired_par):
                logger.info("Will handle signups when {} comes".format(self.paired_par))
                self.runner.mark_handled(self.participant)
                return

        # Try to place all participants, otherwise add them to the waitlist
        signup = models.SignUp.objects.get(participant=self.participant,
                                           trip=self.trip)
        if not self.try_to_place(signup):
            for par in self.to_be_placed:
                add_to_waitlist(models.SignUp.objects.get(trip=self.trip,
                                                          participant=par))
        self.runner.mark_handled(self.participant)
コード例 #6
0
 def test_adds_message_on_request(self):
     request = RequestFactory().get('/')
     signup = factories.SignUpFactory.create(on_trip=False)
     with patch.object(messages, 'success') as success:
         wl_signup = signup_utils.add_to_waitlist(signup, request=request)
     success.assert_called_once_with(request, "Added to waitlist.")
     self.assertEqual(wl_signup.signup, signup)
     self.assertFalse(wl_signup.signup.on_trip)
コード例 #7
0
    def place_participant(self):
        if self.paired:
            self.logger.info(
                f"{self.participant} is paired with {self.paired_par}")
            if not self.runner.handled(self.paired_par):
                self.logger.info(
                    f"Will handle signups when {self.paired_par} comes")
                self.runner.mark_handled(self.participant)
                return

        # Try to place all participants, otherwise add them to the waitlist
        signup = models.SignUp.objects.get(participant=self.participant,
                                           trip=self.trip)
        if not self.try_to_place(signup):
            for par in self.to_be_placed:
                self.logger.info(f"Adding {par.name} to the waitlist")
                add_to_waitlist(
                    models.SignUp.objects.get(trip=self.trip, participant=par))
        self.runner.mark_handled(self.participant)
コード例 #8
0
    def test_can_add_to_top_of_list(self):
        """We can add somebody to the waitlist, passing all others."""
        trip = factories.TripFactory()

        # Build a waitlist with a mixture of ordered by time added & manually ordered
        spot_1 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_2 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_3 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_4 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        factories.WaitListSignupFactory(signup=spot_3)
        factories.WaitListSignupFactory(signup=spot_4)
        factories.WaitListSignupFactory(signup=spot_2, manual_order=10)
        factories.WaitListSignupFactory(signup=spot_1, manual_order=11)
        self.assertEqual(list(trip.waitlist.signups),
                         [spot_1, spot_2, spot_3, spot_4])

        signup = factories.SignUpFactory.create(trip=trip, on_trip=True)
        signup_utils.add_to_waitlist(signup, prioritize=True, top_spot=True)
        self.assertEqual(list(trip.waitlist.signups),
                         [signup, spot_1, spot_2, spot_3, spot_4])
コード例 #9
0
ファイル: lottery.py プロジェクト: chaoranxie/mitoc-trips
    def try_to_place(self, signup):
        """ Try to place participant (and partner) on the trip.

        Returns if successful.
        """
        trip = signup.trip
        if trip.open_slots >= self.slots_needed:
            self.place_all_on_trip(signup)
            return True
        elif self.is_driver and not trip.open_slots and not self.paired:
            # A driver may displace somebody else
            # (but a couple with a driver cannot displace two people)
            if self.count_drivers_on_trip(trip) < self.min_drivers:
                logger.info("{} is full, but doesn't have {} drivers".format(trip, self.min_drivers))
                logger.info("Adding {} to '{}', as they're a driver".format(signup, trip))
                par_to_bump = self.runner.participant_to_bump(trip)
                add_to_waitlist(par_to_bump, prioritize=True)
                signup.on_trip = True
                signup.save()
                return True
        return False
コード例 #10
0
    def test_already_on_waitlist(self):
        """Leaders can't add a participant already on the waitlist!"""
        self.trip.algorithm = 'fcfs'
        self.trip.maximum_participants = 1
        self.trip.save()

        # Trip is full now
        factories.SignUpFactory.create(trip=self.trip)

        signup = factories.SignUpFactory.create(
            trip=self.trip, participant__name='Jane McJaney')
        add_to_waitlist(signup)

        response = self.client.post(
            self.url,
            {'participant_id': signup.participant_id},
            content_type='application/json',
        )

        self.assertEqual(response.status_code, 409)
        self.assertEqual(
            response.json(),
            {'message': "Jane McJaney is already on the waitlist"})
コード例 #11
0
    def place_participant(self):
        # Indicate that this participant's number has come up!
        # (The issue of ranking is external to this module)
        self.runner.mark_seen(self.participant)

        if self.paired:
            self.logger.info(
                f"{self.participant} is paired with {self.paired_par}")
            if not self.runner.seen(self.paired_par):
                self.logger.info(
                    f"Will handle signups when {self.paired_par} comes")
                return

        # Try to place all participants, otherwise add them to the waitlist
        signup = models.SignUp.objects.get(participant=self.participant,
                                           trip=self.trip)
        if not self._try_to_place(signup):
            for par in self.to_be_placed:
                self.logger.info(f"Adding {par.name} to the waitlist")
                add_to_waitlist(
                    models.SignUp.objects.get(trip=self.trip, participant=par))
        self.runner.mark_handled(self.participant)
        if self.paired_par:
            self.runner.mark_handled(self.paired_par)
コード例 #12
0
 def bump_participant(self, signup):
     add_to_waitlist(signup, prioritize=True)
     self.logger.info("Moved %s to the top of the waitlist", signup)
コード例 #13
0
    def test_can_add_to_bottom_of_priority(self):
        """Adding signups with priority puts them beneath other priorities, but above non."""
        trip = factories.TripFactory()

        spot_3 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_1 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_2 = factories.SignUpFactory.create(trip=trip, on_trip=False)

        # Start with a simple waitlist with no manual ordering
        spot_5 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        spot_4 = factories.SignUpFactory.create(trip=trip, on_trip=False)
        signup_utils.add_to_waitlist(spot_4)
        signup_utils.add_to_waitlist(spot_5)
        self.assertEqual(list(trip.waitlist.signups), [spot_4, spot_5])

        # Add each new signup to priority, but not the top spot
        signup_utils.add_to_waitlist(spot_1, prioritize=True, top_spot=False)
        self.assertEqual(list(trip.waitlist.signups), [spot_1, spot_4, spot_5])
        signup_utils.add_to_waitlist(spot_2, prioritize=True, top_spot=False)
        self.assertEqual(list(trip.waitlist.signups),
                         [spot_1, spot_2, spot_4, spot_5])
        signup_utils.add_to_waitlist(spot_3, prioritize=True, top_spot=False)
        self.assertEqual(list(trip.waitlist.signups),
                         [spot_1, spot_2, spot_3, spot_4, spot_5])

        # Adding to the top spot still works!
        signup = factories.SignUpFactory.create(trip=trip, on_trip=True)
        signup_utils.add_to_waitlist(signup, prioritize=True, top_spot=True)
        self.assertEqual(
            list(trip.waitlist.signups),
            [signup, spot_1, spot_2, spot_3, spot_4, spot_5],
        )
コード例 #14
0
 def test_already_has_waitlist_entry(self):
     wl_signup = factories.WaitListSignupFactory.create()
     self.assertIs(wl_signup,
                   signup_utils.add_to_waitlist(wl_signup.signup))
コード例 #15
0
 def test_already_on_trip(self):
     """Participants already on the trip will be waitlisted."""
     signup = factories.SignUpFactory.create(on_trip=True)
     wl_signup = signup_utils.add_to_waitlist(signup)
     self.assertEqual(wl_signup.signup, signup)
     self.assertFalse(wl_signup.signup.on_trip)