Compare commits

..

No commits in common. "caaf704bd9f7cd376dd9b49c5d8f6a032f16af2c" and "6cd9c0c8414ee1854753304ab6e6b1ef40bea755" have entirely different histories.

11 changed files with 102 additions and 245 deletions

View file

@ -122,11 +122,3 @@ footer {
text-align: center; text-align: center;
font-weight: 350; font-weight: 350;
} }
td.c, th.c {
text-align: center;
input {
margin: 0;
}
}

View file

@ -1,35 +0,0 @@
# Generated by Django 5.2.3 on 2025-06-14 18:50
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("game", "0014_musikgame_playlist_loading"),
]
operations = [
migrations.CreateModel(
name="GroupLeader",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("is_leader", models.BooleanField(default=False)),
(
"member",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="game.group_members",
),
),
],
),
]

View file

@ -1,22 +0,0 @@
# Generated by Django 5.2.3 on 2025-06-14 18:50
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("game", "0015_groupleader"),
]
operations = [
migrations.AlterField(
model_name="groupleader",
name="member",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="lead",
to="game.group_members",
),
),
]

View file

@ -23,33 +23,12 @@ class Group(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse("group_detail", kwargs={"pk": self.pk}) return reverse("group_detail", kwargs={"pk": self.pk})
def is_owner(self, user):
return user == self.owner
def is_leader(self, user):
return (
self.is_owner(user)
or self.members.through.objects.filter(
lead__is_leader=True, user=user
).exists()
)
def is_member(self, user):
return self.is_owner(user) or self.members.filter(user=user).exists()
class Meta: class Meta:
constraints = [ constraints = [
models.UniqueConstraint(Lower("name"), "owner", name="unique_group_name") models.UniqueConstraint(Lower("name"), "owner", name="unique_group_name")
] ]
class GroupLeader(models.Model):
member = models.OneToOneField(
Group.members.through, on_delete=models.CASCADE, related_name="lead"
)
is_leader = models.BooleanField(default=False)
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)

View file

