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;
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):
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:
constraints = [
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):
yt_id = models.CharField(max_length=16)
title = models.CharField(blank=True)

View file

@ -9,7 +9,30 @@
{% endif %}
{{ group.name }}
</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 %}
<h2>
<i class="ri-play-circle-fill"></i> Parties
@ -56,7 +79,68 @@
{% endif %}
</form>
{% 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>
<i class="ri-music-2-fill"></i> Mes musiques <span class="music-count">{{ musics.count }}</span>
</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(),
name="group_remove_member",
),
path(
"group/<int:pk>/set_leader/",
views.GroupSetLead.as_view(),
name="group_set_leader",
),
path(
"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.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError
from django.db.models import Count, Q
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)
.count()
)
data["members"] = data["group"].members.through.objects.annotate(
data["members"] = data["group"].members.annotate(
count=Count(
"user__musicvideo",
"musicvideo",
filter=Q(
user__musicvideo__group=data["group"],
user__musicvideo__blacklisted=False,
musicvideo__group=data["group"], 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
@ -116,13 +112,11 @@ class GroupAddMusicView(MemberFilterMixin, SingleObjectMixin, View):
return redirect(group)
class GroupAddMemberView(MemberFilterMixin, SingleObjectMixin, View):
class GroupAddMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
username = request.POST.get("username")
user = User.objects.get(username=username)
if user == group.owner:
@ -186,16 +180,14 @@ class GroupUnblacklistMusicView(MemberFilterMixin, SingleObjectMixin, View):
return redirect(group)
class GroupRemoveMemberView(MemberFilterMixin, SingleObjectMixin, View):
class GroupRemoveMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
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:
messages.add_message(request, messages.INFO, "Aucun membre supprimé.")
@ -218,25 +210,6 @@ class GroupRemoveMemberView(MemberFilterMixin, SingleObjectMixin, View):
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):
model = models.Group
@ -277,15 +250,15 @@ class GameCreateView(LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["group"] = get_object_or_404(models.Group, pk=self.kwargs["pk"])
if not data["group"].is_leader(self.request.user):
raise PermissionDenied()
data["group"] = get_object_or_404(
models.Group, owner=self.request.user, pk=self.kwargs["pk"]
)
return data
def form_valid(self, form):
group = get_object_or_404(models.Group, pk=self.kwargs["pk"])
if not group.is_leader(self.request.user):
return super().form_invalid(form)
group = get_object_or_404(
models.Group, owner=self.request.user, pk=self.kwargs["pk"]
)
form.instance.group = group
res = super().form_valid(form)
players = []
@ -312,7 +285,7 @@ class GameCreateView(LoginRequiredMixin, CreateView):
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.save()
return res
@ -385,13 +358,11 @@ class YoutubeLoginView(LoginRequiredMixin, View):
return redirect(auth_url)
class GroupClearBlacklistView(MemberFilterMixin, SingleObjectMixin, View):
class GroupClearBlacklistView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
if not group.is_leader(request.user):
raise PermissionDenied()
group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False)
messages.add_message(request, messages.SUCCESS, "La blacklist a été effacée.")
return redirect(group)

View file

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

2
uv.lock generated
View file

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