Allow GM to increase/decrese battle effects rounds

This commit is contained in:
Gabriel Augendre 2023-01-17 10:55:10 +01:00
parent 27afcc7791
commit ad5e690e21
6 changed files with 146 additions and 20 deletions

View file

@ -1,5 +1,5 @@
from django.db import models
from django.db.models import Q
from django.db.models import F, Q
from django.urls import reverse
from django_extensions.db.models import TimeStampedModel
@ -69,9 +69,28 @@ class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
character.reset_stats()
class BattleEffectQuerySet(models.QuerySet):
def increase_rounds(self):
self.temporary().update(remaining_rounds=F("remaining_rounds") + 1)
def decrease_rounds(self):
self.active().update(remaining_rounds=F("remaining_rounds") - 1)
def active(self):
return self.filter(remaining_rounds__gt=0)
def terminated(self):
return self.filter(remaining_rounds=0)
def permanent(self):
return self.filter(remaining_rounds=-1)
def temporary(self):
return self.exclude(remaining_rounds=-1)
class BattleEffectManager(models.Manager):
def decrease_all_remaining_rounds(self):
pass
pass
class BattleEffect(TimeStampedModel, models.Model):
@ -99,7 +118,7 @@ class BattleEffect(TimeStampedModel, models.Model):
verbose_name="créé par",
)
objects = BattleEffectManager()
objects = BattleEffectManager.from_queryset(BattleEffectQuerySet)()
@property
def remaining_percent(self) -> float:

View file

@ -7,10 +7,12 @@
{% block content %}
<h1>{{ party.name }}</h1>
<p>MJ : {{ party.game_master.get_full_name|default:party.game_master.username }}</p>
<p>
<a href="{% url "party:reset_stats" pk=party.pk %}" id="reset-stats">Réinitialiser
les stats</a>
</p>
{% if party.game_master == request.user %}
<p>
<a href="{% url "party:reset_stats" pk=party.pk %}" id="reset-stats">Réinitialiser
les stats</a>
</p>
{% endif %}
<h2 class="mt-4">Personnages</h2>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-4">
{% for character in party.characters.all %}

View file

@ -1,3 +1,4 @@
{% load character_extras %}
<div id="effects">
<div id="effects-form">
<button
@ -8,6 +9,26 @@
id="add-effect"
class="btn btn-primary"><i class="fa-solid fa-plus"></i> Ajouter un effet
</button>
{% if party.game_master == request.user %}
<div class="btn-group">
<button
hx-get="{% url "party:increase_rounds" pk=party.pk %}"
hx-target="#effects"
hx-swap="outerHTML"
type="button"
id="increase-rounds"
class="btn btn-outline-secondary"> <i class="fa-solid fa-plus"></i> tours
</button>
<button
hx-get="{% url "party:decrease_rounds" pk=party.pk %}"
hx-target="#effects"
hx-swap="outerHTML"
type="button"
id="decrease-rounds"
class="btn btn-outline-secondary"> <i class="fa-solid fa-minus"></i> tours
</button>
</div>
{% endif %}
</div>
<div id="effects-cards" class="row mt-1 row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
{% for effect in party.effects.all %}

View file

@ -1,3 +1,5 @@
import random
import pytest
from django.urls import reverse
from model_bakery import baker
@ -137,13 +139,8 @@ def test_player_can_add_effect_to_group(
)
remaining_rounds = "8"
login(selenium, live_server, user, password)
url = reverse("party:details", kwargs={"pk": party.pk})
selenium.get(live_server.url + url)
go_to_party(selenium, live_server, party, user, password)
fill_effect(selenium, name, description, target, remaining_rounds)
assert_effect_is_created(name, description, target, remaining_rounds)
# Todo: assert effect is displayed
@ -170,13 +167,8 @@ def test_gm_can_add_effect_to_group(
)
remaining_rounds = "-1"
login(selenium, live_server, user, password)
url = reverse("party:details", kwargs={"pk": party.pk})
selenium.get(live_server.url + url)
go_to_party(selenium, live_server, party, user, password)
fill_effect(selenium, name, description, target, remaining_rounds)
assert_effect_is_created(name, description, target, remaining_rounds)
# Todo: assert effect is displayed
@ -186,6 +178,59 @@ def test_gm_can_change_remaining_rounds(
selenium: WebDriver, live_server: LiveServer, initial_data: None
):
"""The GM of a group can increase or decrease the remaining rounds of effects."""
user, password = "gm", "password"
gm = User.objects.create_user(user, password=password)
party = baker.make(Party, game_master=gm)
active_not_nearly_terminated = baker.make( # noqa: F841
BattleEffect,
_quantity=7,
remaining_rounds=lambda: random.randint(2, 12),
party=party,
)
active_nearly_terminated = baker.make( # noqa: F841
BattleEffect, _quantity=3, remaining_rounds=1, party=party
)
terminated = baker.make( # noqa: F841
BattleEffect, _quantity=5, remaining_rounds=0, party=party
)
permanent = baker.make( # noqa: F841
BattleEffect, _quantity=2, remaining_rounds=-1, party=party
)
not_party = baker.make(BattleEffect, _quantity=4, remaining_rounds=55) # noqa: F841
go_to_party(selenium, live_server, party, user, password)
selenium.find_element(By.ID, "increase-rounds").click()
assert BattleEffect.objects.filter(party=party).permanent().count() == 2
assert (
BattleEffect.objects.filter(party=party, remaining_rounds__gt=1).count() == 10
)
assert BattleEffect.objects.filter(party=party, remaining_rounds=1).count() == 5
assert BattleEffect.objects.filter(party=party).terminated().count() == 0
assert (
BattleEffect.objects.exclude(party=party).filter(remaining_rounds=55).count()
== 4
)
selenium.find_element(By.ID, "decrease-rounds").click()
assert BattleEffect.objects.filter(party=party).permanent().count() == 2
assert BattleEffect.objects.filter(party=party, remaining_rounds__gt=1).count() == 7
assert BattleEffect.objects.filter(party=party, remaining_rounds=1).count() == 3
assert BattleEffect.objects.filter(party=party).terminated().count() == 5
assert (
BattleEffect.objects.exclude(party=party).filter(remaining_rounds=55).count()
== 4
)
selenium.find_element(By.ID, "decrease-rounds").click()
assert BattleEffect.objects.filter(party=party).permanent().count() == 2
assert BattleEffect.objects.filter(party=party).active().count() == 7
assert BattleEffect.objects.filter(party=party, remaining_rounds=1).count() == 0
assert BattleEffect.objects.filter(party=party).terminated().count() == 8
assert (
BattleEffect.objects.exclude(party=party).filter(remaining_rounds=55).count()
== 4
)
@pytest.mark.django_db
@ -234,3 +279,9 @@ def assert_effect_is_created(name, description, target, remaining_rounds):
assert effect.target == target
assert effect.description == description
assert str(effect.remaining_rounds) == remaining_rounds
def go_to_party(selenium, live_server, party, user, password):
login(selenium, live_server, user, password)
url = reverse("party:details", kwargs={"pk": party.pk})
selenium.get(live_server.url + url)

