mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-11-22 06:28:03 +01:00
Enable more ruff rules
This commit is contained in:
parent
bcabc15054
commit
16d7ff5d20
38 changed files with 394 additions and 215 deletions
|
@ -36,50 +36,12 @@ python_files = [
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
src = ["src"]
|
src = ["src"]
|
||||||
target-version = "py311"
|
target-version = "py311"
|
||||||
select = [
|
select = ["ALL"]
|
||||||
"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
|
|
||||||
]
|
|
||||||
unfixable = ["T20", "RUF001", "RUF002", "RUF003"]
|
unfixable = ["T20", "RUF001", "RUF002", "RUF003"]
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
"UP", # pyupgrade
|
|
||||||
"YTT", # flake8-2020
|
|
||||||
"ANN", # flake8-annotations
|
"ANN", # flake8-annotations
|
||||||
"BLE", # flake8-blind-except
|
"BLE", # flake8-blind-except
|
||||||
"COM", # flake8-commas
|
|
||||||
"EM", # flake8-errmsg
|
|
||||||
"Q", # flake8-quotes
|
|
||||||
"TCH", # flake8-type-checking / TODO: revisit later ?
|
"TCH", # flake8-type-checking / TODO: revisit later ?
|
||||||
|
|
||||||
"E501", # long lines
|
"E501", # long lines
|
||||||
|
|
|
@ -92,12 +92,14 @@ class RaceAdmin(admin.ModelAdmin):
|
||||||
class CharacterAdminForm(ModelForm):
|
class CharacterAdminForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Character
|
model = models.Character
|
||||||
exclude = ()
|
exclude = () # noqa: DJ006
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields["capabilities"].queryset = models.Capability.objects.select_related(
|
self.fields["capabilities"].queryset = models.Capability.objects.select_related(
|
||||||
"path", "path__race", "path__profile"
|
"path",
|
||||||
|
"path__race",
|
||||||
|
"path__profile",
|
||||||
)
|
)
|
||||||
self.fields[
|
self.fields[
|
||||||
"racial_capability"
|
"racial_capability"
|
||||||
|
@ -127,7 +129,7 @@ class CharacterAdmin(admin.ModelAdmin):
|
||||||
"level",
|
"level",
|
||||||
"race",
|
"race",
|
||||||
"private",
|
"private",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("Apparence", {"fields": ["gender", "age", "height", "weight"]}),
|
("Apparence", {"fields": ["gender", "age", "height", "weight"]}),
|
||||||
|
@ -141,7 +143,7 @@ class CharacterAdmin(admin.ModelAdmin):
|
||||||
("value_intelligence", "modifier_intelligence"),
|
("value_intelligence", "modifier_intelligence"),
|
||||||
("value_wisdom", "modifier_wisdom"),
|
("value_wisdom", "modifier_wisdom"),
|
||||||
("value_charisma", "modifier_charisma"),
|
("value_charisma", "modifier_charisma"),
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -153,7 +155,7 @@ class CharacterAdmin(admin.ModelAdmin):
|
||||||
"attack_range",
|
"attack_range",
|
||||||
"attack_magic",
|
"attack_magic",
|
||||||
"states",
|
"states",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("Vitalité", {"fields": [("health_max", "health_remaining")]}),
|
("Vitalité", {"fields": [("health_max", "health_remaining")]}),
|
||||||
|
@ -165,7 +167,7 @@ class CharacterAdmin(admin.ModelAdmin):
|
||||||
"weapons",
|
"weapons",
|
||||||
"equipment",
|
"equipment",
|
||||||
("money_pp", "money_po", "money_pa", "money_pc"),
|
("money_pp", "money_po", "money_pa", "money_pc"),
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("Race", {"fields": ["racial_capability"]}),
|
("Race", {"fields": ["racial_capability"]}),
|
||||||
|
|
|
@ -18,10 +18,12 @@ class AddPathForm(forms.Form):
|
||||||
empty_label="----- Voies liées au personnage -----",
|
empty_label="----- Voies liées au personnage -----",
|
||||||
)
|
)
|
||||||
other_path = forms.ModelChoiceField(
|
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)
|
super().__init__(*args, **kwargs)
|
||||||
paths = {cap.path_id for cap in character.capabilities.all()}
|
paths = {cap.path_id for cap in character.capabilities.all()}
|
||||||
paths = (
|
paths = (
|
||||||
|
@ -30,12 +32,12 @@ class AddPathForm(forms.Form):
|
||||||
.select_related("profile", "race")
|
.select_related("profile", "race")
|
||||||
)
|
)
|
||||||
character_paths = paths.filter(
|
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"].queryset = character_paths
|
||||||
self.fields["character_path"].widget.attrs["class"] = "form-select"
|
self.fields["character_path"].widget.attrs["class"] = "form-select"
|
||||||
self.fields["other_path"].queryset = paths.exclude(
|
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"
|
self.fields["other_path"].widget.attrs["class"] = "form-select"
|
||||||
|
|
||||||
|
@ -43,12 +45,13 @@ class AddPathForm(forms.Form):
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
values = [cleaned_data.get("character_path"), cleaned_data.get("other_path")]
|
values = [cleaned_data.get("character_path"), cleaned_data.get("other_path")]
|
||||||
if len(list(filter(None, values))) != 1:
|
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
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class CharacterCreateForm(forms.ModelForm):
|
class CharacterCreateForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.fields[
|
self.fields[
|
||||||
"racial_capability"
|
"racial_capability"
|
||||||
|
|
|
@ -17,7 +17,8 @@ class Command(BaseCommand):
|
||||||
while len(cards) < expected_capability_count:
|
while len(cards) < expected_capability_count:
|
||||||
self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
|
self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
|
||||||
cards = self.selenium.find_elements(
|
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:
|
for card in cards:
|
||||||
try:
|
try:
|
||||||
|
@ -63,11 +64,11 @@ class Command(BaseCommand):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS(f"Created/updated cap {capability}")
|
self.style.SUCCESS(f"Created/updated cap {capability}"),
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stdout.write(
|
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]:
|
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))
|
paths.append(Path.objects.get(name__iexact=path_name))
|
||||||
except Exception:
|
except Exception:
|
||||||
self.stdout.write(
|
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 []
|
||||||
return paths
|
return paths
|
||||||
|
|
|
@ -22,10 +22,12 @@ class Command(BaseCommand):
|
||||||
def import_row(self, url: str, state_row: WebElement) -> None:
|
def import_row(self, url: str, state_row: WebElement) -> None:
|
||||||
name = state_row.find_element(By.CLASS_NAME, "views-field-name").text.strip()
|
name = state_row.find_element(By.CLASS_NAME, "views-field-name").text.strip()
|
||||||
description = state_row.find_element(
|
description = state_row.find_element(
|
||||||
By.CLASS_NAME, "views-field-description__value"
|
By.CLASS_NAME,
|
||||||
|
"views-field-description__value",
|
||||||
).text.strip()
|
).text.strip()
|
||||||
icon_url = state_row.find_element(
|
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")
|
).get_attribute("src")
|
||||||
state, _ = HarmfulState.objects.update_or_create(
|
state, _ = HarmfulState.objects.update_or_create(
|
||||||
name=name,
|
name=name,
|
||||||
|
|
|
@ -16,7 +16,8 @@ class Command(BaseCommand):
|
||||||
while len(anchors) < expected_path_count:
|
while len(anchors) < expected_path_count:
|
||||||
self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
|
self.selenium.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
|
||||||
anchors = self.selenium.find_elements(
|
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]
|
urls = [anchor.get_attribute("href") for anchor in anchors]
|
||||||
for url in urls:
|
for url in urls:
|
||||||
|
@ -57,7 +58,8 @@ class Command(BaseCommand):
|
||||||
try:
|
try:
|
||||||
category = (
|
category = (
|
||||||
self.selenium.find_element(
|
self.selenium.find_element(
|
||||||
By.CSS_SELECTOR, ".field--name-type .field__item"
|
By.CSS_SELECTOR,
|
||||||
|
".field--name-type .field__item",
|
||||||
)
|
)
|
||||||
.text.lower()
|
.text.lower()
|
||||||
.strip()
|
.strip()
|
||||||
|
@ -65,8 +67,8 @@ class Command(BaseCommand):
|
||||||
except Exception:
|
except Exception:
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.WARNING(
|
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
|
return Path.Category.PROFILE
|
||||||
|
|
||||||
|
@ -79,7 +81,8 @@ class Command(BaseCommand):
|
||||||
def get_profile(self, name: str) -> Profile | None:
|
def get_profile(self, name: str) -> Profile | None:
|
||||||
try:
|
try:
|
||||||
profile_name = self.selenium.find_element(
|
profile_name = self.selenium.find_element(
|
||||||
By.CSS_SELECTOR, ".field--name-type + strong + a"
|
By.CSS_SELECTOR,
|
||||||
|
".field--name-type + strong + a",
|
||||||
).text
|
).text
|
||||||
except Exception:
|
except Exception:
|
||||||
self.stdout.write(self.style.WARNING(f"Couldn't find profile for {name}"))
|
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:
|
def get_notes(self) -> str:
|
||||||
try:
|
try:
|
||||||
return self.selenium.find_element(
|
return self.selenium.find_element(
|
||||||
By.CSS_SELECTOR, ".mt-3 > .field--name-description"
|
By.CSS_SELECTOR,
|
||||||
|
".mt-3 > .field--name-description",
|
||||||
).text.strip()
|
).text.strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -41,19 +41,20 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def get_dice(self, name: str) -> Dice:
|
def get_dice(self, name: str) -> Dice:
|
||||||
dice = self.selenium.find_element(By.CSS_SELECTOR, ".dice + div").text.split(
|
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])
|
number_of_dice, dice_value = int(dice[0]), int(dice[1])
|
||||||
if number_of_dice != 1:
|
if number_of_dice != 1:
|
||||||
self.stdout.write(
|
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)
|
return Dice(dice_value)
|
||||||
|
|
||||||
def get_magical_strength(self) -> Profile.MagicalStrength:
|
def get_magical_strength(self) -> Profile.MagicalStrength:
|
||||||
try:
|
try:
|
||||||
magical_strength = self.selenium.find_element(
|
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
|
).text
|
||||||
magical_strength = Profile.MagicalStrength(magical_strength)
|
magical_strength = Profile.MagicalStrength(magical_strength)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -27,7 +27,8 @@ class Command(BaseCommand):
|
||||||
self.stdout.write(self.style.SUCCESS(f"Created/updated race {race}"))
|
self.stdout.write(self.style.SUCCESS(f"Created/updated race {race}"))
|
||||||
|
|
||||||
racial_cap = self.selenium.find_element(
|
racial_cap = self.selenium.find_element(
|
||||||
By.CSS_SELECTOR, ".field--name-abilities"
|
By.CSS_SELECTOR,
|
||||||
|
".field--name-abilities",
|
||||||
)
|
)
|
||||||
racial_name = (
|
racial_name = (
|
||||||
racial_cap.find_element(By.TAG_NAME, "strong")
|
racial_cap.find_element(By.TAG_NAME, "strong")
|
||||||
|
|
|
@ -32,13 +32,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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=[
|
validators=[
|
||||||
django.core.validators.MinValueValidator(1),
|
django.core.validators.MinValueValidator(1),
|
||||||
django.core.validators.MaxValueValidator(5),
|
django.core.validators.MaxValueValidator(5),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("limited", models.BooleanField(blank=True)),
|
("limited", models.BooleanField(blank=True)),
|
||||||
|
@ -74,13 +76,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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"),
|
(10, "D10"),
|
||||||
(12, "D12"),
|
(12, "D12"),
|
||||||
(20, "D20"),
|
(20, "D20"),
|
||||||
]
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -130,13 +134,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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)),
|
("damage", models.CharField(blank=True, max_length=50)),
|
||||||
|
@ -192,20 +200,23 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
django_extensions.db.fields.ModificationDateTimeField(
|
||||||
auto_now=True, verbose_name="modified"
|
auto_now=True,
|
||||||
|
verbose_name="modified",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("description", models.TextField()),
|
("description", models.TextField()),
|
||||||
(
|
(
|
||||||
"race",
|
"race",
|
||||||
models.ForeignKey(
|
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",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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",
|
model_name="capability",
|
||||||
name="path",
|
name="path",
|
||||||
field=models.ForeignKey(
|
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(
|
migrations.AddConstraint(
|
||||||
|
@ -369,7 +383,9 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddConstraint(
|
migrations.AddConstraint(
|
||||||
model_name="capability",
|
model_name="capability",
|
||||||
constraint=models.UniqueConstraint(
|
constraint=models.UniqueConstraint(
|
||||||
models.F("path"), models.F("rank"), name="unique_path_rank"
|
models.F("path"),
|
||||||
|
models.F("rank"),
|
||||||
|
name="unique_path_rank",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,7 +17,9 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddConstraint(
|
migrations.AddConstraint(
|
||||||
model_name="racialcapability",
|
model_name="racialcapability",
|
||||||
constraint=models.UniqueConstraint(
|
constraint=models.UniqueConstraint(
|
||||||
models.F("name"), models.F("race"), name="unique_name_race"
|
models.F("name"),
|
||||||
|
models.F("race"),
|
||||||
|
name="unique_name_race",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -56,7 +56,9 @@ class Migration(migrations.Migration):
|
||||||
model_name="capability",
|
model_name="capability",
|
||||||
name="limited",
|
name="limited",
|
||||||
field=models.BooleanField(
|
field=models.BooleanField(
|
||||||
blank=True, default=False, verbose_name="limitée"
|
blank=True,
|
||||||
|
default=False,
|
||||||
|
verbose_name="limitée",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
@ -108,7 +110,9 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="capabilities",
|
name="capabilities",
|
||||||
field=models.ManyToManyField(
|
field=models.ManyToManyField(
|
||||||
blank=True, to="character.capability", verbose_name="capacités"
|
blank=True,
|
||||||
|
to="character.capability",
|
||||||
|
verbose_name="capacités",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
@ -140,7 +144,7 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="health_remaining",
|
name="health_remaining",
|
||||||
field=models.PositiveSmallIntegerField(
|
field=models.PositiveSmallIntegerField(
|
||||||
verbose_name="points de vie restants"
|
verbose_name="points de vie restants",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
@ -162,14 +166,15 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="luck_points_remaining",
|
name="luck_points_remaining",
|
||||||
field=models.PositiveSmallIntegerField(
|
field=models.PositiveSmallIntegerField(
|
||||||
verbose_name="points de chance restants"
|
verbose_name="points de chance restants",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="mana_consumed",
|
name="mana_consumed",
|
||||||
field=models.PositiveSmallIntegerField(
|
field=models.PositiveSmallIntegerField(
|
||||||
default=0, verbose_name="mana utilisé"
|
default=0,
|
||||||
|
verbose_name="mana utilisé",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
@ -261,7 +266,9 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="weapons",
|
name="weapons",
|
||||||
field=models.ManyToManyField(
|
field=models.ManyToManyField(
|
||||||
blank=True, to="character.weapon", verbose_name="armes"
|
blank=True,
|
||||||
|
to="character.weapon",
|
||||||
|
verbose_name="armes",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
|
|
|
@ -17,7 +17,8 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="mana_remaining",
|
name="mana_remaining",
|
||||||
field=models.PositiveSmallIntegerField(
|
field=models.PositiveSmallIntegerField(
|
||||||
default=0, verbose_name="mana restant"
|
default=0,
|
||||||
|
verbose_name="mana restant",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,8 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="recovery_points_remaining",
|
name="recovery_points_remaining",
|
||||||
field=models.PositiveSmallIntegerField(
|
field=models.PositiveSmallIntegerField(
|
||||||
default=5, verbose_name="points de récupération restants"
|
default=5,
|
||||||
|
verbose_name="points de récupération restants",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -30,13 +30,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
django_extensions.db.fields.ModificationDateTimeField(
|
||||||
auto_now=True, verbose_name="modified"
|
auto_now=True,
|
||||||
|
verbose_name="modified",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
("description", models.TextField()),
|
("description", models.TextField()),
|
||||||
|
|
|
@ -13,7 +13,8 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="states",
|
name="states",
|
||||||
field=models.ManyToManyField(
|
field=models.ManyToManyField(
|
||||||
related_name="characters", to="character.harmfulstate"
|
related_name="characters",
|
||||||
|
to="character.harmfulstate",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,9 @@ class Migration(migrations.Migration):
|
||||||
model_name="character",
|
model_name="character",
|
||||||
name="states",
|
name="states",
|
||||||
field=models.ManyToManyField(
|
field=models.ManyToManyField(
|
||||||
blank=True, related_name="characters", to="character.harmfulstate"
|
blank=True,
|
||||||
|
related_name="characters",
|
||||||
|
to="character.harmfulstate",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,13 +32,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
django_extensions.db.fields.ModificationDateTimeField(
|
||||||
auto_now=True, verbose_name="modified"
|
auto_now=True,
|
||||||
|
verbose_name="modified",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Migration(migrations.Migration):
|
||||||
character.models.character.validate_image,
|
character.models.character.validate_image,
|
||||||
*(),
|
*(),
|
||||||
**{"megabytes_limit": 2},
|
**{"megabytes_limit": 2},
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
verbose_name="image de profil",
|
verbose_name="image de profil",
|
||||||
),
|
),
|
||||||
|
|
|
@ -32,7 +32,9 @@ class Path(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||||
CREATURE = "creature", "Créature"
|
CREATURE = "creature", "Créature"
|
||||||
|
|
||||||
category = models.CharField(
|
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")
|
notes = models.TextField(blank=True, verbose_name="notes")
|
||||||
|
|
||||||
|
@ -93,13 +95,20 @@ class Capability(DocumentedModel, TimeStampedModel, models.Model):
|
||||||
related_name="capabilities",
|
related_name="capabilities",
|
||||||
)
|
)
|
||||||
rank = models.PositiveSmallIntegerField(
|
rank = models.PositiveSmallIntegerField(
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(5)], verbose_name="rang"
|
validators=[MinValueValidator(1), MaxValueValidator(5)],
|
||||||
|
verbose_name="rang",
|
||||||
)
|
)
|
||||||
limited = models.BooleanField(
|
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(
|
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")
|
description = models.TextField(verbose_name="description")
|
||||||
|
|
||||||
|
@ -134,7 +143,9 @@ class RacialCapabilityManager(models.Manager):
|
||||||
class RacialCapability(DocumentedModel, TimeStampedModel, models.Model):
|
class RacialCapability(DocumentedModel, TimeStampedModel, models.Model):
|
||||||
name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom")
|
name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom")
|
||||||
race = models.ForeignKey(
|
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")
|
description = models.TextField(verbose_name="description")
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,12 @@ from character.models.equipment import Weapon
|
||||||
from common.models import DocumentedModel, UniquelyNamedModel
|
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):
|
class MagicalStrength(models.TextChoices):
|
||||||
NONE = "NON", "Aucun"
|
NONE = "NON", "Aucun"
|
||||||
INTELLIGENCE = "INT", "Intelligence"
|
INTELLIGENCE = "INT", "Intelligence"
|
||||||
|
@ -36,10 +41,13 @@ class Profile(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Mode
|
||||||
verbose_name="force magique",
|
verbose_name="force magique",
|
||||||
)
|
)
|
||||||
life_dice = models.PositiveSmallIntegerField(
|
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(
|
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")
|
notes = models.TextField(blank=True, verbose_name="notes")
|
||||||
|
|
||||||
|
@ -48,13 +56,23 @@ class Profile(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Mode
|
||||||
verbose_name_plural = "Profils"
|
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):
|
class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta):
|
||||||
verbose_name = "Race"
|
verbose_name = "Race"
|
||||||
verbose_name_plural = "Races"
|
verbose_name_plural = "Races"
|
||||||
|
|
||||||
|
|
||||||
class HarmfulState(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
|
class HarmfulState( # noqa: DJ008
|
||||||
|
DocumentedModel,
|
||||||
|
UniquelyNamedModel,
|
||||||
|
TimeStampedModel,
|
||||||
|
models.Model,
|
||||||
|
):
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
icon_url = models.URLField()
|
icon_url = models.URLField()
|
||||||
|
|
||||||
|
@ -88,7 +106,7 @@ class CharacterQuerySet(models.QuerySet):
|
||||||
from party.models import Party
|
from party.models import Party
|
||||||
|
|
||||||
return self.filter(
|
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):
|
def mastered_by(self, user):
|
||||||
|
@ -113,7 +131,7 @@ class CharacterQuerySet(models.QuerySet):
|
||||||
return self.filter(
|
return self.filter(
|
||||||
Q(player=user)
|
Q(player=user)
|
||||||
| Q(parties__in=Party.objects.related_to(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()
|
).distinct()
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,7 +202,10 @@ class Character(models.Model):
|
||||||
level = models.PositiveSmallIntegerField(verbose_name="niveau", default=1)
|
level = models.PositiveSmallIntegerField(verbose_name="niveau", default=1)
|
||||||
|
|
||||||
gender = models.CharField(
|
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")
|
age = models.PositiveSmallIntegerField(verbose_name="âge")
|
||||||
height = models.PositiveSmallIntegerField(verbose_name="taille")
|
height = models.PositiveSmallIntegerField(verbose_name="taille")
|
||||||
|
@ -193,17 +214,17 @@ class Character(models.Model):
|
||||||
value_strength = models.PositiveSmallIntegerField(verbose_name="valeur force")
|
value_strength = models.PositiveSmallIntegerField(verbose_name="valeur force")
|
||||||
value_dexterity = models.PositiveSmallIntegerField(verbose_name="valeur dextérité")
|
value_dexterity = models.PositiveSmallIntegerField(verbose_name="valeur dextérité")
|
||||||
value_constitution = models.PositiveSmallIntegerField(
|
value_constitution = models.PositiveSmallIntegerField(
|
||||||
verbose_name="valeur constitution"
|
verbose_name="valeur constitution",
|
||||||
)
|
)
|
||||||
value_intelligence = models.PositiveSmallIntegerField(
|
value_intelligence = models.PositiveSmallIntegerField(
|
||||||
verbose_name="valeur intelligence"
|
verbose_name="valeur intelligence",
|
||||||
)
|
)
|
||||||
value_wisdom = models.PositiveSmallIntegerField(verbose_name="valeur sagesse")
|
value_wisdom = models.PositiveSmallIntegerField(verbose_name="valeur sagesse")
|
||||||
value_charisma = models.PositiveSmallIntegerField(verbose_name="valeur charisme")
|
value_charisma = models.PositiveSmallIntegerField(verbose_name="valeur charisme")
|
||||||
|
|
||||||
health_max = models.PositiveSmallIntegerField(verbose_name="points de vie max")
|
health_max = models.PositiveSmallIntegerField(verbose_name="points de vie max")
|
||||||
health_remaining = models.PositiveSmallIntegerField(
|
health_remaining = models.PositiveSmallIntegerField(
|
||||||
verbose_name="points de vie restants"
|
verbose_name="points de vie restants",
|
||||||
)
|
)
|
||||||
|
|
||||||
racial_capability = models.ForeignKey(
|
racial_capability = models.ForeignKey(
|
||||||
|
@ -214,7 +235,9 @@ class Character(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
weapons = models.ManyToManyField(
|
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)
|
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)
|
defense_misc = models.SmallIntegerField(verbose_name="divers défense", default=0)
|
||||||
|
|
||||||
initiative_misc = models.SmallIntegerField(
|
initiative_misc = models.SmallIntegerField(
|
||||||
verbose_name="divers initiative", default=0
|
verbose_name="divers initiative",
|
||||||
|
default=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
capabilities = models.ManyToManyField(
|
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")
|
equipment = models.TextField(blank=True, verbose_name="équipement")
|
||||||
luck_points_remaining = models.PositiveSmallIntegerField(
|
luck_points_remaining = models.PositiveSmallIntegerField(
|
||||||
verbose_name="points de chance restants"
|
verbose_name="points de chance restants",
|
||||||
)
|
)
|
||||||
|
|
||||||
mana_remaining = models.PositiveSmallIntegerField(
|
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")
|
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")
|
money_pc = models.PositiveSmallIntegerField(default=0, verbose_name="PC")
|
||||||
|
|
||||||
recovery_points_remaining = models.PositiveSmallIntegerField(
|
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)
|
notes = models.TextField(blank=True, verbose_name="notes", default=DEFAULT_NOTES)
|
||||||
|
@ -267,8 +295,10 @@ class Character(models.Model):
|
||||||
verbose_name_plural = "Personnages"
|
verbose_name_plural = "Personnages"
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
Lower("name"), "player", name="unique_character_player"
|
Lower("name"),
|
||||||
)
|
"player",
|
||||||
|
name="unique_character_player",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -329,7 +359,8 @@ class Character(models.Model):
|
||||||
Profile.MagicalStrength.NONE: 0,
|
Profile.MagicalStrength.NONE: 0,
|
||||||
}
|
}
|
||||||
return modifier_map.get(
|
return modifier_map.get(
|
||||||
Profile.MagicalStrength(self.profile.magical_strength), 0
|
Profile.MagicalStrength(self.profile.magical_strength),
|
||||||
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -403,8 +434,9 @@ class Character(models.Model):
|
||||||
for capability in path.capabilities.all():
|
for capability in path.capabilities.all():
|
||||||
capabilities_by_path[capability.path].append(
|
capabilities_by_path[capability.path].append(
|
||||||
CharacterCapability(
|
CharacterCapability(
|
||||||
capability, known=capability in character_capabilities
|
capability,
|
||||||
)
|
known=capability in character_capabilities,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return dict(
|
return dict(
|
||||||
|
@ -414,7 +446,7 @@ class Character(models.Model):
|
||||||
for path, capabilities in capabilities_by_path.items()
|
for path, capabilities in capabilities_by_path.items()
|
||||||
),
|
),
|
||||||
key=lambda x: x[0].name,
|
key=lambda x: x[0].name,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_formatted_notes(self) -> str:
|
def get_formatted_notes(self) -> str:
|
||||||
|
@ -427,7 +459,7 @@ class Character(models.Model):
|
||||||
|
|
||||||
def get_missing_states(self) -> Iterable[HarmfulState]:
|
def get_missing_states(self) -> Iterable[HarmfulState]:
|
||||||
return HarmfulState.objects.exclude(
|
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):
|
def managed_by(self, user):
|
||||||
|
|
|
@ -4,7 +4,12 @@ from django_extensions.db.models import TimeStampedModel
|
||||||
from common.models import DocumentedModel, UniquelyNamedModel
|
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):
|
class Category(models.TextChoices):
|
||||||
MELEE = "MEL", "corps à corps"
|
MELEE = "MEL", "corps à corps"
|
||||||
RANGE = "RAN", "à distance"
|
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")
|
damage = models.CharField(max_length=50, blank=True, verbose_name="dégâts")
|
||||||
special = models.TextField(blank=True, verbose_name="spécial")
|
special = models.TextField(blank=True, verbose_name="spécial")
|
||||||
category = models.CharField(
|
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):
|
class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta):
|
||||||
|
|
|
@ -44,7 +44,10 @@ def test_can_access_character_in_party(client):
|
||||||
notes = "Some notes"
|
notes = "Some notes"
|
||||||
gm_notes = "Some GM notes"
|
gm_notes = "Some GM notes"
|
||||||
friend_character = baker.make(
|
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 = baker.make(Party)
|
||||||
party.characters.add(character)
|
party.characters.add(character)
|
||||||
|
|
|
@ -68,7 +68,10 @@ def test_attack_range(level, dexterity):
|
||||||
@given(armor=integers(), shield=integers(), dexterity=ability_values(), misc=integers())
|
@given(armor=integers(), shield=integers(), dexterity=ability_values(), misc=integers())
|
||||||
def test_defense(armor, shield, dexterity, misc):
|
def test_defense(armor, shield, dexterity, misc):
|
||||||
char = Character(
|
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
|
assert char.defense == 10 + armor + shield + modifier_test(dexterity) + misc
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,8 @@ def test_list_characters(selenium: WebDriver, live_server: LiveServer):
|
||||||
names = {
|
names = {
|
||||||
name.text
|
name.text
|
||||||
for name in selenium.find_elements(
|
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}
|
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
|
assert Character.objects.count() == 2
|
||||||
selenium.find_element(
|
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()
|
).click()
|
||||||
selenium.find_element(By.CSS_SELECTOR, "[type=submit]").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()
|
@pytest.mark.django_db()
|
||||||
def test_reset_stats_view(
|
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"
|
username, password = "user", "some_password"
|
||||||
player = User.objects.create_user(username, password=password)
|
player = User.objects.create_user(username, password=password)
|
||||||
|
@ -186,7 +190,10 @@ def create_hurt_character(player, profile):
|
||||||
|
|
||||||
|
|
||||||
def login(
|
def login(
|
||||||
selenium: WebDriver, live_server: LiveServer, username: str, password: str
|
selenium: WebDriver,
|
||||||
|
live_server: LiveServer,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
selenium.get(live_server.url)
|
selenium.get(live_server.url)
|
||||||
selenium.find_element(By.ID, "login").click()
|
selenium.find_element(By.ID, "login").click()
|
||||||
|
|
|
@ -10,7 +10,9 @@ urlpatterns = [
|
||||||
path("<int:pk>/change/", views.character_change, name="change"),
|
path("<int:pk>/change/", views.character_change, name="change"),
|
||||||
path("<int:pk>/delete/", views.character_delete, name="delete"),
|
path("<int:pk>/delete/", views.character_delete, name="delete"),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/health_change/", views.character_health_change, name="health_change"
|
"<int:pk>/health_change/",
|
||||||
|
views.character_health_change,
|
||||||
|
name="health_change",
|
||||||
),
|
),
|
||||||
path("<int:pk>/mana_change/", views.character_mana_change, name="mana_change"),
|
path("<int:pk>/mana_change/", views.character_mana_change, name="mana_change"),
|
||||||
path(
|
path(
|
||||||
|
@ -87,7 +89,9 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
path("<int:pk>/add_path/", views.add_path, name="add_path"),
|
path("<int:pk>/add_path/", views.add_path, name="add_path"),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/remove_state/<int:state_pk>/", views.remove_state, name="remove_state"
|
"<int:pk>/remove_state/<int:state_pk>/",
|
||||||
|
views.remove_state,
|
||||||
|
name="remove_state",
|
||||||
),
|
),
|
||||||
path("<int:pk>/add_state/<int:state_pk>/", views.add_state, name="add_state"),
|
path("<int:pk>/add_state/<int:state_pk>/", views.add_state, name="add_state"),
|
||||||
path("<int:pk>/reset_stats/", views.reset_stats, name="reset_stats"),
|
path("<int:pk>/reset_stats/", views.reset_stats, name="reset_stats"),
|
||||||
|
|
|
@ -14,7 +14,8 @@ from party.models import Party
|
||||||
def characters_list(request):
|
def characters_list(request):
|
||||||
context = {
|
context = {
|
||||||
"characters": Character.objects.owned_by(request.user).select_related(
|
"characters": Character.objects.owned_by(request.user).select_related(
|
||||||
"race", "profile"
|
"race",
|
||||||
|
"profile",
|
||||||
),
|
),
|
||||||
"all_states": HarmfulState.objects.all(),
|
"all_states": HarmfulState.objects.all(),
|
||||||
}
|
}
|
||||||
|
@ -99,7 +100,7 @@ def add_path(request, pk: int):
|
||||||
context = {"character": character}
|
context = {"character": character}
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
path: Path = form.cleaned_data.get("character_path") or form.cleaned_data.get(
|
path: Path = form.cleaned_data.get("character_path") or form.cleaned_data.get(
|
||||||
"other_path"
|
"other_path",
|
||||||
)
|
)
|
||||||
cap = path.get_next_capability(character)
|
cap = path.get_next_capability(character)
|
||||||
character.capabilities.add(cap)
|
character.capabilities.add(cap)
|
||||||
|
@ -117,7 +118,8 @@ def add_path(request, pk: int):
|
||||||
def character_health_change(request, pk: int):
|
def character_health_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
character = get_object_or_404(
|
||||||
Character.objects.managed_by(request.user).only(
|
Character.objects.managed_by(request.user).only(
|
||||||
"health_max", "health_remaining"
|
"health_max",
|
||||||
|
"health_remaining",
|
||||||
),
|
),
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
|
@ -150,7 +152,9 @@ def character_recovery_points_change(request, pk: int):
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
value = get_updated_value(
|
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.recovery_points_remaining = value
|
||||||
character.save(update_fields=["recovery_points_remaining"])
|
character.save(update_fields=["recovery_points_remaining"])
|
||||||
|
@ -160,7 +164,8 @@ def character_recovery_points_change(request, pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def character_defense_misc_change(request, pk: int):
|
def character_defense_misc_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
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"))
|
value = get_updated_value(request, character.defense_misc, float("inf"))
|
||||||
character.defense_misc = value
|
character.defense_misc = value
|
||||||
|
@ -172,7 +177,8 @@ def character_defense_misc_change(request, pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def character_shield_change(request, pk: int):
|
def character_shield_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
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"))
|
value = get_updated_value(request, character.shield, float("inf"))
|
||||||
character.shield = value
|
character.shield = value
|
||||||
|
@ -184,7 +190,8 @@ def character_shield_change(request, pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def character_armor_change(request, pk: int):
|
def character_armor_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
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"))
|
value = get_updated_value(request, character.armor, float("inf"))
|
||||||
character.armor = value
|
character.armor = value
|
||||||
|
@ -196,7 +203,8 @@ def character_armor_change(request, pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def character_initiative_misc_change(request, pk: int):
|
def character_initiative_misc_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
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"))
|
value = get_updated_value(request, character.initiative_misc, float("inf"))
|
||||||
character.initiative_misc = value
|
character.initiative_misc = value
|
||||||
|
@ -209,12 +217,15 @@ def character_initiative_misc_change(request, pk: int):
|
||||||
def character_luck_points_change(request, pk: int):
|
def character_luck_points_change(request, pk: int):
|
||||||
character = get_object_or_404(
|
character = get_object_or_404(
|
||||||
Character.objects.managed_by(request.user).only(
|
Character.objects.managed_by(request.user).only(
|
||||||
"luck_points_remaining", "value_charisma"
|
"luck_points_remaining",
|
||||||
|
"value_charisma",
|
||||||
),
|
),
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
value = get_updated_value(
|
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.luck_points_remaining = value
|
||||||
character.save(update_fields=["luck_points_remaining"])
|
character.save(update_fields=["luck_points_remaining"])
|
||||||
|
@ -222,7 +233,9 @@ def character_luck_points_change(request, pk: int):
|
||||||
|
|
||||||
|
|
||||||
def get_updated_value(
|
def get_updated_value(
|
||||||
request, remaining_value: int | float, max_value: int | float
|
request,
|
||||||
|
remaining_value: int | float,
|
||||||
|
max_value: int | float,
|
||||||
) -> int:
|
) -> int:
|
||||||
form_value = request.GET.get("value")
|
form_value = request.GET.get("value")
|
||||||
if form_value == "ko":
|
if form_value == "ko":
|
||||||
|
@ -241,7 +254,10 @@ def get_updated_value(
|
||||||
def character_get_defense(request, pk: int):
|
def character_get_defense(request, pk: int):
|
||||||
character = get_object_or_404(
|
character = get_object_or_404(
|
||||||
Character.objects.managed_by(request.user).only(
|
Character.objects.managed_by(request.user).only(
|
||||||
"defense_misc", "armor", "shield", "value_dexterity"
|
"defense_misc",
|
||||||
|
"armor",
|
||||||
|
"shield",
|
||||||
|
"value_dexterity",
|
||||||
),
|
),
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
|
@ -252,13 +268,16 @@ def character_get_defense(request, pk: int):
|
||||||
def character_get_health_bar(request, pk: int):
|
def character_get_health_bar(request, pk: int):
|
||||||
character = get_object_or_404(
|
character = get_object_or_404(
|
||||||
Character.objects.managed_by(request.user).only(
|
Character.objects.managed_by(request.user).only(
|
||||||
"health_max", "health_remaining"
|
"health_max",
|
||||||
|
"health_remaining",
|
||||||
),
|
),
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
context = {"character": character}
|
context = {"character": character}
|
||||||
return render(
|
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}
|
context = {"character": character}
|
||||||
return render(
|
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):
|
def character_get_initiative(request, pk: int):
|
||||||
character = get_object_or_404(
|
character = get_object_or_404(
|
||||||
Character.objects.managed_by(request.user).only(
|
Character.objects.managed_by(request.user).only(
|
||||||
"initiative_misc", "value_dexterity"
|
"initiative_misc",
|
||||||
|
"value_dexterity",
|
||||||
),
|
),
|
||||||
pk=pk,
|
pk=pk,
|
||||||
)
|
)
|
||||||
|
@ -299,7 +321,8 @@ def character_gm_notes_change(request, pk: int):
|
||||||
def character_equipment_change(request, pk: int):
|
def character_equipment_change(request, pk: int):
|
||||||
field = "equipment"
|
field = "equipment"
|
||||||
character = get_object_or_404(
|
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}
|
context = {"character": character}
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -331,7 +354,8 @@ def character_damage_reduction_change(request, pk: int):
|
||||||
|
|
||||||
def update_text_field(request, pk, field):
|
def update_text_field(request, pk, field):
|
||||||
character = get_object_or_404(
|
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}
|
context = {"character": character}
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -343,14 +367,17 @@ def update_text_field(request, pk, field):
|
||||||
setattr(character, field, request.POST.get(field))
|
setattr(character, field, request.POST.get(field))
|
||||||
character.save(update_fields=[field])
|
character.save(update_fields=[field])
|
||||||
return render(
|
return render(
|
||||||
request, f"character/snippets/character_details/{field}_display.html", context
|
request,
|
||||||
|
f"character/snippets/character_details/{field}_display.html",
|
||||||
|
context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_next_in_path(request, character_pk: int, path_pk: int):
|
def add_next_in_path(request, character_pk: int, path_pk: int):
|
||||||
character = get_object_or_404(
|
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)
|
path = get_object_or_404(Path, pk=path_pk)
|
||||||
capability = path.get_next_capability(character)
|
capability = path.get_next_capability(character)
|
||||||
|
@ -369,10 +396,11 @@ def add_next_in_path(request, character_pk: int, path_pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def remove_last_in_path(request, character_pk: int, path_pk: int):
|
def remove_last_in_path(request, character_pk: int, path_pk: int):
|
||||||
character = get_object_or_404(
|
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(
|
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)
|
cap = Capability.objects.get(path_id=path_pk, rank=last_rank)
|
||||||
character.capabilities.remove(cap)
|
character.capabilities.remove(cap)
|
||||||
|
@ -390,13 +418,16 @@ def remove_last_in_path(request, character_pk: int, path_pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def remove_state(request, pk: int, state_pk: int):
|
def remove_state(request, pk: int, state_pk: int):
|
||||||
character: Character = get_object_or_404(
|
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)
|
state = get_object_or_404(HarmfulState, pk=state_pk)
|
||||||
character.states.remove(state)
|
character.states.remove(state)
|
||||||
context = {"character": character, "all_states": HarmfulState.objects.all()}
|
context = {"character": character, "all_states": HarmfulState.objects.all()}
|
||||||
response = render(
|
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")
|
return trigger_client_event(response, "refresh_tooltips", after="swap")
|
||||||
|
|
||||||
|
@ -404,13 +435,16 @@ def remove_state(request, pk: int, state_pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def add_state(request, pk: int, state_pk: int):
|
def add_state(request, pk: int, state_pk: int):
|
||||||
character: Character = get_object_or_404(
|
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)
|
state = get_object_or_404(HarmfulState, pk=state_pk)
|
||||||
character.states.add(state)
|
character.states.add(state)
|
||||||
context = {"character": character, "all_states": HarmfulState.objects.all()}
|
context = {"character": character, "all_states": HarmfulState.objects.all()}
|
||||||
response = render(
|
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")
|
return trigger_client_event(response, "refresh_tooltips", after="swap")
|
||||||
|
|
||||||
|
@ -418,7 +452,8 @@ def add_state(request, pk: int, state_pk: int):
|
||||||
@login_required
|
@login_required
|
||||||
def reset_stats(request, pk: int):
|
def reset_stats(request, pk: int):
|
||||||
character: Character = get_object_or_404(
|
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}
|
context = {"character": character}
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
|
@ -128,7 +128,7 @@ DATABASES = {"default": env.db()}
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
SOLO_CACHE = "default"
|
SOLO_CACHE = "default"
|
||||||
|
@ -244,7 +244,7 @@ APP = {
|
||||||
"date": "latest-date",
|
"date": "latest-date",
|
||||||
"commit": "latest-commit",
|
"commit": "latest-commit",
|
||||||
"describe": "latest-describe",
|
"describe": "latest-describe",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
with Path("/app/git/build-date").open() as f:
|
with Path("/app/git/build-date").open() as f:
|
||||||
|
|
|
@ -28,7 +28,9 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"last_login",
|
"last_login",
|
||||||
models.DateTimeField(
|
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",
|
"username",
|
||||||
models.CharField(
|
models.CharField(
|
||||||
error_messages={
|
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.",
|
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||||
max_length=150,
|
max_length=150,
|
||||||
unique=True,
|
unique=True,
|
||||||
validators=[
|
validators=[
|
||||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
django.contrib.auth.validators.UnicodeUsernameValidator(),
|
||||||
],
|
],
|
||||||
verbose_name="username",
|
verbose_name="username",
|
||||||
),
|
),
|
||||||
|
@ -57,19 +59,25 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"first_name",
|
"first_name",
|
||||||
models.CharField(
|
models.CharField(
|
||||||
blank=True, max_length=150, verbose_name="first name"
|
blank=True,
|
||||||
|
max_length=150,
|
||||||
|
verbose_name="first name",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"last_name",
|
"last_name",
|
||||||
models.CharField(
|
models.CharField(
|
||||||
blank=True, max_length=150, verbose_name="last name"
|
blank=True,
|
||||||
|
max_length=150,
|
||||||
|
verbose_name="last name",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"email",
|
"email",
|
||||||
models.EmailField(
|
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",
|
"date_joined",
|
||||||
models.DateTimeField(
|
models.DateTimeField(
|
||||||
default=django.utils.timezone.now, verbose_name="date joined"
|
default=django.utils.timezone.now,
|
||||||
|
verbose_name="date joined",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
@ -13,7 +13,11 @@ class UniquelyNamedModelManager(models.Manager):
|
||||||
|
|
||||||
class UniquelyNamedModel(models.Model):
|
class UniquelyNamedModel(models.Model):
|
||||||
name = models.CharField(
|
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()
|
objects = UniquelyNamedModelManager()
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,12 @@ def main():
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
raise ImportError(
|
msg = (
|
||||||
"Couldn't import Django. Are you sure it's installed and "
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
"available on your PYTHONPATH environment variable? Did you "
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
"forget to activate a virtual environment?"
|
"forget to activate a virtual environment?"
|
||||||
) from exc
|
)
|
||||||
|
raise ImportError(msg) from exc
|
||||||
execute_from_command_line(sys.argv)
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from party.models import BattleEffect, Party
|
||||||
|
|
||||||
|
|
||||||
class PartyForm(forms.ModelForm):
|
class PartyForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self.original_instance = kwargs.get("instance")
|
self.original_instance = kwargs.get("instance")
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
qs = Character.objects.all()
|
qs = Character.objects.all()
|
||||||
|
@ -16,9 +16,10 @@ class PartyForm(forms.ModelForm):
|
||||||
Q(private=False)
|
Q(private=False)
|
||||||
| Q(
|
| Q(
|
||||||
pk__in=self.original_instance.invited_characters.all().values_list(
|
pk__in=self.original_instance.invited_characters.all().values_list(
|
||||||
"pk", flat=True
|
"pk",
|
||||||
)
|
flat=True,
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.fields["invited_characters"].queryset = qs
|
self.fields["invited_characters"].queryset = qs
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
django_extensions.db.fields.ModificationDateTimeField(
|
||||||
auto_now=True, verbose_name="modified"
|
auto_now=True,
|
||||||
|
verbose_name="modified",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
@ -28,13 +28,15 @@ class Migration(migrations.Migration):
|
||||||
(
|
(
|
||||||
"created",
|
"created",
|
||||||
django_extensions.db.fields.CreationDateTimeField(
|
django_extensions.db.fields.CreationDateTimeField(
|
||||||
auto_now_add=True, verbose_name="created"
|
auto_now_add=True,
|
||||||
|
verbose_name="created",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"modified",
|
"modified",
|
||||||
django_extensions.db.fields.ModificationDateTimeField(
|
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")),
|
("name", models.CharField(max_length=100, verbose_name="nom")),
|
||||||
|
|
|
@ -17,14 +17,14 @@ class PartyQuerySet(models.QuerySet):
|
||||||
def played_or_mastered_by(self, user):
|
def played_or_mastered_by(self, user):
|
||||||
return self.filter(
|
return self.filter(
|
||||||
Q(game_master=user)
|
Q(game_master=user)
|
||||||
| Q(characters__in=Character.objects.filter(player=user))
|
| Q(characters__in=Character.objects.filter(player=user)),
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
def related_to(self, user):
|
def related_to(self, user):
|
||||||
return self.filter(
|
return self.filter(
|
||||||
Q(game_master=user)
|
Q(game_master=user)
|
||||||
| Q(characters__in=Character.objects.filter(player=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()
|
).distinct()
|
||||||
|
|
||||||
def invited_to(self, user):
|
def invited_to(self, user):
|
||||||
|
@ -35,7 +35,7 @@ class PartyManager(UniquelyNamedModelManager):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
|
class Party(UniquelyNamedModel, TimeStampedModel, models.Model): # noqa: DJ008
|
||||||
game_master = models.ForeignKey(
|
game_master = models.ForeignKey(
|
||||||
"common.User",
|
"common.User",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
@ -96,7 +96,10 @@ class BattleEffectManager(models.Manager):
|
||||||
class BattleEffect(TimeStampedModel, models.Model):
|
class BattleEffect(TimeStampedModel, models.Model):
|
||||||
name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom")
|
name = models.CharField(max_length=100, blank=False, null=False, verbose_name="nom")
|
||||||
target = models.CharField(
|
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")
|
description = models.TextField(blank=True, null=False, verbose_name="description")
|
||||||
remaining_rounds = models.SmallIntegerField(
|
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:
|
if self.remaining_rounds >= max_display_percent or self.remaining_rounds < 0:
|
||||||
return 100
|
return 100
|
||||||
return self.remaining_rounds / max_display_percent * 100
|
return self.remaining_rounds / max_display_percent * 100
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
|
@ -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.get(live_server.url + reverse("party:list"))
|
||||||
selenium.find_element(
|
selenium.find_element(
|
||||||
By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .edit"
|
By.CSS_SELECTOR,
|
||||||
|
f".party[data-id='{party.pk}'] .edit",
|
||||||
).click()
|
).click()
|
||||||
invited = Select(selenium.find_element(By.ID, "id_invited_characters"))
|
invited = Select(selenium.find_element(By.ID, "id_invited_characters"))
|
||||||
invited.select_by_index(0)
|
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()
|
@pytest.mark.django_db()
|
||||||
def test_gm_observe_invited_character_in_group(
|
def test_gm_observe_invited_character_in_group(
|
||||||
selenium: WebDriver, live_server: LiveServer
|
selenium: WebDriver,
|
||||||
|
live_server: LiveServer,
|
||||||
):
|
):
|
||||||
username, password = "gm", "password"
|
username, password = "gm", "password"
|
||||||
gm = User.objects.create_user(username, password=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.get(live_server.url + reverse("party:list"))
|
||||||
selenium.find_element(
|
selenium.find_element(
|
||||||
By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .access"
|
By.CSS_SELECTOR,
|
||||||
|
f".party[data-id='{party.pk}'] .access",
|
||||||
).click()
|
).click()
|
||||||
selenium.find_element(
|
selenium.find_element(
|
||||||
By.CSS_SELECTOR, f".character[data-id='{character.pk}'] .observe"
|
By.CSS_SELECTOR,
|
||||||
|
f".character[data-id='{character.pk}'] .observe",
|
||||||
).click()
|
).click()
|
||||||
title = selenium.find_element(By.TAG_NAME, "h1").text.strip()
|
title = selenium.find_element(By.TAG_NAME, "h1").text.strip()
|
||||||
assert title == character.name
|
assert title == character.name
|
||||||
|
@ -65,7 +69,8 @@ def test_gm_observe_invited_character_in_group(
|
||||||
|
|
||||||
@pytest.mark.django_db()
|
@pytest.mark.django_db()
|
||||||
def test_gm_observe_invited_character_in_two_groups(
|
def test_gm_observe_invited_character_in_two_groups(
|
||||||
selenium: WebDriver, live_server: LiveServer
|
selenium: WebDriver,
|
||||||
|
live_server: LiveServer,
|
||||||
):
|
):
|
||||||
username, password = "gm", "password"
|
username, password = "gm", "password"
|
||||||
gm = User.objects.create_user(username, password=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.get(live_server.url + reverse("party:list"))
|
||||||
selenium.find_element(
|
selenium.find_element(
|
||||||
By.CSS_SELECTOR, f".party[data-id='{party.pk}'] .access"
|
By.CSS_SELECTOR,
|
||||||
|
f".party[data-id='{party.pk}'] .access",
|
||||||
).click()
|
).click()
|
||||||
selenium.find_element(
|
selenium.find_element(
|
||||||
By.CSS_SELECTOR, f".character[data-id='{character.pk}'] .observe"
|
By.CSS_SELECTOR,
|
||||||
|
f".character[data-id='{character.pk}'] .observe",
|
||||||
).click()
|
).click()
|
||||||
title = selenium.find_element(By.TAG_NAME, "h1").text.strip()
|
title = selenium.find_element(By.TAG_NAME, "h1").text.strip()
|
||||||
assert title == character.name
|
assert title == character.name
|
||||||
|
@ -91,7 +98,9 @@ def test_gm_observe_invited_character_in_two_groups(
|
||||||
|
|
||||||
@pytest.mark.django_db()
|
@pytest.mark.django_db()
|
||||||
def test_reset_stats_view(
|
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"
|
user, password = "gm", "password"
|
||||||
gm = User.objects.create_user(user, password=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,
|
party=party,
|
||||||
)
|
)
|
||||||
active_nearly_terminated = baker.make(
|
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
|
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
|
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
|
not_party = baker.make(BattleEffect, _quantity=4, remaining_rounds=55) # noqa: F841
|
||||||
beacon = active_nearly_terminated[0]
|
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()
|
@pytest.mark.django_db()
|
||||||
def test_gm_can_delete_any_existing_effect(
|
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."""
|
"""The GM of a group can delete any existing effect, running or terminated."""
|
||||||
user, password = "gm", "password"
|
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)
|
go_to_party(selenium, live_server, party, user, password)
|
||||||
selenium.find_element(
|
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()
|
).click()
|
||||||
|
|
||||||
assert BattleEffect.objects.count() == 1
|
assert BattleEffect.objects.count() == 1
|
||||||
|
@ -267,7 +287,8 @@ def test_gm_can_delete_any_existing_effect(
|
||||||
|
|
||||||
@pytest.mark.django_db()
|
@pytest.mark.django_db()
|
||||||
def test_player_cant_change_existing_running_effect(
|
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."""
|
"""Members of the group can only view existing running effects, no update."""
|
||||||
user, password = "player", "password"
|
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)
|
go_to_party(selenium, live_server, party, user, password)
|
||||||
effect = effects[0]
|
effect = effects[0]
|
||||||
effect_element = selenium.find_element(
|
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.name in effect_element.text
|
||||||
assert effect.target 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(
|
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:
|
) -> None:
|
||||||
selenium.find_element(By.ID, "add-effect").click()
|
selenium.find_element(By.ID, "add-effect").click()
|
||||||
selenium.find_element(By.ID, "id_name").send_keys(name)
|
selenium.find_element(By.ID, "id_name").send_keys(name)
|
||||||
|
@ -308,7 +334,10 @@ def fill_effect(
|
||||||
|
|
||||||
|
|
||||||
def assert_effect_is_created(
|
def assert_effect_is_created(
|
||||||
name: str, description: str, target: str, remaining_rounds: str
|
name: str,
|
||||||
|
description: str,
|
||||||
|
target: str,
|
||||||
|
remaining_rounds: str,
|
||||||
) -> BattleEffect:
|
) -> BattleEffect:
|
||||||
assert BattleEffect.objects.count() == 1
|
assert BattleEffect.objects.count() == 1
|
||||||
effect = BattleEffect.objects.first()
|
effect = BattleEffect.objects.first()
|
||||||
|
@ -320,7 +349,11 @@ def assert_effect_is_created(
|
||||||
|
|
||||||
|
|
||||||
def go_to_party(
|
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:
|
) -> None:
|
||||||
login(selenium, live_server, user, password)
|
login(selenium, live_server, user, password)
|
||||||
url = reverse("party:details", kwargs={"pk": party.pk})
|
url = reverse("party:details", kwargs={"pk": party.pk})
|
||||||
|
|
|
@ -17,10 +17,14 @@ urlpatterns = [
|
||||||
name="delete_effect",
|
name="delete_effect",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/increase_rounds/", views.party_increase_rounds, name="increase_rounds"
|
"<int:pk>/increase_rounds/",
|
||||||
|
views.party_increase_rounds,
|
||||||
|
name="increase_rounds",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/decrease_rounds/", views.party_decrease_rounds, name="decrease_rounds"
|
"<int:pk>/decrease_rounds/",
|
||||||
|
views.party_decrease_rounds,
|
||||||
|
name="decrease_rounds",
|
||||||
),
|
),
|
||||||
path("<int:pk>/leave/<int:character_pk>/", views.party_leave, name="leave"),
|
path("<int:pk>/leave/<int:character_pk>/", views.party_leave, name="leave"),
|
||||||
path("<int:pk>/join/<int:character_pk>/", views.party_join, name="join"),
|
path("<int:pk>/join/<int:character_pk>/", views.party_join, name="join"),
|
||||||
|
|
|
@ -139,7 +139,8 @@ def party_change(request, pk):
|
||||||
def party_leave(request, pk, character_pk):
|
def party_leave(request, pk, character_pk):
|
||||||
party = get_object_or_404(Party.objects.played_by(request.user).distinct(), pk=pk)
|
party = get_object_or_404(Party.objects.played_by(request.user).distinct(), pk=pk)
|
||||||
character = get_object_or_404(
|
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}
|
context = {"party": party, "character": character}
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -154,7 +155,8 @@ def party_leave(request, pk, character_pk):
|
||||||
def party_join(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)
|
party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk)
|
||||||
character = get_object_or_404(
|
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.characters.add(character)
|
||||||
party.invited_characters.remove(character)
|
party.invited_characters.remove(character)
|
||||||
|
@ -167,7 +169,8 @@ def party_join(request, pk, character_pk):
|
||||||
def party_refuse(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)
|
party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk)
|
||||||
character = get_object_or_404(
|
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)
|
party.invited_characters.remove(character)
|
||||||
messages.success(request, f"{character} a refusé l'invitation au groupe {party}.")
|
messages.success(request, f"{character} a refusé l'invitation au groupe {party}.")
|
||||||
|
|
4
tasks.py
4
tasks.py
|
@ -84,7 +84,9 @@ def download_db(ctx: Context):
|
||||||
)
|
)
|
||||||
ctx.run("rm -rf src/media/", pty=True, echo=True)
|
ctx.run("rm -rf src/media/", pty=True, echo=True)
|
||||||
ctx.run(
|
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):
|
with ctx.cd(SRC_DIR):
|
||||||
ctx.run("./manage.py changepassword gaugendre", pty=True, echo=True)
|
ctx.run("./manage.py changepassword gaugendre", pty=True, echo=True)
|
||||||
|
|
Loading…
Reference in a new issue