mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-11-04 22:03:56 +01:00
Add states
This commit is contained in:
parent
4564e71d7d
commit
4664ae0d9d
14 changed files with 194 additions and 5 deletions
|
@ -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"]
|
||||
|
|
39
src/character/management/commands/import_harmful_states.py
Normal file
39
src/character/management/commands/import_harmful_states.py
Normal 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)
|
51
src/character/migrations/0028_harmfulstate.py
Normal file
51
src/character/migrations/0028_harmfulstate.py
Normal 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",
|
||||
},
|
||||
),
|
||||
]
|
20
src/character/migrations/0029_character_states.py
Normal file
20
src/character/migrations/0029_character_states.py
Normal 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"
|
||||
),
|
||||
),
|
||||
]
|
20
src/character/migrations/0030_alter_character_states.py
Normal file
20
src/character/migrations/0030_alter_character_states.py
Normal 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"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1 +1 @@
|
|||
0027_character_initiative_misc
|
||||
0030_alter_character_states
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
15
src/character/templates/character/states.html
Normal file
15
src/character/templates/character/states.html
Normal 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>
|
|
@ -16,6 +16,7 @@
|
|||
{{ 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 }})
|
||||
</p>
|
||||
{% include "character/states.html" %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-6 col-lg-6 col-xl">
|
||||
<table id="fight-table" class="table table-hover table-sm">
|
||||
|
|
|
@ -69,4 +69,7 @@ urlpatterns = [
|
|||
name="remove_last_in_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"
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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", {})
|
||||
|
|
|
@ -42,8 +42,15 @@
|
|||
{% endif %}
|
||||
<script src="{% static "vendor/bootstrap-5.2.2/bootstrap.bundle.min.js" %}"></script>
|
||||
<script type="application/javascript" defer>
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
let tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
1
tasks.py
1
tasks.py
|
@ -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_paths", 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])
|
||||
|
|
Loading…
Reference in a new issue