View file

@ -11,6 +11,12 @@ urlpatterns = [
path("<int:pk>/delete/", views.party_delete, name="delete"),
path("<int:pk>/reset_stats/", views.party_reset_stats, name="reset_stats"),
path("<int:pk>/add_effect/", views.party_add_effect, name="add_effect"),
path(
"<int:pk>/increase_rounds/", views.party_increase_rounds, name="increase_rounds"
),
path(
"<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>/join/<int:character_pk>/", views.party_join, name="join"),
path("<int:pk>/refuse/<int:character_pk>/", views.party_refuse, name="refuse"),

View file

@ -1,12 +1,14 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_GET, require_http_methods
from character.models import Character, HarmfulState
from party.forms import BattleEffectForm, PartyForm
from party.models import Party
@require_GET
@login_required
def parties_list(request):
context = {
@ -17,6 +19,7 @@ def parties_list(request):
return render(request, "party/parties_list.html", context)
@require_http_methods(["GET", "POST"])
@login_required
def party_create(request):
if request.method == "GET":
@ -33,6 +36,7 @@ def party_create(request):
return render(request, "party/party_form.html", context)
@require_GET
@login_required
def party_details(request, pk):
party = get_object_or_404(Party.objects.related_to(request.user), pk=pk)
@ -43,6 +47,7 @@ def party_details(request, pk):
return render(request, "party/party_details.html", context)
@require_http_methods(["GET", "POST"])
@login_required
def party_delete(request, pk):
party = get_object_or_404(Party.objects.managed_by(request.user), pk=pk)
@ -55,6 +60,7 @@ def party_delete(request, pk):
return render(request, "party/party_delete.html", context)
@require_http_methods(["GET", "POST"])
@login_required
def party_reset_stats(request, pk):
party = get_object_or_404(Party.objects.managed_by(request.user), pk=pk)
@ -68,6 +74,7 @@ def party_reset_stats(request, pk):
return render(request, "party/party_reset_stats.html", context)
@require_http_methods(["GET", "POST"])
@login_required
def party_add_effect(request, pk):
party = get_object_or_404(Party.objects.played_or_mastered_by(request.user), pk=pk)
@ -86,6 +93,23 @@ def party_add_effect(request, pk):
return render(request, "party/snippets/add_effect_form.html", context)
@require_GET
@login_required
def party_increase_rounds(request, pk):
party = get_object_or_404(Party.objects.managed_by(request.user), pk=pk)
party.effects.increase_rounds()
return render(request, "party/snippets/effects.html", {"party": party})
@require_GET
@login_required
def party_decrease_rounds(request, pk):
party = get_object_or_404(Party.objects.managed_by(request.user), pk=pk)
party.effects.decrease_rounds()
return render(request, "party/snippets/effects.html", {"party": party})
@require_http_methods(["GET", "POST"])
@login_required
def party_change(request, pk):
party = get_object_or_404(Party.objects.managed_by(request.user), pk=pk)
@ -102,6 +126,7 @@ def party_change(request, pk):
return render(request, "party/party_form.html", context)
@require_http_methods(["GET", "POST"])
@login_required
def party_leave(request, pk, character_pk):
party = get_object_or_404(Party.objects.played_by(request.user).distinct(), pk=pk)
@ -116,6 +141,7 @@ def party_leave(request, pk, character_pk):
return render(request, "party/party_leave.html", context)
@require_GET
@login_required
def party_join(request, pk, character_pk):
party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk)
@ -128,6 +154,7 @@ def party_join(request, pk, character_pk):
return redirect("party:list")
@require_GET
@login_required
def party_refuse(request, pk, character_pk):
party = get_object_or_404(Party.objects.invited_to(request.user).distinct(), pk=pk)