Add game management features: create MusikGame model, implement game creation and detail views, and update group detail template
This commit is contained in:
parent
19e6eb32c8
commit
f7baa91132
10 changed files with 275 additions and 2 deletions
|
@ -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)
|
||||
)
|
||||
|
|
47
game/migrations/0005_musicvideo_date_added_musikgame.py
Normal file
47
game/migrations/0005_musicvideo_date_added_musikgame.py
Normal 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)),
|
||||
],
|
||||
),
|
||||
]
|
28
game/migrations/0006_musikgame_n_alter_musikgame_players.py
Normal file
28
game/migrations/0006_musikgame_n_alter_musikgame_players.py
Normal 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"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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"),
|
||||
},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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"]
|
||||
|
|
|
@ -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>
|
||||
|
|
22
game/templates/game/musikgame_detail.html
Normal file
22
game/templates/game/musikgame_detail.html
Normal 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 %}
|
8
game/templates/game/musikgame_form.html
Normal file
8
game/templates/game/musikgame_form.html
Normal 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 %}
|
|
@ -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"),
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue