musik/game/views.py

326 lines
10 KiB
Python
Raw Normal View History

import random
import google_auth_oauthlib
from django.conf import settings
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.db import IntegrityError
from django.db.models import Count, Q
from django.shortcuts import redirect
from django.views import View
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from . import forms, models, tasks, utils
class OwnerFilterMixin(LoginRequiredMixin):
def get_queryset(self):
return super().get_queryset().filter(owner=self.request.user)
class MemberFilterMixin(LoginRequiredMixin):
def get_queryset(self):
return (
super()
.get_queryset()
.filter(Q(members=self.request.user) | Q(owner=self.request.user))
.distinct()
)
class GroupMixin:
model = models.Group
fields = ["name"]
class GroupIntegrityMixin:
def form_valid(self, form):
try:
return super().form_valid(form)
except IntegrityError:
form.add_error("name", "Ce nom de groupe existe déjà.")
return super().form_invalid(form)
class GroupCreateView(LoginRequiredMixin, GroupMixin, GroupIntegrityMixin, CreateView):
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class GroupUpdateView(OwnerFilterMixin, GroupMixin, GroupIntegrityMixin, UpdateView):
pass
class GroupDeleteView(OwnerFilterMixin, GroupMixin, SuccessMessageMixin, DeleteView):
success_url = "/"
success_message = "Le groupe a été supprimé avec succès."
class GroupDetailView(MemberFilterMixin, GroupMixin, DetailView):
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["musics"] = data["group"].musicvideo_set.filter(owner=self.request.user)
data["owner_count"] = (
data["group"]
.musicvideo_set.filter(owner=data["group"].owner, blacklisted=False)
.count()
)
data["members"] = data["group"].members.annotate(
count=Count(
"musicvideo",
filter=Q(group=data["group"], musicvideo__blacklisted=False),
)
)
return data
class GroupAddMusicView(MemberFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
yt_id = request.POST.get("yt_id")
if not yt_id:
messages.add_message(request, messages.ERROR, "Aucun identifiant donné")
return redirect(group)
yt_id = utils.parse_musik(yt_id)
title = utils.get_yt_title(yt_id)
if not title:
messages.add_message(
request, messages.ERROR, f"Vidéo Youtube invalide : {yt_id}"
)
return redirect(group)
try:
group.musicvideo_set.create(yt_id=yt_id, title=title, owner=request.user)
except IntegrityError:
messages.add_message(
request, messages.ERROR, f"Vidéo Youtube déjà ajoutée : {yt_id}"
)
messages.add_message(
request, messages.SUCCESS, f"Vidéo Youtube ajoutée : {yt_id}"
)
return redirect(group)
class GroupAddMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
username = request.POST.get("username")
user = User.objects.get(username=username)
if user == group.owner:
messages.add_message(
request, messages.WARNING, f"{user} est le propriétaire du groupe."
)
return redirect(group)
if user in group.members.all():
messages.add_message(
request, messages.WARNING, f"{user} est déjà membre du groupe."
)
group.members.add(user)
return redirect(group)
class GroupRemoveMusicView(MemberFilterMixin, SingleObjectMixin, View):
model = models.Group
def post(self, request, pk):
group = self.get_object()
musics = group.musicvideo_set.filter(
owner=request.user, pk__in=request.POST.getlist("musics")
)
if musics.count() == 0:
messages.add_message(request, messages.INFO, "Aucune musique supprimée.")
return redirect(group)
if musics.count() != len(request.POST.getlist("musics")):
messages.add_message(
request,
messages.WARNING,
"Certaines musiques n'ont pas pu être supprimées.",
)
musics.delete()
else:
musics.delete()
messages.add_message(
request,
messages.SUCCESS,
"Les musiques sélectionnées ont été supprimées.",
)
return redirect(group)
class GroupRemoveMemberView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def get(self, request, pk, user_pk):
group = self.get_object()
user = User.objects.get(pk=user_pk)
relation = models.Group.members.through.objects.filter(
group=group, user=user
).first()
if not relation:
messages.add_message(
request,
messages.ERROR,
f"L'utilisateur {user} n'est pas membre du groupe.",
)
else:
relation.delete()
return redirect(group)
class GroupRemoveGameView(SingleObjectMixin, SuccessMessageMixin, View):
model = models.MusikGame
success_message = "Le jeu du %(date)s a été supprimé avec succès."
def get_queryset(self):
return super().get_queryset().filter(group__owner=self.request.user)
def get(self, request, pk):
game = self.get_object()
group = game.group
game.delete()
messages.add_message(
request,
messages.SUCCESS,
f"Le jeu du {game.date.strftime('%x')} a été supprimé avec succès.",
)
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 += form.instance.n * [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(1, len(pm_list) + 1)):
music.blacklisted = True
music.save()
models.MusicGameOrder.objects.create(
game=form.instance, player=player, music_video=music, order=order
)
if creds := self.request.user.youtubecredentials:
form.instance.playlist_loading = True
form.instance.save()
tasks.generate_playlist.delay_on_commit(creds.credentials, form.instance.pk)
return res
class GameDetailView(LoginRequiredMixin, DetailView):
model = models.MusikGame
def get_queryset(self):
return (
super()
.get_queryset()
.filter(
Q(group__members=self.request.user) | Q(group__owner=self.request.user)
)
.distinct()
)
class YoutubeLoginView(LoginRequiredMixin, View):
def get(self, request):
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
settings.YOUTUBE_OAUTH_SECRETS,
["https://www.googleapis.com/auth/youtube.force-ssl"],
)
flow.redirect_uri = "https://localhost/youtube_callback/"
auth_url, state = flow.authorization_url(
access_type="offline",
include_granted_scopes="true",
prompt="consent",
)
self.request.session["state"] = state
return redirect(auth_url)
class YoutubeCallbackView(LoginRequiredMixin, View):
def get(self, request):
if err := request.GET.get("error"):
messages.add_message(
request, messages.ERROR, f"Échec de la connexion à Youtube : {err}"
)
return redirect("/")
state = self.request.session.get("state")
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
settings.YOUTUBE_OAUTH_SECRETS,
["https://www.googleapis.com/auth/youtube.force-ssl"],
state=state,
)
flow.redirect_uri = "https://localhost/youtube_callback/"
flow.fetch_token(code=request.GET.get("code"))
credentials = flow.credentials
models.YoutubeCredentials.objects.update_or_create(
user=request.user,
defaults={
"credentials": {
"token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"granted_scopes": credentials.granted_scopes,
}
},
)
messages.add_message(request, messages.SUCCESS, "Connexion à Youtube réussie.")
return redirect("/")
class GroupClearBlacklistView(OwnerFilterMixin, SingleObjectMixin, View):
model = models.Group
def get(self, request, pk):
group = self.get_object()
group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False)
messages.add_message(request, messages.SUCCESS, "La blacklist a été vidée.")
return redirect(group)