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 }}