deadlines
This commit is contained in:
+4
-1
@@ -1,5 +1,5 @@
|
||||
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):
|
||||
pass
|
||||
@@ -11,9 +11,12 @@ class BackgroundAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
class VoteAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
class ProfileAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
admin.site.register(Track, TrackAdmin)
|
||||
admin.site.register(Artist, ArtistAdmin)
|
||||
admin.site.register(Album, AlbumAdmin)
|
||||
admin.site.register(Background, BackgroundAdmin)
|
||||
admin.site.register(Vote, VoteAdmin)
|
||||
admin.site.register(Profile, ProfileAdmin)
|
||||
|
||||
@@ -4,8 +4,12 @@ from .track import Track
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, UTC
|
||||
from ..spotify import spt
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
NOMINATIONS_PER_DAY = 10
|
||||
MAX_QUOTA = 22
|
||||
@@ -42,6 +46,39 @@ class Profile(models.Model):
|
||||
self.quota = min(self.quota, MAX_QUOTA)
|
||||
print(f"Er zijn {elapsed.seconds}s voorbij, je nieuwe quota is {self.quota}")
|
||||
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):
|
||||
"""Initial setting of spotify tokens from user accept code"""
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -7,10 +7,17 @@ Nomineren
|
||||
{% block main-content %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<p>Vul een spotify link in om een nummer te nomineren.</p>
|
||||
<input type="text" id="spotify-input" name="spotify_link" placeholder="Spotify-link">
|
||||
<input type="submit" value="Insturen" {% if user.profile.quota < 1%}disabled{%endif%}>
|
||||
{% if user.profile.can_nominate.0 %}
|
||||
<p>Vul een spotify link in om een nummer te nomineren.</p>
|
||||
<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>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block status-bar%}
|
||||
@@ -23,4 +30,16 @@ Nomineren
|
||||
</div>
|
||||
<p class="status-bar-field">{{user.profile.quota_display}} nominaties over.</p>
|
||||
</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 %}
|
||||
|
||||
@@ -5,14 +5,14 @@ Overzicht
|
||||
{% endblock %}
|
||||
|
||||
{% 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">
|
||||
<a href="{% url 'overview' %}">Tracks</a>
|
||||
</li>
|
||||
<li role="tab">
|
||||
<a href="{% url 'playlist-list' %}">Playlists</a>
|
||||
</li>
|
||||
</menu>
|
||||
</menu> {% endcomment %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main-content %}
|
||||
|
||||
@@ -5,59 +5,86 @@ Stemmen
|
||||
{% endblock %}
|
||||
|
||||
{% block main-content %}
|
||||
{% if track %}
|
||||
<div id="vote-box" hx-target="body">
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
{% include 'MediaPlayer.html' with track=track %}
|
||||
{% if user.profile.can_vote %}
|
||||
{% if track %}
|
||||
<div id="vote-box" hx-target="body">
|
||||
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
||||
{% include 'MediaPlayer.html' with track=track %}
|
||||
|
||||
<div
|
||||
id="vote-stars"
|
||||
hx-vals='{"spotify_id": "{{ track.id }}"}'
|
||||
style="margin-top:10px;"
|
||||
>
|
||||
<fieldset>
|
||||
<legend>Hoeveel sterren geef jij dit?</legend>
|
||||
<div class="star-wrapper">
|
||||
<a
|
||||
hx-vals='{"vote": "1"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "2"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "3"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "4"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "5"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div
|
||||
id="vote-stars"
|
||||
hx-vals='{"spotify_id": "{{ track.id }}"}'
|
||||
style="margin-top:10px;"
|
||||
>
|
||||
<fieldset>
|
||||
<legend>Hoeveel sterren geef jij dit?</legend>
|
||||
<div class="star-wrapper">
|
||||
<a
|
||||
hx-vals='{"vote": "1"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "2"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "3"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "4"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
<a
|
||||
hx-vals='{"vote": "5"}'
|
||||
hx-post="{% url 'vote' %}"
|
||||
></a>
|
||||
</div>
|
||||
</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 %}
|
||||
<!--<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 %}
|
||||
<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>
|
||||
{% endif %}
|
||||
{% 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>
|
||||
<p> Stemmen is voorbij. Joe.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block clippy %}
|
||||
{% include "Clippy.html" with body=track.banter img="img/draakje/draakje-headphones3.gif" %}
|
||||
{% 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 %}
|
||||
@@ -2,6 +2,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="{% static 'js/htmx.min.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 'style.css'%}" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
|
||||
+10
-2
@@ -6,12 +6,17 @@ 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 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:
|
||||
@@ -26,7 +31,10 @@ class VoteView(LoginRequiredMixin, View):
|
||||
get_and_dither_image(track.album.image_url, track.album, "image")
|
||||
return TemplateResponse(request, "Vote.html", {"track": track, "undo": undo})
|
||||
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)
|
||||
@@ -49,10 +57,10 @@ 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", {})
|
||||
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 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."})
|
||||
try:
|
||||
spotify_link = self.request.POST['spotify_link']
|
||||
|
||||
Reference in New Issue
Block a user