Add game end functionality with view and URL routing, update models and templates for game state management
This commit is contained in:
parent
d03d3b48d4
commit
f9ed70d386
9 changed files with 181 additions and 89 deletions
|
@ -117,6 +117,9 @@ h6,
|
|||
i.i {
|
||||
margin-right: .5em;
|
||||
}
|
||||
i.hl {
|
||||
color: var(--pico-primary);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 10:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("game", "0018_musicgameanswer"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="musikgame",
|
||||
options={"ordering": ["-over", "-date"]},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="musikgame",
|
||||
name="over",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
16
game/migrations/0020_alter_musikgame_options.py
Normal file
16
game/migrations/0020_alter_musikgame_options.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Generated by Django 5.2.3 on 2025-06-15 10:59
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("game", "0019_alter_musikgame_options_musikgame_over"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="musikgame",
|
||||
options={"ordering": ["over", "-date"]},
|
||||
),
|
||||
]
|
|
@ -74,10 +74,14 @@ class MusikGame(models.Model):
|
|||
players = models.ManyToManyField(User, verbose_name="Joueurs")
|
||||
playlist = models.CharField(blank=True, verbose_name="Playlist YouTube")
|
||||
playlist_loading = models.BooleanField(default=False)
|
||||
over = models.BooleanField(default=False)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("game_detail", kwargs={"pk": self.pk})
|
||||
|
||||
class Meta:
|
||||
ordering = ["over", "-date"]
|
||||
|
||||
|
||||
@receiver(post_save, sender=MusikGame)
|
||||
def generateYoutubePlaylist(sender, instance, created, **kwargs):
|
||||
|
|
|
@ -10,52 +10,7 @@
|
|||
{{ group.name }}
|
||||
</h1>
|
||||
{% include "game/include/group_buttons.html" %}
|
||||
{% if group.musikgame_set.exists %}
|
||||
<h2>
|
||||
<i class="ri-play-circle-fill"></i> Parties
|
||||
</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<thead>
|
||||
{% if group.owner == user %}<th></th>{% endif %}
|
||||
<th>Date</th>
|
||||
<th>
|
||||
<i class="ri-youtube-fill"></i> Playlists
|
||||
</th>
|
||||
<th>Joueurs</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for game in group.musikgame_set.all %}
|
||||
<tr>
|
||||
{% if group.owner == user %}
|
||||
<td>
|
||||
<input type="checkbox" name="game" value="{{ game.pk }}">
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
<a href="{% url "game_detail" pk=game.pk %}">{{ game.date }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if game.playlist %}
|
||||
<a href="{% yt_playlist game %}"
|
||||
{% if game.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ game.players.all|join:", " }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if group.owner == user %}
|
||||
<button type="submit"
|
||||
class="secondary"
|
||||
formaction="{% url "group_remove_game" pk=group.pk %}">
|
||||
<i class="ri-delete-bin-fill"></i> Supprimer les parties sélectionnées
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% include "game/include/group_games.html" %}
|
||||
{% include "game/include/group_members.html" %}
|
||||
<h2>
|
||||
<i class="ri-music-2-fill"></i> Mes musiques <span class="music-count">{{ musics.count }}</span>
|
||||
|
|
57
game/templates/game/include/group_games.html
Normal file
57
game/templates/game/include/group_games.html
Normal file
|
@ -0,0 +1,57 @@
|
|||
{% load form youtube %}
|
||||
{% if group.musikgame_set.exists %}
|
||||
<h2>
|
||||
<i class="ri-play-circle-fill"></i> Parties
|
||||
</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<thead>
|
||||
{% if group.owner == user %}<th></th>{% endif %}
|
||||
<th>
|
||||
<i class="ri-play-circle-fill"></i>
|
||||
</th>
|
||||
<th>Date</th>
|
||||
<th>
|
||||
<i class="ri-youtube-fill"></i> Playlists
|
||||
</th>
|
||||
<th>Joueurs</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for game in group.musikgame_set.all %}
|
||||
<tr>
|
||||
{% if group.owner == user %}
|
||||
<td>
|
||||
<input type="checkbox" name="game" value="{{ game.pk }}">
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if game.over %}
|
||||
<i class="ri-stop-circle-fill"></i>
|
||||
{% else %}
|
||||
<i class="ri-play-circle-fill hl"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url "game_detail" pk=game.pk %}">{{ game.date }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if game.playlist %}
|
||||
<a href="{% yt_playlist game %}"
|
||||
{% if game.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ game.players.all|join:", " }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if group.owner == user %}
|
||||
<button type="submit"
|
||||
class="secondary"
|
||||
formaction="{% url "group_remove_game" pk=group.pk %}">
|
||||
<i class="ri-delete-bin-fill"></i> Supprimer les parties sélectionnées
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
|
@ -2,18 +2,33 @@
|
|||
{% load youtube %}
|
||||
{% block content %}
|
||||
<h1>
|
||||
<i class="ri-play-circle-fill"></i> {{ musikgame.date }}
|
||||
{% if musikgame.over %}
|
||||
<i class="ri-stop-circle-fill"></i>
|
||||
{% else %}
|
||||
<i class="ri-play-circle-fill hl"></i>
|
||||
{% endif %}
|
||||
{{ musikgame.date }}
|
||||
</h1>
|
||||
{% if musikgame.playlist or musikgame.playlist_loading %}
|
||||
<p>
|
||||
<a target="_blank"
|
||||
href="{% yt_playlist musikgame %}"
|
||||
role="button"
|
||||
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||
<a target="_blank"
|
||||
href="{% url "game_answer" musikgame.pk %}"
|
||||
role="button"><i class="ri-play-list-2-fill"></i> Répondre</a>
|
||||
</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset role="group">
|
||||
<a target="_blank"
|
||||
href="{% yt_playlist musikgame %}"
|
||||
role="button"
|
||||
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||
{% if not musikgame.over %}
|
||||
<a href="{% url "game_answer" musikgame.pk %}" role="button"><i class="ri-play-list-2-fill"></i> Répondre</a>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% if is_leader and not musikgame.over %}
|
||||
<fieldset>
|
||||
<button type="submit" formaction="{% url "game_end" musikgame.pk %}">
|
||||
<i class="ri-stop-circle-fill"></i> Finir la partie
|
||||
</button>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
<h2>
|
||||
<i class="ri-group-2-fill"></i> Joueurs
|
||||
|
@ -34,38 +49,40 @@
|
|||
{% endfor %}
|
||||
</ol>
|
||||
</details>
|
||||
<h2>
|
||||
<i class="ri-list-ordered"></i> Résultats
|
||||
</h2>
|
||||
<details>
|
||||
<summary role="button">
|
||||
<i class="ri-list-ordered-2"></i> Résultats
|
||||
</summary>
|
||||
<table class="striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<i class="ri-list-ordered-2"></i>
|
||||
</th>
|
||||
<th>
|
||||
<i class="ri-music-2-fill"></i> Musique
|
||||
</th>
|
||||
<th>
|
||||
<i class="ri-user-line"></i> Joueur
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for music in musikgame.musicgameorder_set.all %}
|
||||
{% if musikgame.over %}
|
||||
<h2>
|
||||
<i class="ri-list-ordered"></i> Résultats
|
||||
</h2>
|
||||
<details>
|
||||
<summary role="button">
|
||||
<i class="ri-list-ordered-2"></i> Résultats
|
||||
</summary>
|
||||
<table class="striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ music.order }}</td>
|
||||
<td>
|
||||
<a href="https://youtu.be/{{ music.music_video.yt_id }}">{{ music.music_video.title }}</a>
|
||||
</td>
|
||||
<td>{{ music.player }}</td>
|
||||
<th>
|
||||
<i class="ri-list-ordered-2"></i>
|
||||
</th>
|
||||
<th>
|
||||
<i class="ri-music-2-fill"></i> Musique
|
||||
</th>
|
||||
<th>
|
||||
<i class="ri-user-line"></i> Joueur
|
||||
</th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for music in musikgame.musicgameorder_set.all %}
|
||||
<tr>
|
||||
<td>{{ music.order }}</td>
|
||||
<td>
|
||||
<a href="https://youtu.be/{{ music.music_video.yt_id }}">{{ music.music_video.title }}</a>
|
||||
</td>
|
||||
<td>{{ music.player }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
|
|
|
@ -68,4 +68,5 @@ urlpatterns = [
|
|||
views.GameAnswerView.as_view(),
|
||||
name="game_answer",
|
||||
),
|
||||
path("group/game/<int:pk>/end/", views.GameEndView.as_view(), name="game_end"),
|
||||
]
|
||||
|
|
|
@ -344,6 +344,12 @@ class GameDetailView(LoginRequiredMixin, DetailView):
|
|||
.distinct()
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
data = super().get_context_data(**kwargs)
|
||||
data["is_leader"] = data["musikgame"].group.is_leader(self.request.user)
|
||||
data["is_owner"] = data["musikgame"].group.is_owner(self.request.user)
|
||||
return data
|
||||
|
||||
|
||||
class YoutubeCallbackView(LoginRequiredMixin, View):
|
||||
def get(self, request):
|
||||
|
@ -428,7 +434,7 @@ class GameAnswerView(LoginRequiredMixin, DetailView):
|
|||
template_name = "game/musikgame_answer.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(players=self.request.user)
|
||||
return super().get_queryset().filter(over=False, players=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(**kwargs) | {
|
||||
|
@ -452,3 +458,15 @@ class GameAnswerView(LoginRequiredMixin, DetailView):
|
|||
defaults={"answer": None},
|
||||
)
|
||||
return redirect("game_answer", pk)
|
||||
|
||||
|
||||
class GameEndView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||
model = models.MusikGame
|
||||
|
||||
def post(self, request, pk):
|
||||
game = self.get_object()
|
||||
if not game.group.is_leader(request.user):
|
||||
raise PermissionDenied()
|
||||
game.over = True
|
||||
game.save()
|
||||
return redirect("game_detail", pk)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue