from django.shortcuts import render from django.views import View from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.template.response import TemplateResponse from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Avg from django.urls import reverse import re import os from datetime import UTC, datetime from collections import OrderedDict from dotenv import load_dotenv from .models import Track, Artist, Album, Vote, Background, Profile, Playlist from .utils import track_from_json, get_unvoted from .spotify import spt from .tasks import get_banter, get_and_dither_image load_dotenv() class AuthView(View): def get(self, request): if request.user.is_authenticated: return HttpResponseRedirect('/vote/') else: return TemplateResponse(request, "Login.html", {}) class VoteView(LoginRequiredMixin, View): def get(self, request, undo=None): (track, skipped) = get_unvoted(request.user) if track and not track.album.image: if not track.album.image_url: get_and_dither_image("https://picsum.photos/256", track.album, "image") get_and_dither_image(track.album.image_url, track.album, "image") return TemplateResponse(request, "Vote.html", {"track": track, "undo": undo, "skipped": skipped}) def post(self, request): (profile,_) = Profile.objects.get_or_create(user=request.user) try: if not profile.can_vote: raise Exception(f"{request.user} tried to vote after closing for {track}") spotify_id = self.request.POST['spotify_id'] score = int(self.request.POST['vote']) score = min(max(1, score), 5) track = Track.objects.get(pk=spotify_id) try: prevote = Vote.objects.get(user=request.user, track=track) except Vote.DoesNotExist: pass else: if not prevote.skipped: raise Exception(f"{request.user} already voted for {track}") else: prevote.delete() vote = Vote.objects.create(track=track, user=request.user, points=score) (profile,_) = Profile.objects.get_or_create(user=request.user) profile.last_voted = track profile.save() return self.get(request, undo=vote) except Exception as e: print(e) class NominateView(LoginRequiredMixin, View): def get(self, request): (profile,_) = Profile.objects.get_or_create(user=request.user) profile.update_quota() return TemplateResponse(request, "Nominate.html", {'end_time': os.getenv('DATE_NOM_END')}) def post(self, request): (profile,_) = Profile.objects.get_or_create(user=request.user) if not profile.can_nominate[0]: return TemplateResponse(request, "Nominate.html", {"message_type": "error", "message": "Nee nee nee, dat gaan we dus even niet doen."}) try: spotify_link = self.request.POST['spotify_link'] # Short url? if re.match(r"^https:\/\/spotify.link.*", spotify_link): spotify_link = spt.unshort(spotify_link) m = re.match(r"^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)\??", spotify_link) spotify_id = m.group(1) except: return TemplateResponse(request, "Nominate.html", {"message_type": "error", "message": "Geef een geldige spotify link op!"}) try: track = Track.all_tracks.get(pk=spotify_id) if track.old: track.old = False track.nominated_by = request.user track.save() get_banter.delay(track.id) return TemplateResponse(request, "Nominate.html", { "message_type": "warning", "message": f"Hey een klassieker, die stond al in {track.from_playlist}. Geen probleem, we proberen hem nog een keer dit jaar!", "message_title": str(track) }) return TemplateResponse(request, "Nominate.html", { "message_type": "error", "message_title": str(track), "message": "Deze is al genomineerd dit jaar, probeer eens iets anders.", }) except Track.DoesNotExist: try: json = spt.get_song_info(spotify_id) except Exception as e: print(e) return TemplateResponse(request, "Nominate.html", {"message_type": "error", "message": "Fout bij het ophalen van het liedje."}) track = track_from_json(json) track.nominated_by = request.user track.save() dup = track.is_duplicate_of() if dup: track.delete() return TemplateResponse(request, "Nominate.html", { "message_type": "error", "message": "Die hadden we al knul.", "message_title": str(dup) }) get_banter.delay(track.id) profile.quota -= 1 profile.save() return TemplateResponse(request, "Nominate.html", {"message_type": "nominated", "message_title": str(track), "message": "Genomineerd!"}) class SettingsView(LoginRequiredMixin, View): def get(self, request): backgrounds = Background.objects.all() (profile,created) = Profile.objects.get_or_create(user=request.user) context = { "backgrounds": backgrounds, "currentBackground": (profile.background.id if profile.background else 1) } spt_token = profile.get_or_update_spt() if spt_token: context["sptDisplayName"] = spt.get_profile(spt_token).get('display_name') return TemplateResponse(request, "Settings.html", context) def post(self, request): try: bg = request.POST.get('background', None) unlink_spotify = request.POST.get('unlink-spotify', None) (profile,_) = Profile.objects.get_or_create(user=request.user) if bg: bg = Background.objects.get(pk=request.POST['background']) profile.background = bg if unlink_spotify: print("unlink spt") profile.unlink_spt() profile.save() except: pass finally: return self.get(request) class UndoView(LoginRequiredMixin, View): def get(self, request): return HttpResponseRedirect('/vote/') def post(self, request): spotify_id = self.request.POST['spotify_id'] track = Track.objects.get(pk=spotify_id) (profile,_) = Profile.objects.get_or_create(user=request.user) if profile.last_voted != track: raise Exception(f"{request.user} is not allowed to undo vote for {track}") vote = None try: vote = Vote.objects.get(user=request.user, track=track) except Vote.DoesNotExist: raise Exception(f"{request.user} did not vote for {track}") profile.last_voted = None profile.save() vote.delete() return HttpResponseRedirect('/vote/') class SkipView(LoginRequiredMixin, View): def get(self, request): return HttpResponseRedirect('/vote/') def post(self, request): spotify_id = self.request.POST['spotify_id'] track = Track.objects.get(pk=spotify_id) (profile,_) = Profile.objects.get_or_create(user=request.user) if not profile.can_vote: raise Exception(f"{request.user} tried to vote after closing for {track}") vote = None try: vote = Vote.objects.get(user=request.user, track=track) except Vote.DoesNotExist: pass profile.last_voted = None profile.save() vote = Vote.objects.create(track=track, user=request.user, points=0, skipped=True) profile.save() return self.get(request) class OverView(LoginRequiredMixin, View): def get(self, request): votes = Vote.objects.filter(user=request.user).select_related().distinct() artists = {} for vote in votes: for artist in vote.track.artists.all(): if not artists.get(artist.name): artists[artist.name] = {} for artist in vote.track.album.artists.all(): if not artists.get(artist.name): artists[artist.name] = {} if not artists[artist.name].get(vote.track.album.name): artists[artist.name][vote.track.album.name] = [] artists[artist.name][vote.track.album.name] += [vote.track] artists_ordered = OrderedDict(sorted(artists.items())) context = { "artists": artists_ordered } return TemplateResponse(request, "Overview.html", context) class PlaylistListView(LoginRequiredMixin, View): def get(self, request, count=None): context = { "playlists": Playlist.objects.all() } return TemplateResponse(request, "PlaylistList.html", context) def post(self, request): name = self.request.POST.get('name', None) if name: new_playlist = Playlist(name=name) return TemplateResponse(request, "PlaylistList.html") return self.get(request, count=count) class PlaylistDetailView(LoginRequiredMixin, View): def get(self, request, count=None): context = {} if count: #tracks = Track.objects.order_by('score')[count] tracks = Track.objects.annotate(average_points=Avg('vote__points')).order_by('-average_points')[:count] context["tracks"] = tracks return TemplateResponse(request, "PlaylistDetail.html", context) def post(self, request): try: count = int(self.request.POST['count']) return self.get(request, count=count) except Exception as e: print(e) return self.get(request) class SpotifyView(LoginRequiredMixin, View): def get(self, request): url = spt.get_oauth_redirect() return HttpResponseRedirect(url) class SpotifyCallbackView(LoginRequiredMixin, View): def get(self, request): error = self.request.GET.get('error') code = self.request.GET.get('code') if error: return TemplateResponse(request, "Settings.html", {"message": "Je moet wel accepteren"}) elif code: (profile,_) = Profile.objects.get_or_create(user=request.user) profile.init_spt(code) return HttpResponseRedirect(reverse('settings')) else: url = spt.get_oauth_redirect() return HttpResponseRedirect(url) class PlaylistImportView(LoginRequiredMixin, View): def get(self, request): return HttpResponseRedirect('/settings/') def post(self, request): if not request.user.is_superuser: return HttpResponseRedirect('/settings/') playlist_link = self.request.POST['playlist_link'] m = re.match(r"^https:\/\/open.spotify.com\/playlist\/([a-zA-Z0-9]+)\??", playlist_link) playlist_id = m.group(1) print(playlist_id) json = spt.get_playlist_info(playlist_id) tracks = json["tracks"]["items"] playlist_name = json["name"] for track in tracks: track_id = track["track"]["id"] try: json = spt.get_song_info(track_id) track = track_from_json(json) dup = track.is_duplicate_of() if dup: print(f"{track} hadden we al") track.delete() else: track.old = True track.from_playlist = playlist_name track.save() except Exception as e: print(e) return self.get(request) class StroopwafelView(View): def get(self, request): nominated_count = Track.objects.all().count() return JsonResponse( { "openDate": os.getenv('DATE_OPEN'), "deadlineNominations": os.getenv('DATE_NOM_END'), "deadlineVoting": os.getenv('DATE_VOTE_END'), "stemCount": nominated_count } )