from django.contrib.auth.models import User from django.db import models from django.db.models import F from django.db.models.functions import Lower from django.db.models.signals import post_delete, post_save from django.dispatch import receiver from django.urls import reverse from . import tasks class YoutubeCredentials(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) credentials = models.JSONField() title = models.CharField(blank=True) class Group(models.Model): name = models.CharField(verbose_name="Nom du groupe") owner = models.ForeignKey( User, on_delete=models.CASCADE, related_name="owned_group_set" ) members = models.ManyToManyField(User, blank=True) 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) 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: constraints = [ models.UniqueConstraint( fields=("yt_id", "owner", "group"), name="unique_music_in_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") playlist = models.CharField(blank=True, verbose_name="Playlist YouTube") playlist_loading = models.BooleanField(default=False) over = models.BooleanField(default=False) def get_absolute_url(self): return reverse("game_detail", kwargs={"pk": self.pk}) class Meta: ordering = ["over", "-date"] @receiver(post_save, sender=MusikGame) def generateYoutubePlaylist(sender, instance, created, **kwargs): if not instance.playlist_loading or instance.playlist: return if creds := instance.group.owner.youtubecredentials: tasks.generate_playlist.delay_on_commit(creds.credentials, instance.pk) @receiver(post_delete, sender=MusikGame) def deleteYoutubePlaylist(sender, instance, using, **kwargs): if not instance.playlist: return if creds := instance.group.owner.youtubecredentials: tasks.delete_playlist.delay_on_commit(creds.credentials, instance.playlist) 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() value = models.PositiveIntegerField(default=0) def update_value(self): n_right = self.musicgameanswer_set.filter(game__player=F("answer")).count() if n_right == 0: self.value = 1000 else: self.value = 1000 / ( 1 + ((n_right - 1) / (self.game.players.count() - 1)) ** 0.5 ) self.save() class Meta: constraints = [ models.UniqueConstraint( fields=("game", "player", "music_video"), name="unique_music_in_game" ), models.UniqueConstraint(fields=("game", "order"), name="unique_order"), ] ordering = ["order"] class AnswerManager(models.Manager): def score(self, game, player): qs = self.filter(game__game=game, player=player) return ( qs.exclude(game__player=player) .filter(game__player=F("answer")) .aggregate(score=models.Sum("game__value", default=0)) .get("score") - 500 * qs.filter(game__player=player).exclude(game__player=F("answer")).count() ) class MusicGameAnswer(models.Model): game = models.ForeignKey(MusicGameOrder, on_delete=models.CASCADE) player = models.ForeignKey(User, on_delete=models.CASCADE) answer = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name="+" ) objects = AnswerManager() class Meta: constraints = [ models.UniqueConstraint(fields=("game", "player"), name="unique_answer"), ] ordering = ["game"] class MusicGameResults(models.Model): game = models.ForeignKey(MusikGame, on_delete=models.CASCADE) player = models.ForeignKey(User, on_delete=models.CASCADE) score = models.IntegerField(default=0) class Meta: constraints = [ models.UniqueConstraint(fields=("game", "player"), name="unique_result") ] ordering = ["-score"]