diff --git a/idlerpg/models.py b/idlerpg/models.py index 3ef672f..1db7242 100644 --- a/idlerpg/models.py +++ b/idlerpg/models.py @@ -86,6 +86,19 @@ class CharacterManager(models.Manager): character.game.name, character.name, character.next_level) return character + def levelable(self, game) -> models.QuerySet: + """Provide the characters that are ready to level. + + Args: + game: the game instance to filter characters on + Returns: + QuerySet of the characters that can be leveled up + """ + if not game.active: + raise ValueError(f"{game.name} is not an active game!") + return self.filter(enabled=True, status=Character.CHARACTER_STATUS_LOGGED_IN, + next_level__lte=timezone.now(), game=game) + class Character(models.Model): """A character in a game.""" diff --git a/tests/test_idlerpg_character.py b/tests/test_idlerpg_character.py index 27ccd4e..3d864cb 100644 --- a/tests/test_idlerpg_character.py +++ b/tests/test_idlerpg_character.py @@ -218,3 +218,43 @@ class CharacterTest(TestCase): char.log_out() with self.assertRaises(ValueError): char.level_up() + + def test_levelable_query(self): + """Test that the right things are returned by the levelable query used to find characters to update.""" + game = Game.objects.get(pk=1) + # base data is one character ready to level + assert len(Character.objects.levelable(game)) == 1 + # add one to fiddle with + with patch('django.utils.timezone.now', return_value=timezone.now() - timedelta(days=1)): + new_char = Character.objects.register('levelable-test', game, 'pass', + 'bss!bss@levelable_test', 'unit tester') + assert len(Character.objects.levelable(game)) == 2 + # log the new one out + new_char.log_out() + new_char.save() + assert len(Character.objects.levelable(game)) == 1 + # log the new one back in but penalize it heavily + new_char.log_in('pass', 'bss!bss@levelable_test') + new_char.save() + assert len(Character.objects.levelable(game)) == 2 + new_char.penalize(60*60*24*2, 'test') + new_char.save() + assert len(Character.objects.levelable(game)) == 1 + # actually level them + new_char.next_level = timezone.now() - timedelta(seconds=30) + new_char.save() + assert len(Character.objects.levelable(game)) == 2 + new_char.level_up() + new_char.save() + assert len(Character.objects.levelable(game)) == 1 + + def test_levelable_query_bad_game(self): + """Test that trying to find levelable characters for a non-active game errors.""" + game = Game.objects.get(pk=1) + game.active = False + game.save() + with self.assertRaises(ValueError): + Character.objects.levelable(game) + # clean up + game.active = True + game.save()