deadlines

This commit is contained in:
2024-07-19 17:30:36 +02:00
parent 8a7ae9c2f9
commit 5858cee900
8 changed files with 182 additions and 50 deletions
+4 -1
View File
@@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Track, Artist, Album, Background, Vote from .models import Track, Artist, Album, Background, Vote, Profile
class TrackAdmin(admin.ModelAdmin): class TrackAdmin(admin.ModelAdmin):
pass pass
@@ -11,9 +11,12 @@ class BackgroundAdmin(admin.ModelAdmin):
pass pass
class VoteAdmin(admin.ModelAdmin): class VoteAdmin(admin.ModelAdmin):
pass pass
class ProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(Track, TrackAdmin) admin.site.register(Track, TrackAdmin)
admin.site.register(Artist, ArtistAdmin) admin.site.register(Artist, ArtistAdmin)
admin.site.register(Album, AlbumAdmin) admin.site.register(Album, AlbumAdmin)
admin.site.register(Background, BackgroundAdmin) admin.site.register(Background, BackgroundAdmin)
admin.site.register(Vote, VoteAdmin) admin.site.register(Vote, VoteAdmin)
admin.site.register(Profile, ProfileAdmin)
+38 -1
View File
@@ -4,8 +4,12 @@ from .track import Track
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from datetime import datetime, timedelta from datetime import datetime, timedelta, UTC
from ..spotify import spt from ..spotify import spt
import os
from dotenv import load_dotenv
load_dotenv()
NOMINATIONS_PER_DAY = 10 NOMINATIONS_PER_DAY = 10
MAX_QUOTA = 22 MAX_QUOTA = 22
@@ -43,6 +47,39 @@ class Profile(models.Model):
print(f"Er zijn {elapsed.seconds}s voorbij, je nieuwe quota is {self.quota}") print(f"Er zijn {elapsed.seconds}s voorbij, je nieuwe quota is {self.quota}")
self.quota_last_updated = timezone.now() self.quota_last_updated = timezone.now()
@property
def can_nominate(self):
"""Is this user allowed to nominate at this point in time?"""
end_date = os.getenv('DATE_NOM_END')
if self.user.is_superuser:
return (True,)
if end_date:
end_date = datetime.fromisoformat(end_date)
if end_date <= datetime.now(tz=UTC):
return (False, "over")
if self.quota < 1:
return (False, "quota")
return (True,)
@property
def can_vote(self):
"""Is this user allowed to vote at this point in time?"""
end_date = os.getenv('DATE_VOTE_END')
if self.user.is_superuser or not end_date:
return True
end_date = datetime.fromisoformat(end_date)
if end_date <= datetime.now(tz=UTC):
return False
return True
@property
def vote_end(self):
return os.getenv('DATE_VOTE_END')
@property
def nominate_end(self):
return os.getenv('DATE_NOM_END')
def init_spt(self, code): def init_spt(self, code):
"""Initial setting of spotify tokens from user accept code""" """Initial setting of spotify tokens from user accept code"""
data = spt.get_token_json( data = spt.get_token_json(
+37
View File
@@ -0,0 +1,37 @@
let timer = null;
function startCountdown(dateString) {
if (timer) clearInterval(timer);
let cd = document.getElementById("countdown")
if (cd) {
cd.classList.remove("hide");
if (dateString == "None") cd.classList.add("hide");
}
const end = new Date(dateString).getTime();
const dayEl = document.getElementById('days');
const hoursEl = document.getElementById('hours');
const minutesEl = document.getElementById('minutes');
const secondsEl = document.getElementById('seconds');
const seconds = 1000;
const minutes = seconds * 60;
const hours = minutes * 60;
const days = hours * 24;
timer = setInterval(updateTime, seconds);
function updateTime() {
if (dayEl) {
let now = new Date().getTime();
const difference = end - now;
if (difference < 0) {
if (timer) clearInterval(timer);
cd.classList.add("hide");
return;
}
dayEl.innerText = Math.floor(difference / days);
hoursEl.innerText = Math.floor( (difference % days) / hours );
minutesEl.innerText = Math.floor( (difference % hours) / minutes );
secondsEl.innerText = Math.floor( (difference % minutes) / seconds );
}
}
updateTime();
}
+22 -3
View File
@@ -7,10 +7,17 @@ Nomineren
{% block main-content %} {% block main-content %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<p>Vul een spotify link in om een nummer te nomineren.</p> {% if user.profile.can_nominate.0 %}
<input type="text" id="spotify-input" name="spotify_link" placeholder="Spotify-link"> <p>Vul een spotify link in om een nummer te nomineren.</p>
<input type="submit" value="Insturen" {% if user.profile.quota < 1%}disabled{%endif%}> <input type="text" id="spotify-input" name="spotify_link" placeholder="Spotify-link">
<input type="submit" value="Insturen" {% if user.profile.quota < 1%}disabled{%endif%}>
{% elif user.profile.can_nominate.1 == 'quota' %}
<p>Je quotum is op. Je zal helaas eventjes moeten wachten met nomineren.</p>
{% elif user.profile.can_nominate.1 == 'over' %}
<p>Het nomineren is voorbij voor dit jaar! Bedankt voor de inzendingen.</p>
{% endif %}
</form> </form>
{% endblock %} {% endblock %}
{% block status-bar%} {% block status-bar%}
@@ -23,4 +30,16 @@ Nomineren
</div> </div>
<p class="status-bar-field">{{user.profile.quota_display}} nominaties over.</p> <p class="status-bar-field">{{user.profile.quota_display}} nominaties over.</p>
</div> </div>
<div class="status-bar" id="countdown">
<p class="status-bar-field"><span id="days"></span> dagen</p>
<p class="status-bar-field"><span id="hours"></span> uur</p>
<p class="status-bar-field"><span id="minutes"></span> minuten</p>
<p class="status-bar-field"><span id="seconds"></span> seconden</p>
</div>
<script>
document.body.addEventListener('htmx:load', function(evt) {
//startCountdown("2024-06-25T23:59:59+02:00");
startCountdown("{{user.profile.nominate_end}}");
});
</script>
{% endblock %} {% endblock %}
+2 -2
View File
@@ -5,14 +5,14 @@ Overzicht
{% endblock %} {% endblock %}
{% block multi-row %} {% block multi-row %}
<menu role="tablist" hx-boost="true" class="multirows"> {% comment %} <menu role="tablist" hx-boost="true" class="multirows">
<li role="tab" aria-selected="true"> <li role="tab" aria-selected="true">
<a href="{% url 'overview' %}">Tracks</a> <a href="{% url 'overview' %}">Tracks</a>
</li> </li>
<li role="tab"> <li role="tab">
<a href="{% url 'playlist-list' %}">Playlists</a> <a href="{% url 'playlist-list' %}">Playlists</a>
</li> </li>
</menu> </menu> {% endcomment %}
{% endblock %} {% endblock %}
{% block main-content %} {% block main-content %}
+68 -41
View File
@@ -5,59 +5,86 @@ Stemmen
{% endblock %} {% endblock %}
{% block main-content %} {% block main-content %}
{% if track %} {% if user.profile.can_vote %}
<div id="vote-box" hx-target="body"> {% if track %}
{% if error %}<div class="error">{{ error }}</div>{% endif %} <div id="vote-box" hx-target="body">
{% include 'MediaPlayer.html' with track=track %} {% if error %}<div class="error">{{ error }}</div>{% endif %}
{% include 'MediaPlayer.html' with track=track %}
<div <div
id="vote-stars" id="vote-stars"
hx-vals='{"spotify_id": "{{ track.id }}"}' hx-vals='{"spotify_id": "{{ track.id }}"}'
style="margin-top:10px;" style="margin-top:10px;"
> >
<fieldset> <fieldset>
<legend>Hoeveel sterren geef jij dit?</legend> <legend>Hoeveel sterren geef jij dit?</legend>
<div class="star-wrapper"> <div class="star-wrapper">
<a <a
hx-vals='{"vote": "1"}' hx-vals='{"vote": "1"}'
hx-post="{% url 'vote' %}" hx-post="{% url 'vote' %}"
></a> ></a>
<a <a
hx-vals='{"vote": "2"}' hx-vals='{"vote": "2"}'
hx-post="{% url 'vote' %}" hx-post="{% url 'vote' %}"
></a> ></a>
<a <a
hx-vals='{"vote": "3"}' hx-vals='{"vote": "3"}'
hx-post="{% url 'vote' %}" hx-post="{% url 'vote' %}"
></a> ></a>
<a <a
hx-vals='{"vote": "4"}' hx-vals='{"vote": "4"}'
hx-post="{% url 'vote' %}" hx-post="{% url 'vote' %}"
></a> ></a>
<a <a
hx-vals='{"vote": "5"}' hx-vals='{"vote": "5"}'
hx-post="{% url 'vote' %}" hx-post="{% url 'vote' %}"
></a> ></a>
</div> </div>
</fieldset> </fieldset>
{% if undo %}
<!--<form class="undo-wrapper" method="post" action="{% url 'undo' %}" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' >-->
<div class="undo-wrapper" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' >
{% csrf_token %}
<p>Je hebt {{undo.track}} een score van {{undo.points}} sterren gegeven!</p>
<button class="undo" hx-post="{% url 'undo' %}" >Undo</button>
</div>
{% endif %}
</div>
</div>
{% else %}
<p hx-boost="true">
Je hebt niets meer om te stemmen. Lekker bezig!
<a href="{% url 'nominate' %}">Nomineer zelf iets</a>, als je nog quota hebt.
</p>
{% if undo %} {% if undo %}
<!--<form class="undo-wrapper" method="post" action="{% url 'undo' %}" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' >--> <!--<form class="undo-wrapper" method="post" action="{% url 'undo' %}" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' >-->
<div class="undo-wrapper" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' > <div class="undo-wrapper" hx-target="body" hx-vals='{"spotify_id": "{{ undo.track.id }}"}' >
{% csrf_token %} {% csrf_token %}
<p>Je hebt {{undo.track}} een score van {{undo.points}} sterren gegeven!</p> <p>Je hebt {{undo.track}} een score van {{undo.points}} sterren gegeven!</p>
<button class="undo" hx-post="{% url 'undo' %}" >Undo</button> <button class="undo" hx-post="{% url 'undo' %}" >Undo</button>
</div> </div>
{% endif %} {% endif %}
</div> {% endif %}
</div>
{% else %} {% else %}
<p hx-boost="true"> <p> Stemmen is voorbij. Joe.</p>
Je hebt niets meer om te stemmen. Lekker bezig!
<a href="{% url 'nominate' %}">Nomineer zelf iets</a>, als je nog quota hebt.
</p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block clippy %} {% block clippy %}
{% include "Clippy.html" with body=track.banter img="img/draakje/draakje-headphones3.gif" %} {% include "Clippy.html" with body=track.banter img="img/draakje/draakje-headphones3.gif" %}
{% endblock %} {% endblock %}
{% block status-bar%}
<div class="status-bar" id="countdown">
<p class="status-bar-field"><span id="days"></span> dagen</p>
<p class="status-bar-field"><span id="hours"></span> uur</p>
<p class="status-bar-field"><span id="minutes"></span> minuten</p>
<p class="status-bar-field"><span id="seconds"></span> seconden</p>
</div>
<script>
document.body.addEventListener('htmx:load', function(evt) {
//startCountdown("2024-08-25T23:59:59+02:00");
startCountdown("{{user.profile.vote_end}}");
});
</script>
{% endblock %}
+1
View File
@@ -2,6 +2,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="{% static 'js/htmx.min.js' %}" defer></script> <script src="{% static 'js/htmx.min.js' %}" defer></script>
<script src="{% static 'js/interact.js' %}" defer></script> <script src="{% static 'js/interact.js' %}" defer></script>
<script src="{% static 'js/countdown.js' %}" defer></script>
<link href="{% static '98.css/style.css' %}" rel="stylesheet" type="text/css" /> <link href="{% static '98.css/style.css' %}" rel="stylesheet" type="text/css" />
<link href="{% static 'style.css'%}" rel="stylesheet" type="text/css" /> <link href="{% static 'style.css'%}" rel="stylesheet" type="text/css" />
<style> <style>
+10 -2
View File
@@ -6,12 +6,17 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Avg from django.db.models import Avg
from django.urls import reverse from django.urls import reverse
import re import re
import os
from datetime import UTC, datetime
from collections import OrderedDict from collections import OrderedDict
from dotenv import load_dotenv
from .models import Track, Artist, Album, Vote, Background, Profile, Playlist from .models import Track, Artist, Album, Vote, Background, Profile, Playlist
from .utils import from_json, get_unvoted from .utils import from_json, get_unvoted
from .spotify import spt from .spotify import spt
from .tasks import get_banter, get_and_dither_image from .tasks import get_banter, get_and_dither_image
load_dotenv()
class AuthView(View): class AuthView(View):
def get(self, request): def get(self, request):
if request.user.is_authenticated: if request.user.is_authenticated:
@@ -26,7 +31,10 @@ class VoteView(LoginRequiredMixin, View):
get_and_dither_image(track.album.image_url, track.album, "image") get_and_dither_image(track.album.image_url, track.album, "image")
return TemplateResponse(request, "Vote.html", {"track": track, "undo": undo}) return TemplateResponse(request, "Vote.html", {"track": track, "undo": undo})
def post(self, request): def post(self, request):
(profile,_) = Profile.objects.get_or_create(user=request.user)
try: 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'] spotify_id = self.request.POST['spotify_id']
score = int(self.request.POST['vote']) score = int(self.request.POST['vote'])
score = min(max(1, score), 5) score = min(max(1, score), 5)
@@ -49,10 +57,10 @@ class NominateView(LoginRequiredMixin, View):
def get(self, request): def get(self, request):
(profile,_) = Profile.objects.get_or_create(user=request.user) (profile,_) = Profile.objects.get_or_create(user=request.user)
profile.update_quota() profile.update_quota()
return TemplateResponse(request, "Nominate.html", {}) return TemplateResponse(request, "Nominate.html", {'end_time': os.getenv('DATE_NOM_END')})
def post(self, request): def post(self, request):
(profile,_) = Profile.objects.get_or_create(user=request.user) (profile,_) = Profile.objects.get_or_create(user=request.user)
if profile.quota < 1: 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."}) return TemplateResponse(request, "Nominate.html", {"message_type": "error", "message": "Nee nee nee, dat gaan we dus even niet doen."})
try: try:
spotify_link = self.request.POST['spotify_link'] spotify_link = self.request.POST['spotify_link']