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 {
|
i.i {
|
||||||
margin-right: .5em;
|
margin-right: .5em;
|
||||||
}
|
}
|
||||||
|
i.hl {
|
||||||
|
color: var(--pico-primary);
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
text-align: center;
|
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")
|
players = models.ManyToManyField(User, verbose_name="Joueurs")
|
||||||
playlist = models.CharField(blank=True, verbose_name="Playlist YouTube")
|
playlist = models.CharField(blank=True, verbose_name="Playlist YouTube")
|
||||||
playlist_loading = models.BooleanField(default=False)
|
playlist_loading = models.BooleanField(default=False)
|
||||||
|
over = models.BooleanField(default=False)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("game_detail", kwargs={"pk": self.pk})
|
return reverse("game_detail", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["over", "-date"]
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=MusikGame)
|
@receiver(post_save, sender=MusikGame)
|
||||||
def generateYoutubePlaylist(sender, instance, created, **kwargs):
|
def generateYoutubePlaylist(sender, instance, created, **kwargs):
|
||||||
|
|
|
@ -10,52 +10,7 @@
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
</h1>
|
</h1>
|
||||||
{% include "game/include/group_buttons.html" %}
|
{% include "game/include/group_buttons.html" %}
|
||||||
{% if group.musikgame_set.exists %}
|
{% include "game/include/group_games.html" %}
|
||||||
<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_members.html" %}
|
{% include "game/include/group_members.html" %}
|
||||||
<h2>
|
<h2>
|
||||||
<i class="ri-music-2-fill"></i> Mes musiques <span class="music-count">{{ musics.count }}</span>
|
<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 %}
|
{% load youtube %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<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>
|
</h1>
|
||||||
{% if musikgame.playlist or musikgame.playlist_loading %}
|
{% if musikgame.playlist or musikgame.playlist_loading %}
|
||||||
<p>
|
<form method="post">
|
||||||
<a target="_blank"
|
{% csrf_token %}
|
||||||
href="{% yt_playlist musikgame %}"
|
<fieldset role="group">
|
||||||
role="button"
|
<a target="_blank"
|
||||||
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
href="{% yt_playlist musikgame %}"
|
||||||
<a target="_blank"
|
role="button"
|
||||||
href="{% url "game_answer" musikgame.pk %}"
|
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a>
|
||||||
role="button"><i class="ri-play-list-2-fill"></i> Répondre</a>
|
{% if not musikgame.over %}
|
||||||
</p>
|
<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 %}
|
{% endif %}
|
||||||
<h2>
|
<h2>
|
||||||
<i class="ri-group-2-fill"></i> Joueurs
|
<i class="ri-group-2-fill"></i> Joueurs
|
||||||
|
@ -34,38 +49,40 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
</details>
|
</details>
|
||||||
<h2>
|
{% if musikgame.over %}
|
||||||
<i class="ri-list-ordered"></i> Résultats
|
<h2>
|
||||||
</h2>
|
<i class="ri-list-ordered"></i> Résultats
|
||||||
<details>
|
</h2>
|
||||||
<summary role="button">
|
<details>
|
||||||
<i class="ri-list-ordered-2"></i> Résultats
|
<summary role="button">
|
||||||
</summary>
|
<i class="ri-list-ordered-2"></i> Résultats
|
||||||
<table class="striped">
|
</summary>
|
||||||
<thead>
|
<table class="striped">
|
||||||
<tr>
|
<thead>
|
||||||
<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 %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ music.order }}</td>
|
<th>
|
||||||
<td>
|
<i class="ri-list-ordered-2"></i>
|
||||||
<a href="https://youtu.be/{{ music.music_video.yt_id }}">{{ music.music_video.title }}</a>
|
</th>
|
||||||
</td>
|
<th>
|
||||||
<td>{{ music.player }}</td>
|
<i class="ri-music-2-fill"></i> Musique
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<i class="ri-user-line"></i> Joueur
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{% for music in musikgame.musicgameorder_set.all %}
|
||||||
</details>
|
<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 %}
|
{% endblock content %}
|
||||||
|
|
|
@ -68,4 +68,5 @@ urlpatterns = [
|
||||||
views.GameAnswerView.as_view(),
|
views.GameAnswerView.as_view(),
|
||||||
name="game_answer",
|
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()
|
.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):
|
class YoutubeCallbackView(LoginRequiredMixin, View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
@ -428,7 +434,7 @@ class GameAnswerView(LoginRequiredMixin, DetailView):
|
||||||
template_name = "game/musikgame_answer.html"
|
template_name = "game/musikgame_answer.html"
|
||||||
|
|
||||||
def get_queryset(self):
|
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):
|
def get_context_data(self, **kwargs):
|
||||||
return super().get_context_data(**kwargs) | {
|
return super().get_context_data(**kwargs) | {
|
||||||
|
@ -452,3 +458,15 @@ class GameAnswerView(LoginRequiredMixin, DetailView):
|
||||||
defaults={"answer": None},
|
defaults={"answer": None},
|
||||||
)
|
)
|
||||||
return redirect("game_answer", pk)
|
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