From 4664ae0d9d1ad6f699fcbcbbec735d96820deca4 Mon Sep 17 00:00:00 2001
From: Gabriel Augendre
Date: Wed, 2 Nov 2022 21:41:06 +0100
Subject: [PATCH] Add states
---
src/character/admin.py | 8 +++
.../commands/import_harmful_states.py | 39 ++++++++++++++
src/character/migrations/0028_harmfulstate.py | 51 +++++++++++++++++++
.../migrations/0029_character_states.py | 20 ++++++++
.../migrations/0030_alter_character_states.py | 20 ++++++++
src/character/migrations/max_migration.txt | 2 +-
src/character/models/__init__.py | 3 +-
src/character/models/character.py | 11 ++++
src/character/templates/character/states.html | 15 ++++++
src/character/templates/character/view.html | 1 +
src/character/urls.py | 3 ++
src/character/views.py | 14 ++++-
src/common/templates/common/base.html | 11 +++-
tasks.py | 1 +
14 files changed, 194 insertions(+), 5 deletions(-)
create mode 100644 src/character/management/commands/import_harmful_states.py
create mode 100644 src/character/migrations/0028_harmfulstate.py
create mode 100644 src/character/migrations/0029_character_states.py
create mode 100644 src/character/migrations/0030_alter_character_states.py
create mode 100644 src/character/templates/character/states.html
diff --git a/src/character/admin.py b/src/character/admin.py
index 617f319..2fdd4cc 100644
--- a/src/character/admin.py
+++ b/src/character/admin.py
@@ -138,6 +138,7 @@ class CharacterAdmin(admin.ModelAdmin):
"attack_melee",
"attack_range",
"attack_magic",
+ "states",
]
},
),
@@ -182,6 +183,7 @@ class CharacterAdmin(admin.ModelAdmin):
filter_horizontal = [
"capabilities",
"weapons",
+ "states",
]
form = CharacterAdminForm
@@ -196,3 +198,9 @@ class CharacterAdmin(admin.ModelAdmin):
class WeaponAdmin(admin.ModelAdmin):
list_display = ["name", "damage"]
search_fields = ["name", "special", "damage"]
+
+
+@admin.register(models.HarmfulState)
+class HarmfulStateAdmin(admin.ModelAdmin):
+ list_display = ["name", "description"]
+ search_fields = ["name"]
diff --git a/src/character/management/commands/import_harmful_states.py b/src/character/management/commands/import_harmful_states.py
new file mode 100644
index 0000000..bdc093d
--- /dev/null
+++ b/src/character/management/commands/import_harmful_states.py
@@ -0,0 +1,39 @@
+from django.core.management import BaseCommand
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.remote.webelement import WebElement
+
+from character.models.character import HarmfulState
+
+
+class Command(BaseCommand):
+ def handle(self, *args, **options) -> None:
+ url = "https://www.co-drs.org/fr/jeu/etats-prejudiciables"
+ self.setup_selenium()
+ self.selenium.get(url)
+ states = self.selenium.find_elements(By.CSS_SELECTOR, "tbody tr")
+ for state in states:
+ try:
+ self.import_race(url, state)
+ except Exception as e:
+ print(f"{type(e)}: {e}")
+ self.stdout.write(f"Finished processing {len(states)} states.")
+
+ def import_race(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"
+ ).text.strip()
+ icon_url = state_row.find_element(
+ By.CSS_SELECTOR, ".views-field-field-svg-icon img"
+ ).get_attribute("src")
+ state, _ = HarmfulState.objects.update_or_create(
+ name=name,
+ defaults={"description": description, "url": url, "icon_url": icon_url},
+ )
+ self.stdout.write(self.style.SUCCESS(f"Created/updated state {state}"))
+
+ def setup_selenium(self) -> None:
+ options = webdriver.FirefoxOptions()
+ options.add_argument("-headless")
+ self.selenium = webdriver.Firefox(options=options)
diff --git a/src/character/migrations/0028_harmfulstate.py b/src/character/migrations/0028_harmfulstate.py
new file mode 100644
index 0000000..4f1ca5d
--- /dev/null
+++ b/src/character/migrations/0028_harmfulstate.py
@@ -0,0 +1,51 @@
+# Generated by Django 4.1.2 on 2022-11-02 20:00
+
+import django_extensions.db.fields
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("character", "0027_character_initiative_misc"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="HarmfulState",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "name",
+ models.CharField(max_length=100, unique=True, verbose_name="nom"),
+ ),
+ ("url", models.URLField(blank=True, verbose_name="url")),
+ (
+ "created",
+ django_extensions.db.fields.CreationDateTimeField(
+ auto_now_add=True, verbose_name="created"
+ ),
+ ),
+ (
+ "modified",
+ django_extensions.db.fields.ModificationDateTimeField(
+ auto_now=True, verbose_name="modified"
+ ),
+ ),
+ ("description", models.TextField()),
+ ("icon_url", models.URLField()),
+ ],
+ options={
+ "verbose_name": "État préjudiciable",
+ "verbose_name_plural": "États préjudiciables",
+ },
+ ),
+ ]
diff --git a/src/character/migrations/0029_character_states.py b/src/character/migrations/0029_character_states.py
new file mode 100644
index 0000000..59dcf62
--- /dev/null
+++ b/src/character/migrations/0029_character_states.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.2 on 2022-11-02 20:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("character", "0028_harmfulstate"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="character",
+ name="states",
+ field=models.ManyToManyField(
+ 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
new file mode 100644
index 0000000..24bc81d
--- /dev/null
+++ b/src/character/migrations/0030_alter_character_states.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.1.2 on 2022-11-02 20:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("character", "0029_character_states"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="character",
+ name="states",
+ field=models.ManyToManyField(
+ blank=True, related_name="characters", to="character.harmfulstate"
+ ),
+ ),
+ ]
diff --git a/src/character/migrations/max_migration.txt b/src/character/migrations/max_migration.txt
index 3642965..8f8b1da 100644
--- a/src/character/migrations/max_migration.txt
+++ b/src/character/migrations/max_migration.txt
@@ -1 +1 @@
-0027_character_initiative_misc
+0030_alter_character_states
diff --git a/src/character/models/__init__.py b/src/character/models/__init__.py
index 2dde838..c945787 100644
--- a/src/character/models/__init__.py
+++ b/src/character/models/__init__.py
@@ -1,5 +1,5 @@
from .capabilities import Capability, Path, RacialCapability
-from .character import Character, Profile, Race
+from .character import Character, HarmfulState, Profile, Race
from .equipment import Weapon
__all__ = [
@@ -7,6 +7,7 @@ __all__ = [
"Path",
"RacialCapability",
"Character",
+ "HarmfulState",
"Profile",
"Race",
"Weapon",
diff --git a/src/character/models/character.py b/src/character/models/character.py
index ab68831..1dd20c4 100644
--- a/src/character/models/character.py
+++ b/src/character/models/character.py
@@ -49,6 +49,15 @@ class Race(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
verbose_name_plural = "Races"
+class HarmfulState(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
+ description = models.TextField()
+ icon_url = models.URLField()
+
+ class Meta:
+ verbose_name = "État préjudiciable"
+ verbose_name_plural = "États préjudiciables"
+
+
def modifier(value: int) -> int:
if not value:
return 0
@@ -177,6 +186,8 @@ class Character(models.Model):
notes = models.TextField(blank=True, verbose_name="notes", default=DEFAULT_NOTES)
damage_reduction = models.TextField(blank=True, verbose_name="réduction de dégâts")
+ states = models.ManyToManyField(HarmfulState, blank=True, related_name="characters")
+
objects = CharacterManager()
class Meta:
diff --git a/src/character/templates/character/states.html b/src/character/templates/character/states.html
new file mode 100644
index 0000000..0fdcf58
--- /dev/null
+++ b/src/character/templates/character/states.html
@@ -0,0 +1,15 @@
+
+ États :
+ {% for state in character.states.all %}
+
+ {% empty %}
+ Aucun
+ {% endfor %}
+
diff --git a/src/character/templates/character/view.html b/src/character/templates/character/view.html
index e63d3f3..246c7ee 100644
--- a/src/character/templates/character/view.html
+++ b/src/character/templates/character/view.html
@@ -16,6 +16,7 @@
{{ character.race.name }} {{ character.profile.name }} niv. {{ character.level }}
{{ character.get_gender_display }}, {{ character.age }} ans, {{ character.height_m }}m, {{ character.weight }}kg (IMC: {{ character.imc|floatformat }})
+ {% include "character/states.html" %}
diff --git a/src/character/urls.py b/src/character/urls.py
index 3731ced..a333358 100644
--- a/src/character/urls.py
+++ b/src/character/urls.py
@@ -69,4 +69,7 @@ urlpatterns = [
name="remove_last_in_path",
),
path("/add_path/", views.add_path, name="add_path"),
+ path(
+ "/remove_state//", views.remove_state, name="remove_state"
+ ),
]
diff --git a/src/character/views.py b/src/character/views.py
index cec4101..0d5ba34 100644
--- a/src/character/views.py
+++ b/src/character/views.py
@@ -4,7 +4,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django_htmx.http import trigger_client_event
from character.forms import AddPathForm, EquipmentForm
-from character.models import Capability, Character, Path
+from character.models import Capability, Character, HarmfulState, Path
from character.templatetags.character_extras import modifier
@@ -265,3 +265,15 @@ def remove_last_in_path(request, character_pk: int, path_pk: int):
"add_path_form": AddPathForm(character),
}
return render(request, "character/paths_and_capabilities.html", context)
+
+
+@login_required
+def remove_state(request, pk: int, state_pk: int):
+ character: Character = get_object_or_404(
+ Character.objects.filter(player=request.user), pk=pk
+ )
+ state = get_object_or_404(HarmfulState, pk=state_pk)
+ character.states.remove(state)
+ context = {"character": character}
+ response = render(request, "character/states.html", context)
+ return trigger_client_event(response, "refresh_tooltips", {})
diff --git a/src/common/templates/common/base.html b/src/common/templates/common/base.html
index 506118e..2b5e04b 100644
--- a/src/common/templates/common/base.html
+++ b/src/common/templates/common/base.html
@@ -42,8 +42,15 @@
{% endif %}