Add game answer functionality with form and view for user responses

This commit is contained in:
Edgar P. Burkhart 2025-06-15 12:40:55 +02:00
parent 6dbb1a54e0
commit d03d3b48d4
Signed by: edpibu
GPG key ID: 9833D3C5A25BD227
9 changed files with 97 additions and 12 deletions

View file

@ -130,3 +130,7 @@ td.c, th.c {
margin: 0; margin: 0;
} }
} }
table select {
margin-bottom: 0;
}

View file

@ -39,3 +39,22 @@ class MusikGameForm(forms.ModelForm):
kwargs["initial"].setdefault("players", players) kwargs["initial"].setdefault("players", players)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["players"].queryset = players self.fields["players"].queryset = players
class AnswerForm(forms.Form):
def __init__(self, *args, **kwargs):
game = kwargs.pop("game")
user = kwargs.pop("user")
super().__init__(*args, **kwargs)
for music in game.musicgameorder_set.all():
self.fields[f"answer-{music.order}"] = forms.ChoiceField(
choices=[("", "")]
+ list(game.players.all().values_list("id", "username")),
required=False,
label=music.order,
initial=ma.answer.id
if (ma := music.musicgameanswer_set.filter(player=user).first())
and ma.answer
else "",
)

View file

@ -1,4 +1,4 @@
# Generated by Django 5.2.3 on 2025-06-15 09:56 # Generated by Django 5.2.3 on 2025-06-15 10:36
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -24,7 +24,6 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
("order", models.PositiveIntegerField()),
( (
"answer", "answer",
models.ForeignKey( models.ForeignKey(
@ -37,7 +36,8 @@ class Migration(migrations.Migration):
( (
"game", "game",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="game.musikgame" on_delete=django.db.models.deletion.CASCADE,
to="game.musicgameorder",
), ),
), ),
( (
@ -49,10 +49,10 @@ class Migration(migrations.Migration):
), ),
], ],
options={ options={
"ordering": ["order"], "ordering": ["game"],
"constraints": [ "constraints": [
models.UniqueConstraint( models.UniqueConstraint(
fields=("game", "player", "order"), name="unique_answer" fields=("game", "player"), name="unique_answer"
) )
], ],
}, },

View file

@ -114,17 +114,14 @@ class MusicGameOrder(models.Model):
class MusicGameAnswer(models.Model): class MusicGameAnswer(models.Model):
game = models.ForeignKey(MusikGame, on_delete=models.CASCADE) game = models.ForeignKey(MusicGameOrder, on_delete=models.CASCADE)
player = models.ForeignKey(User, on_delete=models.CASCADE) player = models.ForeignKey(User, on_delete=models.CASCADE)
order = models.PositiveIntegerField()
answer = models.ForeignKey( answer = models.ForeignKey(
User, on_delete=models.SET_NULL, null=True, related_name="+" User, on_delete=models.SET_NULL, null=True, related_name="+"
) )
class Meta: class Meta:
constraints = [ constraints = [
models.UniqueConstraint( models.UniqueConstraint(fields=("game", "player"), name="unique_answer"),
fields=("game", "player", "order"), name="unique_answer"
),
] ]
ordering = ["order"] ordering = ["game"]

View file

@ -12,7 +12,7 @@
<i class="ri-vip-crown-fill"></i> <i class="ri-vip-crown-fill"></i>
</th> </th>
<th> <th>
<i class="ri-mv-line"></i> <i class="ri-mv-fill"></i>
</th> </th>
</tr> </tr>
</thead> </thead>

View file

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block content %}
<h2>Mes réponses</h2>
<form method="post">
{% csrf_token %}
<table>
<thead>
<tr>
<th>
<i class="ri-mv-fill" alt="Musique"></i>
</th>
<th>Réponse</th>
</tr>
</thead>
<tbody>
{% for field in form %}
<tr>
<td>{{ field.label }}</td>
<td>{{ field }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Valider mes réponses</button>
</form>
{% endblock content %}

View file

@ -10,6 +10,9 @@
href="{% yt_playlist musikgame %}" href="{% yt_playlist musikgame %}"
role="button" role="button"
{% if musikgame.playlist_loading %}aria-busy="true"{% endif %}><i class="ri-youtube-fill"></i> Playlist</a> {% 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> </p>
{% endif %} {% endif %}
<h2> <h2>

View file

@ -63,4 +63,9 @@ urlpatterns = [
views.GroupClearBlacklistView.as_view(), views.GroupClearBlacklistView.as_view(),
name="group_clear_blacklist", name="group_clear_blacklist",
), ),
path(
"group/game/<int:pk>/answer/",
views.GameAnswerView.as_view(),
name="game_answer",
),
] ]

View file

@ -421,3 +421,34 @@ class GroupClearBlacklistView(MemberFilterMixin, SingleObjectMixin, View):
group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False) group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False)
messages.add_message(request, messages.SUCCESS, "La blacklist a été effacée.") messages.add_message(request, messages.SUCCESS, "La blacklist a été effacée.")
return redirect(group) return redirect(group)
class GameAnswerView(LoginRequiredMixin, DetailView):
model = models.MusikGame
template_name = "game/musikgame_answer.html"
def get_queryset(self):
return super().get_queryset().filter(players=self.request.user)
def get_context_data(self, **kwargs):
return super().get_context_data(**kwargs) | {
"form": forms.AnswerForm(game=self.object, user=self.request.user)
}
def post(self, request, pk):
game = self.get_object()
for music in game.musicgameorder_set.all():
answer = request.POST.get(f"answer-{music.order}")
if answer:
models.MusicGameAnswer.objects.update_or_create(
game=music,
player=request.user,
defaults={"answer": game.players.get(pk=answer)},
)
else:
models.MusicGameAnswer.objects.update_or_create(
game=music,
player=request.user,
defaults={"answer": None},
)
return redirect("game_answer", pk)