Example #1
0
 def test_uniqueness(self):
     """ Seeds should be different from one another. """
     seeds = [
         rank.seed_for(models.Participant(pk=33), '22'),
         rank.seed_for(models.Participant(pk=22), '33'),
         rank.seed_for(models.Participant(pk=22), '22'),
     ]
     self.assertEqual(len(seeds), len(set(seeds)))
Example #2
0
    def test_sort_key_randomness(self):
        """ We break ties with a random value. """
        tweedle_dee = models.Participant(pk=5, affiliation='NG')
        tweedle_dum = models.Participant(pk=6, affiliation='NG')

        # All other ranking factors are equal
        self.ranker.number_trips_led.return_value = 0
        solid_record = rank.TripCounts(attended=3, flaked=0, total=3)
        self.ranker.number_ws_trips.return_value = solid_record

        # Despite their equality, some randomness distinguishes keys
        dee_key = self.ranker.priority_key(tweedle_dee)
        dum_key = self.ranker.priority_key(tweedle_dum)
        self.assertNotEqual(dee_key, dum_key)
        self.assertEqual(dee_key[:-1], dum_key[:-1])  # (last item is random)
Example #3
0
    def test_deterministic(self):
        """ Affiliation-weighted random numbers are deterministic with the same seed.

        This enables us to have repeatable lottery results.
        """
        participant = models.Participant(pk=12, affiliation='MU')
        self.assertEqual(
            rank.affiliation_weighted_rand(participant, 'trip-542'),
            rank.affiliation_weighted_rand(participant, 'trip-542'))
Example #4
0
    def test_lotteryinfo(self):
        """ Drivers are based off car status from that week. """
        par = models.Participant()
        par.lotteryinfo = models.LotteryInfo(car_status=None)
        self.assertFalse(handle.par_is_driver(par))

        par.lotteryinfo = models.LotteryInfo(car_status='own')
        self.assertTrue(handle.par_is_driver(par))
        par.lotteryinfo = models.LotteryInfo(car_status='rent')
        self.assertTrue(handle.par_is_driver(par))
Example #5
0
    def test_weight_subtraction(self):
        """ Test the definition of affiliation-weighted randomness.

        Specifically, we take a random float from a particular seed, and
        subtract a known offset according to the participant's affiliation.
        """
        # MIT undergraduates get an advantage: their number is more likely to be lower
        mit_undergrad = models.Participant(pk=12, affiliation='MU')
        seed = rank.seed_for(mit_undergrad, 'trip-142')
        random.seed(seed)
        self.assertEqual(
            random.random() - 0.3,
            rank.affiliation_weighted_rand(mit_undergrad, 'trip-142'))

        # Non-affiliates are just a random number
        non_affiliate = models.Participant(pk=24, affiliation='NA')
        seed = rank.seed_for(non_affiliate, 'trip-142')
        random.seed(seed)
        self.assertEqual(
            random.random(),
            rank.affiliation_weighted_rand(non_affiliate, 'trip-142'))
Example #6
0
    def test_leader_bump(self):
        """ All else held equal, the most active leaders get priority. """
        # Both participants are MIT undergraduates, equally likely to flake
        novice = models.Participant(pk=1024,
                                    affiliation='MU',
                                    name='New Leader')
        veteran = models.Participant(pk=256,
                                     affiliation='MU',
                                     name='Veteran Leader')

        def attended_all(num):
            return rank.TripCounts(attended=num, flaked=0, total=num)

        # Key difference: the veteran leader has a greater balance of led trips
        mocked_counts = {
            veteran: {
                'number_trips_led': 4,
                'number_ws_trips': attended_all(1),
            },  # Net 3
            novice: {
                'number_trips_led': 2,
                'number_ws_trips': attended_all(3),
            },  # Net -1
        }

        def by_participant(attribute):
            """ Quick closure for looking up the count. """
            return lambda par: mocked_counts[par][attribute]

        self.ranker.get_rank_override.return_value = 0
        for attr in ['number_ws_trips', 'number_trips_led']:
            getattr(self.ranker, attr).side_effect = by_participant(attr)

        # Sanity check that our net trips led balance works properly
        self.assertEqual(self.ranker.trips_led_balance(veteran), 3)
        self.assertEqual(self.ranker.trips_led_balance(novice), 0)

        # Veteran is given higher ranking
        self.expect_ranking(veteran, novice)
Example #7
0
    def test_flaking(self):
        """ Those who flake on trips always come last. """
        # Flaking participant is an MIT undergrad (would normally get priority)
        serial_flaker = models.Participant(pk=1,
                                           affiliation='MU',
                                           name='Serial Flaker')
        flaked_once = models.Participant(pk=2,
                                         affiliation='MG',
                                         name='One-time Flaker')
        reliable = models.Participant(pk=3, affiliation='NA', name='Reliable')

        mocked_counts = {
            flaked_once: {
                'number_trips_led': 8,
                'number_ws_trips': rank.TripCounts(attended=0,
                                                   flaked=1,
                                                   total=1),
            },
            serial_flaker: {
                'number_trips_led': 4,
                'number_ws_trips': rank.TripCounts(attended=0,
                                                   flaked=3,
                                                   total=3),
            },
            reliable: {
                'number_trips_led': 0,
                'number_ws_trips': rank.TripCounts(attended=4,
                                                   flaked=0,
                                                   total=4),
            },
        }

        self.ranker.get_rank_override.return_value = 0
        self.ranker.number_trips_led.side_effect = lambda par: mocked_counts[
            par]['number_trips_led']
        self.ranker.number_ws_trips.side_effect = lambda par: mocked_counts[
            par]['number_ws_trips']

        self.expect_ranking(reliable, flaked_once, serial_flaker)
Example #8
0
    def test_current_year(self, ws_year, ws_lectures_complete,
                          lecture_attendance):
        """ Check attendance in current year, after lectures complete.

        We're in a year where attendance is recorded, and we're asking about the current
        year. Did the participant attend?
        """
        participant = models.Participant()

        # We're asking about the current WS season, when lectures have occurred
        ws_year.return_value = current_year = 2020
        ws_lectures_complete.return_value = True

        attendance_exists = lecture_attendance.return_value.filter.return_value.exists

        # When participant has no attendance recorded, they've missed lectures
        attendance_exists.return_value = False
        self.assertTrue(utils.missed_lectures(participant, current_year))

        # When the participant attended, they did not miss lectures
        attendance_exists.return_value = True
        self.assertFalse(utils.missed_lectures(participant, current_year))
Example #9
0
 def test_no_lotteryinfo(self):
     """ Don't regard anybody as a driver if they didn't submit prefs. """
     par = models.Participant()
     self.assertFalse(handle.par_is_driver(par))
Example #10
0
 def test_seed_contains_secret(self):
     """ The seed should contain the secret that participant's don't know. """
     seed = rank.seed_for(models.Participant(pk=33), 'some extra seed')
     self.assertIn(settings.PRNG_SEED_SECRET, seed)
Example #11
0
 def test_participant_must_be_saved_to_db(self):
     """ We can't come up with a fair seed for a participant that lacks a pk. """
     with self.assertRaises(ValueError):
         rank.seed_for(models.Participant(name='Not Saved'),
                       lottery_key='hi')