diff --git a/pyproject.toml b/pyproject.toml index 7c3b359..2884b2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,50 +36,12 @@ python_files = [ [tool.ruff] src = ["src"] target-version = "py311" -select = [ - "F", # pyflakes - "E", "W", # pycodestyle - "C90", # mccabe - "I", # isort - "N", # pep8-naming - "D", # pydocstyle - "S", # flake8-bandit - "FBT", # flake8-boolean-trap - "B", # flake8-bugbear - "A", # flake8-builtins - "C4", # flake8-comprehensions - "DTZ", # flake8-datetimez - "T10", # flake8-debugger - "EXE", # flake8-executable - "ISC", # flake8-implicit-str-concat - "ICN", # flake8-import-conventions - "G", # flake8-logging-format - "INP", # flake8-no-pep420 - "PIE", # flake8-pie - "T20", # flake8-print - "PT", # flake8-pytest-style - "RET", # flake8-return - "SIM", # flake8-simplify - "TID", # flake8-tidy-imports - "ARG", # flake8-unused-arguments - "PTH", # flake8-use-pathlib - "ERA", # eradicate - "PD", # pandas-vet - "PGH", # pygrep-hooks - "PL", # pylint - "TRY", # tryceratops - "RUF", # ruff-specific rules -] +select = ["ALL"] unfixable = ["T20", "RUF001", "RUF002", "RUF003"] ignore = [ - "UP", # pyupgrade - "YTT", # flake8-2020 "ANN", # flake8-annotations "BLE", # flake8-blind-except - "COM", # flake8-commas - "EM", # flake8-errmsg - "Q", # flake8-quotes "TCH", # flake8-type-checking / TODO: revisit later ? "E501", # long lines diff --git a/src/character/admin.py b/src/character/admin.py index 5df8223..f21d505 100644 --- a/src/character/admin.py +++ b/src/character/admin.py @@ -92,12 +92,14 @@ class RaceAdmin(admin.ModelAdmin): class CharacterAdminForm(ModelForm): class Meta: model = models.Character - exclude = () + exclude = () # noqa: DJ006 - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.fields["capabilities"].queryset = models.Capability.objects.select_related( - "path", "path__race", "path__profile" + "path", + "path__race", + "path__profile", ) self.fields[ "racial_capability" @@ -127,7 +129,7 @@ class CharacterAdmin(admin.ModelAdmin): "level", "race", "private", - ] + ], }, ), ("Apparence", {"fields": ["gender", "age", "height", "weight"]}), @@ -141,7 +143,7 @@ class CharacterAdmin(admin.ModelAdmin): ("value_intelligence", "modifier_intelligence"), ("value_wisdom", "modifier_wisdom"), ("value_charisma", "modifier_charisma"), - ] + ], }, ), ( @@ -153,7 +155,7 @@ class CharacterAdmin(admin.ModelAdmin): "attack_range", "attack_magic", "states", - ] + ], }, ), ("Vitalité", {"fields": [("health_max", "health_remaining")]}), @@ -165,7 +167,7 @@ class CharacterAdmin(admin.ModelAdmin): "weapons", "equipment", ("money_pp", "money_po", "money_pa", "money_pc"), - ] + ], }, ), ("Race", {"fields": ["racial_capability"]}), diff --git a/src/character/forms.py b/src/character/forms.py index 3d37f1a..c9bac4b 100644 --- a/src/character/forms.py +++ b/src/character/forms.py @@ -18,10 +18,12 @@ class AddPathForm(forms.Form): empty_label="----- Voies liées au personnage -----", ) other_path = forms.ModelChoiceField( - Path.objects.none(), required=False, empty_label="----- Autres voies -----" + Path.objects.none(), + required=False, + empty_label="----- Autres voies -----", ) - def __init__(self, character: Character, *args, **kwargs): + def __init__(self, character: Character, *args, **kwargs) -> None: super().__init__(*args, **kwargs) paths = {cap.path_id for cap in character.capabilities.all()} paths = ( @@ -30,12 +32,12 @@ class AddPathForm(forms.Form): .select_related("profile", "race") ) character_paths = paths.filter( - Q(profile=character.profile) | Q(race=character.race) + Q(profile=character.profile) | Q(race=character.race), ) self.fields["character_path"].queryset = character_paths self.fields["character_path"].widget.attrs["class"] = "form-select" self.fields["other_path"].queryset = paths.exclude( - pk__in={path.pk for path in character_paths} + pk__in={path.pk for path in character_paths}, ) self.fields["other_path"].widget.attrs["class"] = "form-select" @@ -43,12 +45,13 @@ class AddPathForm(forms.Form): cleaned_data = super().clean() values = [cleaned_data.get("character_path"), cleaned_data.get("other_path")] if len(list(filter(None, values))) != 1: - raise ValidationError("Vous devez sélectionner une seule valeur.") + msg = "Vous devez sélectionner une seule valeur." + raise ValidationError(msg) return cleaned_data class CharacterCreateForm(forms.ModelForm): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.fields[ "racial_capability" diff --git a/src/character/management/commands/import_capabilities.py b/src/character/management/commands/import_capabilities.py index 32e0ddf..f3fa07b 100644 --- a/src/character/management/commands/import_capabilities.py +++ b/src/character/management/commands/import_capabilities.py @@ -17,7 +17,8 @@ class Command(BaseCommand): while len(cards) < expected_capability_count: self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END) cards = self.selenium.find_elements( - By.CSS_SELECTOR, ".col-md-4.col-sm-6.col-12.mb-4.views-row" + By.CSS_SELECTOR, + ".col-md-4.col-sm-6.col-12.mb-4.views-row", ) for card in cards: try: @@ -63,11 +64,11 @@ class Command(BaseCommand): }, ) self.stdout.write( - self.style.SUCCESS(f"Created/updated cap {capability}") + self.style.SUCCESS(f"Created/updated cap {capability}"), ) except Exception as e: self.stdout.write( - self.style.ERROR(f"Couldn't create/update cap {name}: {e}") + self.style.ERROR(f"Couldn't create/update cap {name}: {e}"), ) def get_paths(self, card: WebElement, name: str) -> list[Path]: @@ -78,7 +79,7 @@ class Command(BaseCommand): paths.append(Path.objects.get(name__iexact=path_name)) except Exception: self.stdout.write( - self.style.WARNING(f"Couldn't find path in card for cap '{name}'.") + self.style.WARNING(f"Couldn't find path in card for cap '{name}'."), ) return [] return paths diff --git a/src/character/management/commands/import_harmful_states.py b/src/character/management/commands/import_harmful_states.py index 722879f..02dc919 100644 --- a/src/character/management/commands/import_harmful_states.py +++ b/src/character/management/commands/import_harmful_states.py @@ -22,10 +22,12 @@ class Command(BaseCommand): def import_row(self, url: str, state_row: WebElement) -> None: name = state_row.find_element(By.CLASS_NAME, "views-field-name").text.strip() description = state_row.find_element( - By.CLASS_NAME, "views-field-description__value" + By.CLASS_NAME, + "views-field-description__value", ).text.strip() icon_url = state_row.find_element( - By.CSS_SELECTOR, ".views-field-field-svg-icon img" + By.CSS_SELECTOR, + ".views-field-field-svg-icon img", ).get_attribute("src") state, _ = HarmfulState.objects.update_or_create( name=name, diff --git a/src/character/management/commands/import_paths.py b/src/character/management/commands/import_paths.py index 8d9fc1b..08b8523 100644 --- a/src/character/management/commands/import_paths.py +++ b/src/character/management/commands/import_paths.py @@ -16,7 +16,8 @@ class Command(BaseCommand): while len(anchors) < expected_path_count: self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END) anchors = self.selenium.find_elements( - By.CSS_SELECTOR, ".card-body .card-title a" + By.CSS_SELECTOR, + ".card-body .card-title a", ) urls = [anchor.get_attribute("href") for anchor in anchors] for url in urls: @@ -57,7 +58,8 @@ class Command(BaseCommand): try: category = ( self.selenium.find_element( - By.CSS_SELECTOR, ".field--name-type .field__item" + By.CSS_SELECTOR, + ".field--name-type .field__item", ) .text.lower() .strip() @@ -65,8 +67,8 @@ class Command(BaseCommand): except Exception: self.stdout.write( self.style.WARNING( - f"Couldn't find category for {name}. Defaulting to profile." - ) + f"Couldn't find category for {name}. Defaulting to profile.", + ), ) return Path.Category.PROFILE @@ -79,7 +81,8 @@ class Command(BaseCommand): def get_profile(self, name: str) -> Profile | None: try: profile_name = self.selenium.find_element( - By.CSS_SELECTOR, ".field--name-type + strong + a" + By.CSS_SELECTOR, + ".field--name-type + strong + a", ).text except Exception: self.stdout.write(self.style.WARNING(f"Couldn't find profile for {name}")) @@ -99,7 +102,8 @@ class Command(BaseCommand): def get_notes(self) -> str: try: return self.selenium.find_element( - By.CSS_SELECTOR, ".mt-3 > .field--name-description" + By.CSS_SELECTOR, + ".mt-3 > .field--name-description", ).text.strip() except Exception: return "" diff --git a/src/character/management/commands/import_profiles.py b/src/character/management/commands/import_profiles.py index c7039d7..f7b8001 100644 --- a/src/character/management/commands/import_profiles.py +++ b/src/character/management/commands/import_profiles.py @@ -41,19 +41,20 @@ class Command(BaseCommand): def get_dice(self, name: str) -> Dice: dice = self.selenium.find_element(By.CSS_SELECTOR, ".dice + div").text.split( - "D" + "D", ) number_of_dice, dice_value = int(dice[0]), int(dice[1]) if number_of_dice != 1: self.stdout.write( - self.style.WARNING(f"Multiple dice for {name}: {number_of_dice}") + self.style.WARNING(f"Multiple dice for {name}: {number_of_dice}"), ) return Dice(dice_value) def get_magical_strength(self) -> Profile.MagicalStrength: try: magical_strength = self.selenium.find_element( - By.CSS_SELECTOR, ".field--name-magic-attack-modifier .field__item" + By.CSS_SELECTOR, + ".field--name-magic-attack-modifier .field__item", ).text magical_strength = Profile.MagicalStrength(magical_strength) except Exception: diff --git a/src/character/management/commands/import_races.py b/src/character/management/commands/import_races.py index f40654b..95adfab 100644 --- a/src/character/management/commands/import_races.py +++ b/src/character/management/commands/import_races.py @@ -27,7 +27,8 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS(f"Created/updated race {race}")) racial_cap = self.selenium.find_element( - By.CSS_SELECTOR, ".field--name-abilities" + By.CSS_SELECTOR, + ".field--name-abilities", ) racial_name = ( racial_cap.find_element(By.TAG_NAME, "strong") diff --git a/src/character/migrations/0001_initial.py b/src/character/migrations/0001_initial.py index eef101e..cb49d81 100644 --- a/src/character/migrations/0001_initial.py +++ b/src/character/migrations/0001_initial.py @@ -32,13 +32,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ( @@ -47,7 +49,7 @@ class Migration(migrations.Migration): validators=[ django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5), - ] + ], ), ), ("limited", models.BooleanField(blank=True)), @@ -74,13 +76,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ( @@ -106,7 +110,7 @@ class Migration(migrations.Migration): (10, "D10"), (12, "D12"), (20, "D20"), - ] + ], ), ), ], @@ -130,13 +134,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ], @@ -160,13 +166,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ("damage", models.CharField(blank=True, max_length=50)), @@ -192,20 +200,23 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ("description", models.TextField()), ( "race", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="character.race" + on_delete=django.db.models.deletion.CASCADE, + to="character.race", ), ), ], @@ -229,13 +240,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ( @@ -355,7 +368,8 @@ class Migration(migrations.Migration): model_name="capability", name="path", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="character.path" + on_delete=django.db.models.deletion.CASCADE, + to="character.path", ), ), migrations.AddConstraint( @@ -369,7 +383,9 @@ class Migration(migrations.Migration): migrations.AddConstraint( model_name="capability", constraint=models.UniqueConstraint( - models.F("path"), models.F("rank"), name="unique_path_rank" + models.F("path"), + models.F("rank"), + name="unique_path_rank", ), ), ] diff --git a/src/character/migrations/0011_alter_racialcapability_name_and_more.py b/src/character/migrations/0011_alter_racialcapability_name_and_more.py index 4d55976..bd75f33 100644 --- a/src/character/migrations/0011_alter_racialcapability_name_and_more.py +++ b/src/character/migrations/0011_alter_racialcapability_name_and_more.py @@ -17,7 +17,9 @@ class Migration(migrations.Migration): migrations.AddConstraint( model_name="racialcapability", constraint=models.UniqueConstraint( - models.F("name"), models.F("race"), name="unique_name_race" + models.F("name"), + models.F("race"), + name="unique_name_race", ), ), ] diff --git a/src/character/migrations/0012_alter_capability_options_alter_character_options_and_more.py b/src/character/migrations/0012_alter_capability_options_alter_character_options_and_more.py index da91f5c..19f0df5 100644 --- a/src/character/migrations/0012_alter_capability_options_alter_character_options_and_more.py +++ b/src/character/migrations/0012_alter_capability_options_alter_character_options_and_more.py @@ -56,7 +56,9 @@ class Migration(migrations.Migration): model_name="capability", name="limited", field=models.BooleanField( - blank=True, default=False, verbose_name="limitée" + blank=True, + default=False, + verbose_name="limitée", ), ), migrations.AlterField( @@ -108,7 +110,9 @@ class Migration(migrations.Migration): model_name="character", name="capabilities", field=models.ManyToManyField( - blank=True, to="character.capability", verbose_name="capacités" + blank=True, + to="character.capability", + verbose_name="capacités", ), ), migrations.AlterField( @@ -140,7 +144,7 @@ class Migration(migrations.Migration): model_name="character", name="health_remaining", field=models.PositiveSmallIntegerField( - verbose_name="points de vie restants" + verbose_name="points de vie restants", ), ), migrations.AlterField( @@ -162,14 +166,15 @@ class Migration(migrations.Migration): model_name="character", name="luck_points_remaining", field=models.PositiveSmallIntegerField( - verbose_name="points de chance restants" + verbose_name="points de chance restants", ), ), migrations.AlterField( model_name="character", name="mana_consumed", field=models.PositiveSmallIntegerField( - default=0, verbose_name="mana utilisé" + default=0, + verbose_name="mana utilisé", ), ), migrations.AlterField( @@ -261,7 +266,9 @@ class Migration(migrations.Migration): model_name="character", name="weapons", field=models.ManyToManyField( - blank=True, to="character.weapon", verbose_name="armes" + blank=True, + to="character.weapon", + verbose_name="armes", ), ), migrations.AlterField( diff --git a/src/character/migrations/0014_remove_character_mana_consumed_and_more.py b/src/character/migrations/0014_remove_character_mana_consumed_and_more.py index 49319ab..d930345 100644 --- a/src/character/migrations/0014_remove_character_mana_consumed_and_more.py +++ b/src/character/migrations/0014_remove_character_mana_consumed_and_more.py @@ -17,7 +17,8 @@ class Migration(migrations.Migration): model_name="character", name="mana_remaining", field=models.PositiveSmallIntegerField( - default=0, verbose_name="mana restant" + default=0, + verbose_name="mana restant", ), ), ] diff --git a/src/character/migrations/0015_character_recovery_points_remaining.py b/src/character/migrations/0015_character_recovery_points_remaining.py index 02c48d2..9cc1958 100644 --- a/src/character/migrations/0015_character_recovery_points_remaining.py +++ b/src/character/migrations/0015_character_recovery_points_remaining.py @@ -13,7 +13,8 @@ class Migration(migrations.Migration): model_name="character", name="recovery_points_remaining", field=models.PositiveSmallIntegerField( - default=5, verbose_name="points de récupération restants" + default=5, + verbose_name="points de récupération restants", ), ), ] diff --git a/src/character/migrations/0028_harmfulstate.py b/src/character/migrations/0028_harmfulstate.py index 1083615..f918b79 100644 --- a/src/character/migrations/0028_harmfulstate.py +++ b/src/character/migrations/0028_harmfulstate.py @@ -30,13 +30,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ("description", models.TextField()), diff --git a/src/character/migrations/0029_character_states.py b/src/character/migrations/0029_character_states.py index 085c22c..4c6b3c2 100644 --- a/src/character/migrations/0029_character_states.py +++ b/src/character/migrations/0029_character_states.py @@ -13,7 +13,8 @@ class Migration(migrations.Migration): model_name="character", name="states", field=models.ManyToManyField( - related_name="characters", to="character.harmfulstate" + related_name="characters", + to="character.harmfulstate", ), ), ] diff --git a/src/character/migrations/0030_alter_character_states.py b/src/character/migrations/0030_alter_character_states.py index de4e565..495ee6b 100644 --- a/src/character/migrations/0030_alter_character_states.py +++ b/src/character/migrations/0030_alter_character_states.py @@ -13,7 +13,9 @@ class Migration(migrations.Migration): model_name="character", name="states", field=models.ManyToManyField( - blank=True, related_name="characters", to="character.harmfulstate" + blank=True, + related_name="characters", + to="character.harmfulstate", ), ), ] diff --git a/src/character/migrations/0033_party.py b/src/character/migrations/0033_party.py index 84f1935..d1e1f25 100644 --- a/src/character/migrations/0033_party.py +++ b/src/character/migrations/0033_party.py @@ -32,13 +32,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ( diff --git a/src/character/migrations/0039_alter_character_profile_picture.py b/src/character/migrations/0039_alter_character_profile_picture.py index 92e2a8e..e001656 100644 --- a/src/character/migrations/0039_alter_character_profile_picture.py +++ b/src/character/migrations/0039_alter_character_profile_picture.py @@ -25,7 +25,7 @@ class Migration(migrations.Migration): character.models.character.validate_image, *(), **{"megabytes_limit": 2}, - ) + ), ], verbose_name="image de profil", ), diff --git a/src/character/models/capabilities.py b/src/character/models/capabilities.py index 415ad75..b16b87b 100644 --- a/src/character/models/capabilities.py +++ b/src/character/models/capabilities.py @@ -32,7 +32,9 @@ class Path(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model): CREATURE = "creature", "Créature" category = models.CharField( - max_length=20, choices=Category.choices, verbose_name="catégorie" + max_length=20, + choices=Category.choices, + verbose_name="catégorie", ) notes = models.TextField(blank=True, verbose_name="notes") @@ -93,13 +95,20 @@ class Capability(DocumentedModel, TimeStampedModel, models.Model): related_name="capabilities", ) rank = models.PositiveSmallIntegerField( - validators=[MinValueValidator(1), MaxValueValidator(5)], verbose_name="rang" + validators=[MinValueValidator(1), MaxValueValidator(5)], + verbose_name="rang", ) limited = models.BooleanField( - blank=True, null=False, default=False, verbose_name="limitée" + blank=True, + null=False, + default=False, + verbose_name="limitée", ) spell = models.BooleanField( - blank=True, null=False, default=False, verbose_name="sort" + blank=True, + null=False, + default=False, + verbose_name="sort", ) description = models.TextField(verbose_name="description") @@ -134,7 +143,9 @@ class RacialCapabilityManager(models.Manager): class RacialCapability(DocumentedModel, TimeStampedModel, models.Model): name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom") race = models.ForeignKey( - "character.Race", on_delete=models.CASCADE, verbose_name="race" + "character.Race", + on_delete=models.CASCADE, + verbose_name="race", ) description = models.TextField(verbose_name="description") diff --git a/src/character/models/character.py b/src/character/models/character.py index b838918..e2899b7 100644 --- a/src/character/models/character.py +++ b/src/character/models/character.py @@ -17,7 +17,12 @@ from character.models.equipment import Weapon from common.models import DocumentedModel, UniquelyNamedModel -class Profile(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model): +class Profile( # noqa: DJ008 + DocumentedModel, + UniquelyNamedModel, + TimeStampedModel, + models.Model, +): class MagicalStrength(models.TextChoices): NONE = "NON", "Aucun" INTELLIGENCE = "INT", "Intelligence" @@ -36,10 +41,13 @@ class Profile(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Mode verbose_name="force magique", ) life_dice = models.PositiveSmallIntegerField( - choices=Dice.choices, verbose_name="dé de vie" + choices=Dice.choices, + verbose_name="dé de vie", ) mana_max_compute = models.PositiveSmallIntegerField( - choices=ManaMax.choices, verbose_name="calcul mana max", default=ManaMax.NO_MANA + choices=ManaMax.choices, + verbose_name="calcul mana max", + default=ManaMax.NO_MANA, ) notes = models.TextField(blank=True, verbose_name="notes") @@ -48,13 +56,23 @@ class Profile(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Mode verbose_name_plural = "Profils" -class Race(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model): +class Race( # noqa: DJ008 + DocumentedModel, + UniquelyNamedModel, + TimeStampedModel, + models.Model, +): class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta): verbose_name = "Race" verbose_name_plural = "Races" -class HarmfulState(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model): +class HarmfulState( # noqa: DJ008 + DocumentedModel, + UniquelyNamedModel, + TimeStampedModel, + models.Model, +): description = models.TextField() icon_url = models.URLField() @@ -88,7 +106,7 @@ class CharacterQuerySet(models.QuerySet): from party.models import Party return self.filter( - Q(player=user) | Q(parties__in=Party.objects.managed_by(user)) + Q(player=user) | Q(parties__in=Party.objects.managed_by(user)), ) def mastered_by(self, user): @@ -113,7 +131,7 @@ class CharacterQuerySet(models.QuerySet): return self.filter( Q(player=user) | Q(parties__in=Party.objects.related_to(user)) - | Q(invites__in=Party.objects.related_to(user)) + | Q(invites__in=Party.objects.related_to(user)), ).distinct() @@ -184,7 +202,10 @@ class Character(models.Model): level = models.PositiveSmallIntegerField(verbose_name="niveau", default=1) gender = models.CharField( - max_length=1, choices=Gender.choices, default=Gender.OTHER, verbose_name="genre" + max_length=1, + choices=Gender.choices, + default=Gender.OTHER, + verbose_name="genre", ) age = models.PositiveSmallIntegerField(verbose_name="âge") height = models.PositiveSmallIntegerField(verbose_name="taille") @@ -193,17 +214,17 @@ class Character(models.Model): value_strength = models.PositiveSmallIntegerField(verbose_name="valeur force") value_dexterity = models.PositiveSmallIntegerField(verbose_name="valeur dextérité") value_constitution = models.PositiveSmallIntegerField( - verbose_name="valeur constitution" + verbose_name="valeur constitution", ) value_intelligence = models.PositiveSmallIntegerField( - verbose_name="valeur intelligence" + verbose_name="valeur intelligence", ) value_wisdom = models.PositiveSmallIntegerField(verbose_name="valeur sagesse") value_charisma = models.PositiveSmallIntegerField(verbose_name="valeur charisme") health_max = models.PositiveSmallIntegerField(verbose_name="points de vie max") health_remaining = models.PositiveSmallIntegerField( - verbose_name="points de vie restants" + verbose_name="points de vie restants", ) racial_capability = models.ForeignKey( @@ -214,7 +235,9 @@ class Character(models.Model): ) weapons = models.ManyToManyField( - "character.Weapon", blank=True, verbose_name="armes" + "character.Weapon", + blank=True, + verbose_name="armes", ) armor = models.PositiveSmallIntegerField(verbose_name="armure", default=0) @@ -222,20 +245,24 @@ class Character(models.Model): defense_misc = models.SmallIntegerField(verbose_name="divers défense", default=0) initiative_misc = models.SmallIntegerField( - verbose_name="divers initiative", default=0 + verbose_name="divers initiative", + default=0, ) capabilities = models.ManyToManyField( - "character.Capability", blank=True, verbose_name="capacités" + "character.Capability", + blank=True, + verbose_name="capacités", ) equipment = models.TextField(blank=True, verbose_name="équipement") luck_points_remaining = models.PositiveSmallIntegerField( - verbose_name="points de chance restants" + verbose_name="points de chance restants", ) mana_remaining = models.PositiveSmallIntegerField( - default=0, verbose_name="mana restant" + default=0, + verbose_name="mana restant", ) money_pp = models.PositiveSmallIntegerField(default=0, verbose_name="PP") @@ -244,7 +271,8 @@ class Character(models.Model): money_pc = models.PositiveSmallIntegerField(default=0, verbose_name="PC") recovery_points_remaining = models.PositiveSmallIntegerField( - default=5, verbose_name="points de récupération restants" + default=5, + verbose_name="points de récupération restants", ) notes = models.TextField(blank=True, verbose_name="notes", default=DEFAULT_NOTES) @@ -267,8 +295,10 @@ class Character(models.Model): verbose_name_plural = "Personnages" constraints = [ models.UniqueConstraint( - Lower("name"), "player", name="unique_character_player" - ) + Lower("name"), + "player", + name="unique_character_player", + ), ] def __str__(self): @@ -329,7 +359,8 @@ class Character(models.Model): Profile.MagicalStrength.NONE: 0, } return modifier_map.get( - Profile.MagicalStrength(self.profile.magical_strength), 0 + Profile.MagicalStrength(self.profile.magical_strength), + 0, ) @property @@ -403,8 +434,9 @@ class Character(models.Model): for capability in path.capabilities.all(): capabilities_by_path[capability.path].append( CharacterCapability( - capability, known=capability in character_capabilities - ) + capability, + known=capability in character_capabilities, + ), ) return dict( @@ -414,7 +446,7 @@ class Character(models.Model): for path, capabilities in capabilities_by_path.items() ), key=lambda x: x[0].name, - ) + ), ) def get_formatted_notes(self) -> str: @@ -427,7 +459,7 @@ class Character(models.Model): def get_missing_states(self) -> Iterable[HarmfulState]: return HarmfulState.objects.exclude( - pk__in=self.states.all().values_list("pk", flat=True) + pk__in=self.states.all().values_list("pk", flat=True), ) def managed_by(self, user): diff --git a/src/character/models/equipment.py b/src/character/models/equipment.py index c74ed30..fd6d163 100644 --- a/src/character/models/equipment.py +++ b/src/character/models/equipment.py @@ -4,7 +4,12 @@ from django_extensions.db.models import TimeStampedModel from common.models import DocumentedModel, UniquelyNamedModel -class Weapon(UniquelyNamedModel, DocumentedModel, TimeStampedModel, models.Model): +class Weapon( # noqa: DJ008 + UniquelyNamedModel, + DocumentedModel, + TimeStampedModel, + models.Model, +): class Category(models.TextChoices): MELEE = "MEL", "corps à corps" RANGE = "RAN", "à distance" @@ -13,7 +18,9 @@ class Weapon(UniquelyNamedModel, DocumentedModel, TimeStampedModel, models.Model damage = models.CharField(max_length=50, blank=True, verbose_name="dégâts") special = models.TextField(blank=True, verbose_name="spécial") category = models.CharField( - max_length=3, choices=Category.choices, default=Category.NONE + max_length=3, + choices=Category.choices, + default=Category.NONE, ) class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta): diff --git a/src/character/tests/test_access.py b/src/character/tests/test_access.py index af3a4fc..6116ef4 100644 --- a/src/character/tests/test_access.py +++ b/src/character/tests/test_access.py @@ -44,7 +44,10 @@ def test_can_access_character_in_party(client): notes = "Some notes" gm_notes = "Some GM notes" friend_character = baker.make( - Character, player=friend, notes=notes, gm_notes=gm_notes + Character, + player=friend, + notes=notes, + gm_notes=gm_notes, ) party = baker.make(Party) party.characters.add(character) diff --git a/src/character/tests/test_character.py b/src/character/tests/test_character.py index eb87589..274fa4d 100644 --- a/src/character/tests/test_character.py +++ b/src/character/tests/test_character.py @@ -68,7 +68,10 @@ def test_attack_range(level, dexterity): @given(armor=integers(), shield=integers(), dexterity=ability_values(), misc=integers()) def test_defense(armor, shield, dexterity, misc): char = Character( - armor=armor, shield=shield, value_dexterity=dexterity, defense_misc=misc + armor=armor, + shield=shield, + value_dexterity=dexterity, + defense_misc=misc, ) assert char.defense == 10 + armor + shield + modifier_test(dexterity) + misc diff --git a/src/character/tests/test_interactions.py b/src/character/tests/test_interactions.py index 70ae403..939730e 100644 --- a/src/character/tests/test_interactions.py +++ b/src/character/tests/test_interactions.py @@ -118,7 +118,8 @@ def test_list_characters(selenium: WebDriver, live_server: LiveServer): names = { name.text for name in selenium.find_elements( - By.CSS_SELECTOR, ".character.card .card-title" + By.CSS_SELECTOR, + ".character.card .card-title", ) } expected_names = {character.name for character in characters} @@ -137,7 +138,8 @@ def test_delete_character(selenium: WebDriver, live_server: LiveServer): assert Character.objects.count() == 2 selenium.find_element( - By.CSS_SELECTOR, f".character.card[data-id='{characters[0].pk}'] .delete" + By.CSS_SELECTOR, + f".character.card[data-id='{characters[0].pk}'] .delete", ).click() selenium.find_element(By.CSS_SELECTOR, "[type=submit]").click() @@ -148,7 +150,9 @@ def test_delete_character(selenium: WebDriver, live_server: LiveServer): @pytest.mark.django_db() def test_reset_stats_view( - selenium: WebDriver, live_server: LiveServer, initial_data: None + selenium: WebDriver, + live_server: LiveServer, + initial_data: None, ): username, password = "user", "some_password" player = User.objects.create_user(username, password=password) @@ -186,7 +190,10 @@ def create_hurt_character(player, profile): def login( - selenium: WebDriver, live_server: LiveServer, username: str, password: str + selenium: WebDriver, + live_server: LiveServer, + username: str, + password: str, ) -> None: selenium.get(live_server.url) selenium.find_element(By.ID, "login").click() diff --git a/src/character/urls.py b/src/character/urls.py index 2ef18a9..d21e8c1 100644 --- a/src/character/urls.py +++ b/src/character/urls.py @@ -10,7 +10,9 @@ urlpatterns = [ path("/change/", views.character_change, name="change"), path("/delete/", views.character_delete, name="delete"), path( - "/health_change/", views.character_health_change, name="health_change" + "/health_change/", + views.character_health_change, + name="health_change", ), path("/mana_change/", views.character_mana_change, name="mana_change"), path( @@ -87,7 +89,9 @@ urlpatterns = [ ), path("/add_path/", views.add_path, name="add_path"), path( - "/remove_state//", views.remove_state, name="remove_state" + "/remove_state//", + views.remove_state, + name="remove_state", ), path("/add_state//", views.add_state, name="add_state"), path("/reset_stats/", views.reset_stats, name="reset_stats"), diff --git a/src/character/views.py b/src/character/views.py index 491bc83..f5000cc 100644 --- a/src/character/views.py +++ b/src/character/views.py @@ -14,7 +14,8 @@ from party.models import Party def characters_list(request): context = { "characters": Character.objects.owned_by(request.user).select_related( - "race", "profile" + "race", + "profile", ), "all_states": HarmfulState.objects.all(), } @@ -99,7 +100,7 @@ def add_path(request, pk: int): context = {"character": character} if form.is_valid(): path: Path = form.cleaned_data.get("character_path") or form.cleaned_data.get( - "other_path" + "other_path", ) cap = path.get_next_capability(character) character.capabilities.add(cap) @@ -117,7 +118,8 @@ def add_path(request, pk: int): def character_health_change(request, pk: int): character = get_object_or_404( Character.objects.managed_by(request.user).only( - "health_max", "health_remaining" + "health_max", + "health_remaining", ), pk=pk, ) @@ -150,7 +152,9 @@ def character_recovery_points_change(request, pk: int): pk=pk, ) value = get_updated_value( - request, character.recovery_points_remaining, character.recovery_points_max + request, + character.recovery_points_remaining, + character.recovery_points_max, ) character.recovery_points_remaining = value character.save(update_fields=["recovery_points_remaining"]) @@ -160,7 +164,8 @@ def character_recovery_points_change(request, pk: int): @login_required def character_defense_misc_change(request, pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user).only("defense_misc"), pk=pk + Character.objects.managed_by(request.user).only("defense_misc"), + pk=pk, ) value = get_updated_value(request, character.defense_misc, float("inf")) character.defense_misc = value @@ -172,7 +177,8 @@ def character_defense_misc_change(request, pk: int): @login_required def character_shield_change(request, pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user).only("shield"), pk=pk + Character.objects.managed_by(request.user).only("shield"), + pk=pk, ) value = get_updated_value(request, character.shield, float("inf")) character.shield = value @@ -184,7 +190,8 @@ def character_shield_change(request, pk: int): @login_required def character_armor_change(request, pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user).only("armor"), pk=pk + Character.objects.managed_by(request.user).only("armor"), + pk=pk, ) value = get_updated_value(request, character.armor, float("inf")) character.armor = value @@ -196,7 +203,8 @@ def character_armor_change(request, pk: int): @login_required def character_initiative_misc_change(request, pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user).only("initiative_misc"), pk=pk + Character.objects.managed_by(request.user).only("initiative_misc"), + pk=pk, ) value = get_updated_value(request, character.initiative_misc, float("inf")) character.initiative_misc = value @@ -209,12 +217,15 @@ def character_initiative_misc_change(request, pk: int): def character_luck_points_change(request, pk: int): character = get_object_or_404( Character.objects.managed_by(request.user).only( - "luck_points_remaining", "value_charisma" + "luck_points_remaining", + "value_charisma", ), pk=pk, ) value = get_updated_value( - request, character.luck_points_remaining, character.luck_points_max + request, + character.luck_points_remaining, + character.luck_points_max, ) character.luck_points_remaining = value character.save(update_fields=["luck_points_remaining"]) @@ -222,7 +233,9 @@ def character_luck_points_change(request, pk: int): def get_updated_value( - request, remaining_value: int | float, max_value: int | float + request, + remaining_value: int | float, + max_value: int | float, ) -> int: form_value = request.GET.get("value") if form_value == "ko": @@ -241,7 +254,10 @@ def get_updated_value( def character_get_defense(request, pk: int): character = get_object_or_404( Character.objects.managed_by(request.user).only( - "defense_misc", "armor", "shield", "value_dexterity" + "defense_misc", + "armor", + "shield", + "value_dexterity", ), pk=pk, ) @@ -252,13 +268,16 @@ def character_get_defense(request, pk: int): def character_get_health_bar(request, pk: int): character = get_object_or_404( Character.objects.managed_by(request.user).only( - "health_max", "health_remaining" + "health_max", + "health_remaining", ), pk=pk, ) context = {"character": character} return render( - request, "character/snippets/character_details/health_bar.html", context + request, + "character/snippets/character_details/health_bar.html", + context, ) @@ -270,7 +289,9 @@ def character_get_mana_bar(request, pk: int): ) context = {"character": character} return render( - request, "character/snippets/character_details/mana_bar.html", context + request, + "character/snippets/character_details/mana_bar.html", + context, ) @@ -278,7 +299,8 @@ def character_get_mana_bar(request, pk: int): def character_get_initiative(request, pk: int): character = get_object_or_404( Character.objects.managed_by(request.user).only( - "initiative_misc", "value_dexterity" + "initiative_misc", + "value_dexterity", ), pk=pk, ) @@ -299,7 +321,8 @@ def character_gm_notes_change(request, pk: int): def character_equipment_change(request, pk: int): field = "equipment" character = get_object_or_404( - Character.objects.managed_by(request.user).only(field), pk=pk + Character.objects.managed_by(request.user).only(field), + pk=pk, ) context = {"character": character} if request.method == "GET": @@ -331,7 +354,8 @@ def character_damage_reduction_change(request, pk: int): def update_text_field(request, pk, field): character = get_object_or_404( - Character.objects.managed_by(request.user).only(field), pk=pk + Character.objects.managed_by(request.user).only(field), + pk=pk, ) context = {"character": character} if request.method == "GET": @@ -343,14 +367,17 @@ def update_text_field(request, pk, field): setattr(character, field, request.POST.get(field)) character.save(update_fields=[field]) return render( - request, f"character/snippets/character_details/{field}_display.html", context + request, + f"character/snippets/character_details/{field}_display.html", + context, ) @login_required def add_next_in_path(request, character_pk: int, path_pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user), pk=character_pk + Character.objects.managed_by(request.user), + pk=character_pk, ) path = get_object_or_404(Path, pk=path_pk) capability = path.get_next_capability(character) @@ -369,10 +396,11 @@ def add_next_in_path(request, character_pk: int, path_pk: int): @login_required def remove_last_in_path(request, character_pk: int, path_pk: int): character = get_object_or_404( - Character.objects.managed_by(request.user), pk=character_pk + Character.objects.managed_by(request.user), + pk=character_pk, ) last_rank = max( - character.capabilities.filter(path_id=path_pk).values_list("rank", flat=True) + character.capabilities.filter(path_id=path_pk).values_list("rank", flat=True), ) cap = Capability.objects.get(path_id=path_pk, rank=last_rank) character.capabilities.remove(cap) @@ -390,13 +418,16 @@ def remove_last_in_path(request, character_pk: int, path_pk: int): @login_required def remove_state(request, pk: int, state_pk: int): character: Character = get_object_or_404( - Character.objects.managed_by(request.user), pk=pk + Character.objects.managed_by(request.user), + pk=pk, ) state = get_object_or_404(HarmfulState, pk=state_pk) character.states.remove(state) context = {"character": character, "all_states": HarmfulState.objects.all()} response = render( - request, "character/snippets/character_details/states.html", context + request, + "character/snippets/character_details/states.html", + context, ) return trigger_client_event(response, "refresh_tooltips", after="swap") @@ -404,13 +435,16 @@ def remove_state(request, pk: int, state_pk: int): @login_required def add_state(request, pk: int, state_pk: int): character: Character = get_object_or_404( - Character.objects.managed_by(request.user), pk=pk + Character.objects.managed_by(request.user), + pk=pk, ) state = get_object_or_404(HarmfulState, pk=state_pk) character.states.add(state) context = {"character": character, "all_states": HarmfulState.objects.all()} response = render( - request, "character/snippets/character_details/states.html", context + request, + "character/snippets/character_details/states.html", + context, ) return trigger_client_event(response, "refresh_tooltips", after="swap") @@ -418,7 +452,8 @@ def add_state(request, pk: int, state_pk: int): @login_required def reset_stats(request, pk: int): character: Character = get_object_or_404( - Character.objects.managed_by(request.user), pk=pk + Character.objects.managed_by(request.user), + pk=pk, ) context = {"character": character} if request.method == "POST": diff --git a/src/charasheet/settings.py b/src/charasheet/settings.py index 4644050..ec98c00 100644 --- a/src/charasheet/settings.py +++ b/src/charasheet/settings.py @@ -128,7 +128,7 @@ DATABASES = {"default": env.db()} CACHES = { "default": { "BACKEND": "django.core.cache.backends.db.DatabaseCache", - } + }, } SOLO_CACHE = "default" @@ -244,7 +244,7 @@ APP = { "date": "latest-date", "commit": "latest-commit", "describe": "latest-describe", - } + }, } try: with Path("/app/git/build-date").open() as f: diff --git a/src/common/migrations/0001_initial.py b/src/common/migrations/0001_initial.py index 65ec892..5df98e7 100644 --- a/src/common/migrations/0001_initial.py +++ b/src/common/migrations/0001_initial.py @@ -28,7 +28,9 @@ class Migration(migrations.Migration): ( "last_login", models.DateTimeField( - blank=True, null=True, verbose_name="last login" + blank=True, + null=True, + verbose_name="last login", ), ), ( @@ -43,13 +45,13 @@ class Migration(migrations.Migration): "username", models.CharField( error_messages={ - "unique": "A user with that username already exists." + "unique": "A user with that username already exists.", }, help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, validators=[ - django.contrib.auth.validators.UnicodeUsernameValidator() + django.contrib.auth.validators.UnicodeUsernameValidator(), ], verbose_name="username", ), @@ -57,19 +59,25 @@ class Migration(migrations.Migration): ( "first_name", models.CharField( - blank=True, max_length=150, verbose_name="first name" + blank=True, + max_length=150, + verbose_name="first name", ), ), ( "last_name", models.CharField( - blank=True, max_length=150, verbose_name="last name" + blank=True, + max_length=150, + verbose_name="last name", ), ), ( "email", models.EmailField( - blank=True, max_length=254, verbose_name="email address" + blank=True, + max_length=254, + verbose_name="email address", ), ), ( @@ -91,7 +99,8 @@ class Migration(migrations.Migration): ( "date_joined", models.DateTimeField( - default=django.utils.timezone.now, verbose_name="date joined" + default=django.utils.timezone.now, + verbose_name="date joined", ), ), ( diff --git a/src/common/models.py b/src/common/models.py index 70871d1..802fc18 100644 --- a/src/common/models.py +++ b/src/common/models.py @@ -13,7 +13,11 @@ class UniquelyNamedModelManager(models.Manager): class UniquelyNamedModel(models.Model): name = models.CharField( - max_length=100, blank=False, null=False, unique=True, verbose_name="nom" + max_length=100, + blank=False, + null=False, + unique=True, + verbose_name="nom", ) objects = UniquelyNamedModelManager() diff --git a/src/manage.py b/src/manage.py index 4d52876..35b3d3a 100755 --- a/src/manage.py +++ b/src/manage.py @@ -9,11 +9,12 @@ def main(): try: from django.core.management import execute_from_command_line except ImportError as exc: - raise ImportError( + msg = ( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" - ) from exc + ) + raise ImportError(msg) from exc execute_from_command_line(sys.argv) diff --git a/src/party/forms.py b/src/party/forms.py index 22f6b49..0757527 100644 --- a/src/party/forms.py +++ b/src/party/forms.py @@ -7,7 +7,7 @@ from party.models import BattleEffect, Party class PartyForm(forms.ModelForm): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: self.original_instance = kwargs.get("instance") super().__init__(*args, **kwargs) qs = Character.objects.all() @@ -16,9 +16,10 @@ class PartyForm(forms.ModelForm): Q(private=False) | Q( pk__in=self.original_instance.invited_characters.all().values_list( - "pk", flat=True - ) - ) + "pk", + flat=True, + ), + ), ) self.fields["invited_characters"].queryset = qs diff --git a/src/party/migrations/0001_initial.py b/src/party/migrations/0001_initial.py index 7191099..95bc63a 100644 --- a/src/party/migrations/0001_initial.py +++ b/src/party/migrations/0001_initial.py @@ -34,13 +34,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ( diff --git a/src/party/migrations/0003_battleeffect.py b/src/party/migrations/0003_battleeffect.py index 0aa2a8e..2e8aca0 100644 --- a/src/party/migrations/0003_battleeffect.py +++ b/src/party/migrations/0003_battleeffect.py @@ -28,13 +28,15 @@ class Migration(migrations.Migration): ( "created", django_extensions.db.fields.CreationDateTimeField( - auto_now_add=True, verbose_name="created" + auto_now_add=True, + verbose_name="created", ), ), ( "modified", django_extensions.db.fields.ModificationDateTimeField( - auto_now=True, verbose_name="modified" + auto_now=True, + verbose_name="modified", ), ), ("name", models.CharField(max_length=100, verbose_name="nom")), diff --git a/src/party/models.py b/src/party/models.py index 83ffbeb..e07c5c1 100644 --- a/src/party/models.py +++ b/src/party/models.py @@ -17,14 +17,14 @@ class PartyQuerySet(models.QuerySet): def played_or_mastered_by(self, user): return self.filter( Q(game_master=user) - | Q(characters__in=Character.objects.filter(player=user)) + | Q(characters__in=Character.objects.filter(player=user)), ).distinct() def related_to(self, user): return self.filter( Q(game_master=user) | Q(characters__in=Character.objects.filter(player=user)) - | Q(invited_characters__in=Character.objects.filter(player=user)) + | Q(invited_characters__in=Character.objects.filter(player=user)), ).distinct() def invited_to(self, user): @@ -35,7 +35,7 @@ class PartyManager(UniquelyNamedModelManager): pass -class Party(UniquelyNamedModel, TimeStampedModel, models.Model): +class Party(UniquelyNamedModel, TimeStampedModel, models.Model): # noqa: DJ008 game_master = models.ForeignKey( "common.User", on_delete=models.PROTECT, @@ -96,7 +96,10 @@ class BattleEffectManager(models.Manager): class BattleEffect(TimeStampedModel, models.Model): name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom") target = models.CharField( - max_length=100, blank=False, null=False, verbose_name="cible" + max_length=100, + blank=False, + null=False, + verbose_name="cible", ) description = models.TextField(blank=True, null=False, verbose_name="description") remaining_rounds = models.SmallIntegerField( @@ -126,3 +129,6 @@ class BattleEffect(TimeStampedModel, models.Model): if self.remaining_rounds >= max_display_percent or self.remaining_rounds < 0: return 100 return self.remaining_rounds / max_display_percent * 100 + + def __str__(self): + return self.name diff --git a/src/party/tests/test_interactions.py b/src/party/tests/test_interactions.py index 06bf7df..3b6aefb 100644 --- a/src/party/tests/test_interactions.py +++ b/src/party/tests/test_interactions.py @@ -28,7 +28,8 @@ def test_add_character_to_existing_group(selenium: WebDriver, live_server: LiveS selenium.get(live_server.url + reverse("party:list")) selenium.find_element( - By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .edit" + By.CSS_SELECTOR, + f".party[data-id='{party.pk}'] .edit", ).click() invited = Select(selenium.find_element(By.ID, "id_invited_characters")) invited.select_by_index(0) @@ -41,7 +42,8 @@ def test_add_character_to_existing_group(selenium: WebDriver, live_server: LiveS @pytest.mark.django_db() def test_gm_observe_invited_character_in_group( - selenium: WebDriver, live_server: LiveServer + selenium: WebDriver, + live_server: LiveServer, ): username, password = "gm", "password" gm = User.objects.create_user(username, password=password) @@ -54,10 +56,12 @@ def test_gm_observe_invited_character_in_group( selenium.get(live_server.url + reverse("party:list")) selenium.find_element( - By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .access" + By.CSS_SELECTOR, + f".party[data-id='{party.pk}'] .access", ).click() selenium.find_element( - By.CSS_SELECTOR, f".character[data-id='{character.pk}'] .observe" + By.CSS_SELECTOR, + f".character[data-id='{character.pk}'] .observe", ).click() title = selenium.find_element(By.TAG_NAME, "h1").text.strip() assert title == character.name @@ -65,7 +69,8 @@ def test_gm_observe_invited_character_in_group( @pytest.mark.django_db() def test_gm_observe_invited_character_in_two_groups( - selenium: WebDriver, live_server: LiveServer + selenium: WebDriver, + live_server: LiveServer, ): username, password = "gm", "password" gm = User.objects.create_user(username, password=password) @@ -80,10 +85,12 @@ def test_gm_observe_invited_character_in_two_groups( selenium.get(live_server.url + reverse("party:list")) selenium.find_element( - By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .access" + By.CSS_SELECTOR, + f".party[data-id='{party.pk}'] .access", ).click() selenium.find_element( - By.CSS_SELECTOR, f".character[data-id='{character.pk}'] .observe" + By.CSS_SELECTOR, + f".character[data-id='{character.pk}'] .observe", ).click() title = selenium.find_element(By.TAG_NAME, "h1").text.strip() assert title == character.name @@ -91,7 +98,9 @@ def test_gm_observe_invited_character_in_two_groups( @pytest.mark.django_db() def test_reset_stats_view( - selenium: WebDriver, live_server: LiveServer, initial_data: None + selenium: WebDriver, + live_server: LiveServer, + initial_data: None, ): user, password = "gm", "password" gm = User.objects.create_user(user, password=password) @@ -189,13 +198,22 @@ def test_gm_can_change_remaining_rounds(selenium: WebDriver, live_server: LiveSe party=party, ) active_nearly_terminated = baker.make( - BattleEffect, _quantity=3, remaining_rounds=1, party=party + BattleEffect, + _quantity=3, + remaining_rounds=1, + party=party, ) terminated = baker.make( # noqa: F841 - BattleEffect, _quantity=5, remaining_rounds=0, party=party + BattleEffect, + _quantity=5, + remaining_rounds=0, + party=party, ) permanent = baker.make( # noqa: F841 - BattleEffect, _quantity=2, remaining_rounds=-1, party=party + BattleEffect, + _quantity=2, + remaining_rounds=-1, + party=party, ) not_party = baker.make(BattleEffect, _quantity=4, remaining_rounds=55) # noqa: F841 beacon = active_nearly_terminated[0] @@ -246,7 +264,8 @@ def test_gm_can_change_remaining_rounds(selenium: WebDriver, live_server: LiveSe @pytest.mark.django_db() def test_gm_can_delete_any_existing_effect( - selenium: WebDriver, live_server: LiveServer + selenium: WebDriver, + live_server: LiveServer, ): """The GM of a group can delete any existing effect, running or terminated.""" user, password = "gm", "password" @@ -258,7 +277,8 @@ def test_gm_can_delete_any_existing_effect( go_to_party(selenium, live_server, party, user, password) selenium.find_element( - By.CSS_SELECTOR, f'.effect[data-id="{effects[0].pk}"] .delete' + By.CSS_SELECTOR, + f'.effect[data-id="{effects[0].pk}"] .delete', ).click() assert BattleEffect.objects.count() == 1 @@ -267,7 +287,8 @@ def test_gm_can_delete_any_existing_effect( @pytest.mark.django_db() def test_player_cant_change_existing_running_effect( - selenium: WebDriver, live_server: LiveServer + selenium: WebDriver, + live_server: LiveServer, ): """Members of the group can only view existing running effects, no update.""" user, password = "player", "password" @@ -280,7 +301,8 @@ def test_player_cant_change_existing_running_effect( go_to_party(selenium, live_server, party, user, password) effect = effects[0] effect_element = selenium.find_element( - By.CSS_SELECTOR, f'.effect[data-id="{effect.pk}"]' + By.CSS_SELECTOR, + f'.effect[data-id="{effect.pk}"]', ) assert effect.name in effect_element.text assert effect.target in effect_element.text @@ -295,7 +317,11 @@ def test_player_cant_change_existing_running_effect( def fill_effect( - selenium: WebDriver, name: str, description: str, target: str, remaining_rounds: str + selenium: WebDriver, + name: str, + description: str, + target: str, + remaining_rounds: str, ) -> None: selenium.find_element(By.ID, "add-effect").click() selenium.find_element(By.ID, "id_name").send_keys(name) @@ -308,7 +334,10 @@ def fill_effect( def assert_effect_is_created( - name: str, description: str, target: str, remaining_rounds: str + name: str, + description: str, + target: str, + remaining_rounds: str, ) -> BattleEffect: assert BattleEffect.objects.count() == 1 effect = BattleEffect.objects.first() @@ -320,7 +349,11 @@ def assert_effect_is_created( def go_to_party( - selenium: WebDriver, live_server: LiveServer, party: Party, user: str, password: str + selenium: WebDriver, + live_server: LiveServer, + party: Party, + user: str, + password: str, ) -> None: login(selenium, live_server, user, password) url = reverse("party:details", kwargs={"pk": party.pk}) diff --git a/src/party/urls.py b/src/party/urls.py index bcd34c8..17bbb57 100644 --- a/src/party/urls.py +++ b/src/party/urls.py @@ -17,10 +17,14 @@ urlpatterns = [ name="delete_effect", ), path( - "/increase_rounds/", views.party_increase_rounds, name="increase_rounds" + "/increase_rounds/", + views.party_increase_rounds, + name="increase_rounds", ), path( - "/decrease_rounds/", views.party_decrease_rounds, name="decrease_rounds" + "/decrease_rounds/", + views.party_decrease_rounds, + name="decrease_rounds", ), path("/leave//", views.party_leave, name="leave"), path("/join//", views.party_join, name="join"), diff --git a/src/party/views.py b/src/party/views.py index 7f4d816..e987b5f 100644 --- a/src/party/views.py +++ b/src/party/views.py @@ -139,7 +139,8 @@ def party_change(request, pk): def party_leave(request, pk, character_pk): party = get_object_or_404(Party.objects.played_by(request.user).distinct(), pk=pk) character = get_object_or_404( - Character.objects.owned_by(request.user), pk=character_pk + Character.objects.owned_by(request.user), + pk=character_pk, ) context = {"party": party, "character": character} if request.method == "POST": @@ -154,7 +155,8 @@ def party_leave(request, pk, character_pk): def party_join(request, pk, character_pk): party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk) character = get_object_or_404( - Character.objects.owned_by(request.user), pk=character_pk + Character.objects.owned_by(request.user), + pk=character_pk, ) party.characters.add(character) party.invited_characters.remove(character) @@ -167,7 +169,8 @@ def party_join(request, pk, character_pk): def party_refuse(request, pk, character_pk): party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk) character = get_object_or_404( - Character.objects.owned_by(request.user), pk=character_pk + Character.objects.owned_by(request.user), + pk=character_pk, ) party.invited_characters.remove(character) messages.success(request, f"{character} a refusé l'invitation au groupe {party}.") diff --git a/tasks.py b/tasks.py index 308813f..c27672e 100644 --- a/tasks.py +++ b/tasks.py @@ -84,7 +84,9 @@ def download_db(ctx: Context): ) ctx.run("rm -rf src/media/", pty=True, echo=True) ctx.run( - "scp -r ubuntu:/mnt/data/charasheet/media/ ./src/media", pty=True, echo=True + "scp -r ubuntu:/mnt/data/charasheet/media/ ./src/media", + pty=True, + echo=True, ) with ctx.cd(SRC_DIR): ctx.run("./manage.py changepassword gaugendre", pty=True, echo=True)