@ -9,7 +9,30 @@
{% endif %} {% endif %}
{{ group.name }} {{ group.name }}
</h1> </h1>
{% include "game/include/group_buttons.html" %} {% if group.owner == user %}
<p>
<form method="post">
{% csrf_token %}
<p>
<a href="{% url "start_game" pk=group.pk %}" role="button"><i class="ri-play-fill"></i> Jouer</a>
</p>
<div role="group">
<a href="{% url "group_update" pk=group.pk %}"
class="secondary"
role="button"><i class="ri-edit-line"></i> Renommer</a>
<button type="submit"
class="secondary"
formaction="{% url "group_clear_blacklist" pk=group.pk %}">
<i class="ri-history-fill"></i> Effacer la blacklist
</button>
<a href="{% url "group_delete" group.pk %}"
role="button"
class="secondary">
<i class="ri-delete-bin-fill"></i> Supprimer</a>
</div>
</form>
</p>
{% endif %}
{% if group.musikgame_set.exists %} {% if group.musikgame_set.exists %}
<h2> <h2>
<i class="ri-play-circle-fill"></i> Parties <i class="ri-play-circle-fill"></i> Parties
@ -56,7 +79,68 @@
{% endif %} {% endif %}
</form> </form>
{% endif %} {% endif %}
{% include "game/include/group_members.html" %} <h2>
<i class="ri-group-2-fill"></i> Membres
</h2>
<form method="post">
{% csrf_token %}
<table>
<thead>
<tr>
{% if group.owner == user %}<th></th>{% endif %}
<th>Membre</th>
<th>
<i class="ri-vip-crown-fill"></i>
</th>
<th>
<i class="ri-mv-line"></i>
</th>
</tr>
</thead>
<tbody>
<tr>
{% if group.owner == user %}<td></td>{% endif %}
<td>{{ group.owner }}</td>
<td>
<i class="ri-vip-crown-fill owner"></i>
</td>
<td>{{ owner_count }}</td>
</tr>
{% for member in members.all %}
<tr>
{% if group.owner == user %}
<td>
<input type="checkbox" name="member" value="{{ member.pk }}">
</td>
{% endif %}
<td>{{ member }}</td>
<td></td>
<td>{{ member.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if group.owner == user %}
<button type="submit"
class="secondary"
formaction="{% url "group_remove_member" pk=group.pk %}">
<i class="ri-delete-bin-fill"></i> Supprimer les membres sélectionnés
</button>
{% endif %}
</form>
{% if group.owner == user %}
<form method="post" action="{% url "group_add_member" pk=group.pk %}">
{% csrf_token %}
<fieldset role="group">
<input type="string"
name="username"
id="username"
placeholder="Membre"
required>
<button type="submit">Ajouter</button>
</fieldset>
</form>
{% endif %}
<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>
</h2> </h2>

View file

@ -1,28 +0,0 @@
{% if is_leader %}
<p>
<form method="post">
{% csrf_token %}
<p>
<a href="{% url "start_game" pk=group.pk %}" role="button"><i class="ri-play-fill"></i> Jouer</a>
</p>
<div role="group">
{% if is_owner %}
<a href="{% url "group_update" pk=group.pk %}"
class="secondary"
role="button"><i class="ri-edit-line"></i> Renommer</a>
{% endif %}
<button type="submit"
class="secondary"
formaction="{% url "group_clear_blacklist" pk=group.pk %}">
<i class="ri-history-fill"></i> Effacer la blacklist
</button>
{% if is_owner %}
<a href="{% url "group_delete" group.pk %}"
role="button"
class="secondary">
<i class="ri-delete-bin-fill"></i> Supprimer</a>
{% endif %}
</div>
</form>
</p>
{% endif %}

View file

@ -1,79 +0,0 @@
<h2>
<i class="ri-group-2-fill"></i> Membres
</h2>
<form method="post">
{% csrf_token %}
<table>
<thead>
<tr>
{% if is_leader %}<th></th>{% endif %}
<th>Membre</th>
<th class="c">
<i class="ri-vip-crown-fill"></i>
</th>
<th>
<i class="ri-mv-line"></i>
</th>
</tr>
</thead>
<tbody>
<tr>
{% if is_leader %}<td></td>{% endif %}
<td>{{ group.owner }}</td>
<td class="c">
<i class="ri-vip-crown-fill owner"></i>
</td>
<td>{{ owner_count }}</td>
</tr>
{% for member in members.all %}
<tr>
{% if is_leader %}
<td>
<input type="checkbox" name="member" value="{{ member.pk }}">
</td>
{% endif %}
<td>{{ member.user }}</td>
<td class="c">
{% if is_owner %}
<input type="checkbox"
name="leader"
value="{{ member.pk }}"
role="switch"
{% if member.lead.is_leader %}checked{% endif %}>
{% endif %}
</td>
<td>{{ member.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_leader %}
<div role="group">
{% if is_owner %}
<button type="submit"
class="secondary"
formaction="{% url "group_set_leader" pk=group.pk %}">
<i class="ri-vip-crown-fill"></i> Mettre à jour meneurs
</button>
{% endif %}
<button type="submit"
class="secondary"
formaction="{% url "group_remove_member" pk=group.pk %}">
<i class="ri-delete-bin-fill"></i> Supprimer
</button>
</div>
{% endif %}
</form>
{% if is_leader %}
<form method="post" action="{% url "group_add_member" pk=group.pk %}">
{% csrf_token %}
<fieldset role="group">
<input type="string"
name="username"
id="username"
placeholder="Membre"
required>
<button type="submit">Ajouter</button>
</fieldset>
</form>
{% endif %}

View file

@ -41,11 +41,6 @@ urlpatterns = [
views.GroupRemoveMemberView.as_view(), views.GroupRemoveMemberView.as_view(),
name="group_remove_member", name="group_remove_member",
), ),
path(
"group/<int:pk>/set_leader/",
views.GroupSetLead.as_view(),
name="group_set_leader",
),
path( path(
"group/<int:pk>/start_game/", views.GameCreateView.as_view(), name="start_game" "group/<int:pk>/start_game/", views.GameCreateView.as_view(), name="start_game"
), ),

View file

@ -6,7 +6,6 @@ from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import Count, Q from django.db.models import Count, Q
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
@ -71,17 +70,14 @@ class GroupDetailView(MemberFilterMixin, GroupMixin, DetailView):
.musicvideo_set.filter(owner=data["group"].owner, blacklisted=False) .musicvideo_set.filter(owner=data["group"].owner, blacklisted=False)
.count() .count()
) )
data["members"] = data["group"].members.through.objects.annotate( data["members"] = data["group"].members.annotate(
count=Count( count=Count(
"user__musicvideo", "musicvideo",
filter=Q( filter=Q(
user__musicvideo__group=data["group"], musicvideo__group=data["group"], musicvideo__blacklisted=False
user__musicvideo__blacklisted=False,
), ),
) )
) )
data["is_leader"] = data["group"].is_leader(self.request.user)
data["is_owner"] = data["group"].is_owner(self.request.user)
return data return data
@ -116,13 +112,11 @@ class GroupAddMusicView(MemberFilterMixin, SingleObjectMixin, View):
return redirect(group) return redirect(group)
class GroupAddMemberView(MemberFilterMixin, SingleObjectMixin, View): class GroupAddMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group model = models.Group
def post(self, request, pk): def post(self, request, pk):
group = self.get_object() group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
username = request.POST.get("username") username = request.POST.get("username")
user = User.objects.get(username=username) user = User.objects.get(username=username)
if user == group.owner: if user == group.owner:
@ -186,16 +180,14 @@ class GroupUnblacklistMusicView(MemberFilterMixin, SingleObjectMixin, View):
return redirect(group) return redirect(group)
class GroupRemoveMemberView(MemberFilterMixin, SingleObjectMixin, View): class GroupRemoveMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group model = models.Group
def post(self, request, pk): def post(self, request, pk):
group = self.get_object() group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
relations = models.Group.members.through.objects.filter( relations = models.Group.members.through.objects.filter(
group=group, pk__in=request.POST.getlist("member") group=group, user__id__in=request.POST.getlist("member")
) )
if relations.count() == 0: if relations.count() == 0:
messages.add_message(request, messages.INFO, "Aucun membre supprimé.") messages.add_message(request, messages.INFO, "Aucun membre supprimé.")
@ -218,25 +210,6 @@ class GroupRemoveMemberView(MemberFilterMixin, SingleObjectMixin, View):
return redirect(group) return redirect(group)
class GroupSetLead(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
members = models.Group.members.through.objects.filter(group=group)
for member in members.filter(pk__in=request.POST.getlist("leader")):
models.GroupLeader.objects.update_or_create(
member=member, defaults={"is_leader": True}
)
for member in members.exclude(pk__in=request.POST.getlist("leader")):
models.GroupLeader.objects.update_or_create(
member=member, defaults={"is_leader": False}
)
return redirect(group)
class GroupRemoveGameView(OwnerFilterMixin, SingleObjectMixin, View): class GroupRemoveGameView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group model = models.Group
@ -277,15 +250,15 @@ class GameCreateView(LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs) data = super().get_context_data(**kwargs)
data["group"] = get_object_or_404(models.Group, pk=self.kwargs["pk"]) data["group"] = get_object_or_404(
if not data["group"].is_leader(self.request.user): models.Group, owner=self.request.user, pk=self.kwargs["pk"]
raise PermissionDenied() )
return data return data
def form_valid(self, form): def form_valid(self, form):
group = get_object_or_404(models.Group, pk=self.kwargs["pk"]) group = get_object_or_404(
if not group.is_leader(self.request.user): models.Group, owner=self.request.user, pk=self.kwargs["pk"]
return super().form_invalid(form) )
form.instance.group = group form.instance.group = group
res = super().form_valid(form) res = super().form_valid(form)
players = [] players = []
@ -312,7 +285,7 @@ class GameCreateView(LoginRequiredMixin, CreateView):
game=form.instance, player=player, music_video=music, order=order game=form.instance, player=player, music_video=music, order=order
) )
if models.YoutubeCredentials.objects.filter(user=self.request.user).exists(): if self.request.user.youtubecredentials:
form.instance.playlist_loading = True form.instance.playlist_loading = True
form.instance.save() form.instance.save()
return res return res
@ -385,13 +358,11 @@ class YoutubeLoginView(LoginRequiredMixin, View):
return redirect(auth_url) return redirect(auth_url)
class GroupClearBlacklistView(MemberFilterMixin, SingleObjectMixin, View): class GroupClearBlacklistView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group model = models.Group
def post(self, request, pk): def post(self, request, pk):
group = self.get_object() group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
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)

View file

@ -1,6 +1,6 @@
[project] [project]
name = "musik" name = "musik"
version = "0.1.2" version = "0.1.1"
description = "Le jeu de Musik." description = "Le jeu de Musik."
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"

2
uv.lock generated
View file

@ -423,7 +423,7 @@ wheels = [
[[package]] [[package]]
name = "musik" name = "musik"
version = "0.1.1" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "celery" }, { name = "celery" },