Add 'insert' view for inserting ratings on paper

main
Luca 2 years ago
parent bb7e7b5089
commit d0694ed38a

@ -214,3 +214,23 @@ svg {
max-width: var(--max-size);
width: 3em;
}
.row {
display: flex;
margin-bottom: 0.2em;
}
.row > :not(:first-child) {
margin-left: 0.5em !important;
}
.row > .rating-container {
justify-content: center;
margin: 0;
padding: 0;
}
.row > .rating-container > .rating > .rating-point {
height: 1em;
width: 1em;
}

@ -1,5 +1,11 @@
from django import forms
from django.conf import settings
class InsertForm(forms.Form):
name = forms.CharField()
rating = forms.ChoiceField(choices=settings.RATING_CHOICES, required=False, widget=forms.RadioSelect)
InsertFormSet = forms.formset_factory(InsertForm)
class RateArtistForm(forms.Form):
rating = forms.ChoiceField(choices=settings.RATING_CHOICES, required=False, widget=forms.RadioSelect)

@ -0,0 +1,126 @@
{% extends "core/base.html" %}
{% block title %}A/D-Wandler{% endblock %}
{% block body %}
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="row">
{{ form.name }}
<div class="rating-container">
<div class="rating" style="--num-choices: {{ form.rating | length }}">
{% for radio in form.rating %}
{{ radio.tag }}
<label class="rating-point" for="{{ radio.id_for_label }}"></label>
{% endfor %}
</div>
</div>
<button class="reset" type="button">Zurücksetzen</button>
</div>
{% endfor %}
<button id="add" type="button">+</button>
<button type="submit">Speichern</button>
</form>
{{ playlist | json_script:"playlist" }}
<script>
const playlist = JSON.parse(document.getElementById('playlist').textContent);
const findBestMatch = value => playlist.find(name => name.toLowerCase().includes(value) || value.includes(name.toLowerCase())) || '';
const registerEvents = row => {
const input = row.querySelector('input[name$=name]');
input.addEventListener('input', () => {
const value = (input.dataset.value || '') + input.value.toLowerCase();
input.dataset.value = value;
const bestMatch = findBestMatch(value);
input.placeholder = bestMatch;
input.value = '';
input.dataset.bestMatch = bestMatch;
});
input.addEventListener('keydown', event => {
if (event.key === 'Backspace') {
if (input.value !== '') {
input.value = '';
} else {
const value = (input.dataset.value || '').slice(0, -1);
input.dataset.value = value;
if (value === '') {
input.placeholder = '';
input.dataset.bestMatch = '';
} else {
const bestMatch = findBestMatch(value);
input.placeholder = bestMatch;
input.dataset.bestMatch = bestMatch;
}
}
event.preventDefault();
} else if (event.key === 'Tab') {
input.placeholder = '';
input.value = input.dataset.bestMatch || '';
input.dataset.bestMatch = '';
input.dataset.value = '';
event.preventDefault();
}
});
row.querySelector('.reset').addEventListener('click', event => {
event.target.parentElement.querySelectorAll('.rating-container > .rating > input').forEach(input => {
input.checked = false;
});
});
};
document.querySelectorAll('.row').forEach(registerEvents);
const totalForms = document.getElementById('id_form-TOTAL_FORMS');
let numForms = Number.parseInt(totalForms.value);
const replace = value => value.replace(/form-\d+-(.+)/, `form-${numForms}-$1`);
const addBtn = document.getElementById('add');
addBtn.addEventListener('click', () => {
const row = document.querySelector('.row').cloneNode(true);
row.querySelectorAll('[name^=form-]').forEach(input => {
const label = row.querySelector(`label[for=${input.id}]`);
if (label) {
label.htmlFor = replace(label.htmlFor);
}
input.id = replace(input.id);
input.name = replace(input.name);
});
const input = row.querySelector('input[name$=name]');
input.placeholder = '';
input.value = '';
input.dataset.bestMatch = '';
input.dataset.value = '';
row.querySelectorAll('.rating-container > .rating > input').forEach(input => {
input.checked = false;
});
registerEvents(row);
addBtn.before(row);
numForms += 1;
totalForms.value = numForms;
});
</script>
{% endblock %}

@ -4,6 +4,7 @@ from . import views
urlpatterns = [
path('create_token/', views.create_token, name='create_token'),
path('insert/<slug:session>/', views.insert, name='insert'),
path('join/<slug:session>/', views.join_session, name='join_session'),
path('participate/<slug:token>/', views.participate, name='participate'),
path('<slug:playlist>/', include([

@ -1,10 +1,11 @@
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from .forms import RateArtistForm
from .forms import InsertFormSet, RateArtistForm
from .models import Participant, Rating
from ..core.models import Session
from ..core.models import Artist, Session
# Create your views here.
@ -21,6 +22,34 @@ def create_token(request):
return render(request, 'vote/create_token.html', {'token': token})
@login_required
@transaction.atomic
def insert(request, session):
session = get_object_or_404(Session, pk=session)
formset = InsertFormSet()
if request.method == 'POST':
formset = InsertFormSet(request.POST)
if formset.is_valid():
participant = Participant(session=session)
participant.save()
for form in formset:
if not form.cleaned_data.get('name'):
continue
artist = get_object_or_404(Artist, name=form.cleaned_data['name'])
rating = Rating(artist=artist, participant=participant, value=form.cleaned_data['rating'])
rating.save()
formset = InsertFormSet() # clear formset if successful
playlist = list(session.playlist.artist_set.values_list('name', flat=True))
return render(request, 'vote/insert.html', {'formset': formset, 'playlist': playlist})
def join_session(request, session):
session = get_object_or_404(Session, pk=session)

Loading…
Cancel
Save