Add states

This commit is contained in:
Gabriel Augendre 2022-11-02 21:41:06 +01:00
parent 4564e71d7d
commit 4664ae0d9d
14 changed files with 194 additions and 5 deletions

View file

@ -138,6 +138,7 @@ class CharacterAdmin(admin.ModelAdmin):
"attack_melee", "attack_melee",
"attack_range", "attack_range",
"attack_magic", "attack_magic",
"states",
] ]
}, },
), ),
@ -182,6 +183,7 @@ class CharacterAdmin(admin.ModelAdmin):
filter_horizontal = [ filter_horizontal = [
"capabilities", "capabilities",
"weapons", "weapons",
"states",
] ]
form = CharacterAdminForm form = CharacterAdminForm
@ -196,3 +198,9 @@ class CharacterAdmin(admin.ModelAdmin):
class WeaponAdmin(admin.ModelAdmin): class WeaponAdmin(admin.ModelAdmin):
list_display = ["name", "damage"] list_display = ["name", "damage"]
search_fields = ["name", "special", "damage"] search_fields = ["name", "special", "damage"]
@admin.register(models.HarmfulState)
class HarmfulStateAdmin(admin.ModelAdmin):
list_display = ["name", "description"]
search_fields = ["name"]

View file

@ -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)

View file

@ -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",
},
),
]

View file

@ -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"
),
),
]

View file

@ -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"
),
),
]

View file

@ -1 +1 @@
0027_character_initiative_misc 0030_alter_character_states

View file

@ -1,5 +1,5 @@
from .capabilities import Capability, Path, RacialCapability from .capabilities import Capability, Path, RacialCapability
from .character import Character, Profile, Race from .character import Character, HarmfulState, Profile, Race
from .equipment import Weapon from .equipment import Weapon
__all__ = [ __all__ = [
@ -7,6 +7,7 @@ __all__ = [
"Path", "Path",
"RacialCapability", "RacialCapability",
"Character", "Character",
"HarmfulState",
"Profile", "Profile",
"Race", "Race",
"Weapon", "Weapon",

View file

@ -49,6 +49,15 @@ class Race(DocumentedModel, UniquelyNamedModel, TimeStampedModel, models.Model):
verbose_name_plural = "Races" 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: def modifier(value: int) -> int:
if not value: if not value:
return 0 return 0
@ -177,6 +186,8 @@ class Character(models.Model):
notes = models.TextField(blank=True, verbose_name="notes", default=DEFAULT_NOTES) 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") 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() objects = CharacterManager()
class Meta: class Meta:

View file

@ -0,0 +1,15 @@
<p id="states">
États :
{% for state in character.states.all %}
<img src="{{ state.icon_url }}" alt="{{ state.name }}" height="25" width="25"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-title="{{ state.name }} : {{ state.description }}"
hx-get="{% url "character:remove_state" pk=character.pk state_pk=state.pk %}"
hx-target="#states"
hx-swap="outerHTML"
>
{% empty %}
Aucun
{% endfor %}
</p>

View file

@ -16,6 +16,7 @@
{{ character.race.name }} {{ character.profile.name }} niv. {{ character.level }}<br> {{ character.race.name }} {{ character.profile.name }} niv. {{ character.level }}<br>
{{ character.get_gender_display }}, {{ character.age }} ans, {{ character.height_m }}m, {{ character.weight }}kg (IMC: {{ character.imc|floatformat }}) {{ character.get_gender_display }}, {{ character.age }} ans, {{ character.height_m }}m, {{ character.weight }}kg (IMC: {{ character.imc|floatformat }})
</p> </p>
{% include "character/states.html" %}
<div class="row"> <div class="row">
<div class="col-sm-12 col-md-6 col-lg-6 col-xl"> <div class="col-sm-12 col-md-6 col-lg-6 col-xl">
<table id="fight-table" class="table table-hover table-sm"> <table id="fight-table" class="table table-hover table-sm">

View file

@ -69,4 +69,7 @@ urlpatterns = [
name="remove_last_in_path", name="remove_last_in_path",
), ),
path("<int:pk>/add_path/", views.add_path, name="add_path"), path("<int:pk>/add_path/", views.add_path, name="add_path"),
path(
"<int:pk>/remove_state/<int:state_pk>/", views.remove_state, name="remove_state"
),
] ]

View file

@ -4,7 +4,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django_htmx.http import trigger_client_event from django_htmx.http import trigger_client_event
from character.forms import AddPathForm, EquipmentForm 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 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), "add_path_form": AddPathForm(character),
} }
return render(request, "character/paths_and_capabilities.html", context) 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", {})

View file

@ -42,8 +42,15 @@
{% endif %} {% endif %}
<script src="{% static "vendor/bootstrap-5.2.2/bootstrap.bundle.min.js" %}"></script> <script src="{% static "vendor/bootstrap-5.2.2/bootstrap.bundle.min.js" %}"></script>
<script type="application/javascript" defer> <script type="application/javascript" defer>
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); let tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); let tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
addEventListener("refresh_tooltips", function (event) {
tooltipList.forEach(tooltip => tooltip.dispose());
setTimeout(() => {
tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
}, 50);
});
</script> </script>
</body> </body>
</html> </html>

View file

@ -127,6 +127,7 @@ def import_from_co_drs(ctx):
ctx.run("./manage.py import_profiles", pty=True, echo=True) ctx.run("./manage.py import_profiles", pty=True, echo=True)
ctx.run("./manage.py import_paths", pty=True, echo=True) ctx.run("./manage.py import_paths", pty=True, echo=True)
ctx.run("./manage.py import_capabilities", pty=True, echo=True) ctx.run("./manage.py import_capabilities", pty=True, echo=True)
ctx.run("./manage.py import_harmful_states", pty=True, echo=True)
@task(pre=[import_from_co_drs, dump_initial]) @task(pre=[import_from_co_drs, dump_initial])