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:
|
class Meta:
|
||||||
model = models.Group
|
model = models.Group
|
||||||
fields = ["members"]
|
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):
|
class MusicVideo(models.Model):
|
||||||
yt_id = models.CharField(max_length=16)
|
yt_id = models.CharField(max_length=16)
|
||||||
title = models.CharField(blank=True)
|
title = models.CharField(blank=True)
|
||||||
|
date_added = models.DateTimeField(auto_now_add=True)
|
||||||
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
owner = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
blacklisted = models.BooleanField(default=False)
|
blacklisted = models.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ["yt_id", "owner", "group"]
|
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>
|
<p>
|
||||||
<a href="{% url "group_update" pk=group.pk %}"><i class="ri-edit-line"></i> Modifier le groupe</a>
|
<a href="{% url "group_update" pk=group.pk %}"><i class="ri-edit-line"></i> Modifier le groupe</a>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="{% url "start_game" pk=group.pk %}" role="button"><i class="ri-play-fill"></i> Lancer une partie</a>
|
||||||
|
</p>
|
||||||
{% endif %}
|
{% 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 %}
|
{% if group.owner == user %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url "group_edit_members" pk=group.pk %}" role="button"><i class="ri-user-add-fill"></i> Modifier les membres</a>
|
<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>
|
</li>
|
||||||
{% for member in members.all %}<li>{{ member }} ({{ member.count }})</li>{% endfor %}
|
{% for member in members.all %}<li>{{ member }} ({{ member.count }})</li>{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<h2>Mes musiques ({{ musics.count }})</h2>
|
<h2>
|
||||||
|
<i class="ri-music-2-fill"></i> Mes musiques ({{ musics.count }})
|
||||||
|
</h2>
|
||||||
<table class="striped">
|
<table class="striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<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(),
|
views.GroupRemoveMusicView.as_view(),
|
||||||
name="group_remove_music",
|
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.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
@ -88,3 +90,50 @@ class GroupRemoveMusicView(OwnerFilterMixin, SingleObjectMixin, View):
|
||||||
group = music.group
|
group = music.group
|
||||||
music.delete()
|
music.delete()
|
||||||
return redirect(group)
|
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