Add game management features: create MusikGame model, implement game creation and detail views, and update group detail template

This commit is contained in:
Edgar P. Burkhart 2025-06-13 21:06:23 +02:00
parent 19e6eb32c8
commit f7baa91132
Signed by: edpibu
GPG key ID: 9833D3C5A25BD227
10 changed files with 275 additions and 2 deletions

View file

@ -13,3 +13,16 @@ class GroupAddMembersForm(forms.ModelForm):
class Meta:
model = models.Group
fields = ["members"]
class MusikGameForm(forms.ModelForm):
class Meta:
model = models.MusikGame
fields = ["players", "n"]
def __init__(self, *args, **kwargs):
group = models.Group.objects.get(pk=kwargs.pop("group", None))
super().__init__(*args, **kwargs)
self.fields["players"].queryset = (
group.members.all() | models.User.objects.filter(id=group.owner.id)
)

View file

@ -0,0 +1,47 @@
# Generated by Django 5.2.3 on 2025-06-13 17:23
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("game", "0004_musicvideo_title"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name="musicvideo",
name="date_added",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.CreateModel(
name="MusikGame",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateTimeField(auto_now_add=True)),
(
"group",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="game.group"
),
),
("music_videos", models.ManyToManyField(to="game.musicvideo")),
("players", models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -0,0 +1,28 @@
# Generated by Django 5.2.3 on 2025-06-13 18:41
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("game", "0005_musicvideo_date_added_musikgame"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name="musikgame",
name="n",
field=models.PositiveIntegerField(
default=2, verbose_name="Nombre de musiques"
),
),
migrations.AlterField(
model_name="musikgame",
name="players",
field=models.ManyToManyField(
to=settings.AUTH_USER_MODEL, verbose_name="Joueurs"
),
),
]

View file

@ -0,0 +1,61 @@
# Generated by Django 5.2.3 on 2025-06-13 18:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("game", "0006_musikgame_n_alter_musikgame_players"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name="musikgame",
name="music_videos",
),
migrations.CreateModel(
name="MusicGameOrder",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("order", models.PositiveIntegerField()),
(
"game",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="game.musikgame"
),
),
(
"music_video",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="game.musicvideo",
),
),
(
"player",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["order"],
"unique_together": {
("game", "order"),
("game", "player", "music_video"),
},
},
),
]

View file

@ -20,9 +20,31 @@ class Group(models.Model):
class MusicVideo(models.Model):
yt_id = models.CharField(max_length=16)
title = models.CharField(blank=True)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
blacklisted = models.BooleanField(default=False)
class Meta:
unique_together = ["yt_id", "owner", "group"]
class MusikGame(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
n = models.PositiveIntegerField(default=2, verbose_name="Nombre de musiques")
players = models.ManyToManyField(User, verbose_name="Joueurs")
def get_absolute_url(self):
return reverse("game_detail", kwargs={"pk": self.pk})
class MusicGameOrder(models.Model):
game = models.ForeignKey(MusikGame, on_delete=models.CASCADE)
player = models.ForeignKey(User, on_delete=models.CASCADE)
music_video = models.ForeignKey(MusicVideo, on_delete=models.CASCADE)
order = models.PositiveIntegerField()
class Meta:
unique_together = [["game", "player", "music_video"], ["game", "order"]]
ordering = ["order"]

View file

@ -8,8 +8,25 @@
<p>
<a href="{% url "group_update" pk=group.pk %}"><i class="ri-edit-line"></i> Modifier le groupe</a>
</p>
<p>
<a href="{% url "start_game" pk=group.pk %}" role="button"><i class="ri-play-fill"></i> Lancer une partie</a>
</p>
{% endif %}
<h2>Membres</h2>
{% if group.musikgame_set %}
<h2>
<i class="ri-play-circle-fill"></i> Parties
</h2>
<ul>
{% for game in group.musikgame_set.all %}
<li>
<a href="{% url "game_detail" pk=game.pk %}">{{ game.date }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
<h2>
<i class="ri-group-2-fill"></i> Membres
</h2>
{% if group.owner == user %}
<p>
<a href="{% url "group_edit_members" pk=group.pk %}" role="button"><i class="ri-user-add-fill"></i> Modifier les membres</a>
@ -21,7 +38,9 @@
</li>
{% for member in members.all %}<li>{{ member }} ({{ member.count }})</li>{% endfor %}
</ul>
<h2>Mes musiques ({{ musics.count }})</h2>
<h2>
<i class="ri-music-2-fill"></i> Mes musiques ({{ musics.count }})
</h2>
<table class="striped">
<thead>
<tr>

View file

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block content %}
<h1>
<i class="ri-play-circle-fill"></i> {{ musikgame.date }}
</h1>
<h2>
<i class="ri-group-2-fill"></i> Joueurs
</h2>
<ul>
{% for member in musikgame.players.all %}<li>{{ member }}</li>{% endfor %}
</ul>
<h2>
<i class="ri-music-2-fill"></i> Musiques
</h2>
<ol>
{% for music in musikgame.musicgameorder_set.all %}
<li>
<a href="https://youtu.be/{{ music.music_video.yt_id }}">{{ music.music_video.title }}</a>
</li>
{% endfor %}
</ol>
{% endblock content %}

View file

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% load form %}
{% block content %}
<h1>
<i class="ri-group-2-fill"></i> {{ group.name }}
</h1>
{% form form %}
{% endblock content %}

View file

@ -26,4 +26,8 @@ urlpatterns = [
views.GroupRemoveMusicView.as_view(),
name="group_remove_music",
),
path(
"group/<int:pk>/start_game/", views.GameCreateView.as_view(), name="start_game"
),
path("group/game/<int:pk>/", views.GameDetailView.as_view(), name="game_detail"),
]

View file

@ -1,3 +1,5 @@
import random
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Q
from django.http import JsonResponse
@ -88,3 +90,50 @@ class GroupRemoveMusicView(OwnerFilterMixin, SingleObjectMixin, View):
group = music.group
music.delete()
return redirect(group)
class GameCreateView(LoginRequiredMixin, CreateView):
model = models.MusikGame
form_class = forms.MusikGameForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["group"] = self.kwargs["pk"]
return kwargs
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["group"] = models.Group.objects.filter(owner=self.request.user).get(
pk=self.kwargs["pk"]
)
return data
def form_valid(self, form):
group = models.Group.objects.get(pk=self.kwargs["pk"])
form.instance.group = group
res = super().form_valid(form)
players = []
musics = []
for player in form.instance.players.all():
players += 2 * [player]
musics += random.sample(
list(
player.musicvideo_set.filter(group=group, blacklisted=False).all()
),
form.instance.n,
)
pm_list = list(zip(players, musics))
random.shuffle(pm_list)
for (player, music), order in zip(pm_list, range(len(pm_list))):
models.MusicGameOrder.objects.create(
game=form.instance, player=player, music_video=music, order=order
)
return res
class GameDetailView(LoginRequiredMixin, DetailView):
model = models.MusikGame
def get_queryset(self):
return super().get_queryset().filter(group__owner=self.request.user)