Files
blechreiz-website/scoremanager/views.py

212 lines
7.0 KiB
Python

"""
Views for the score manager application.
"""
import json
import os
from django import forms
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import render
from django.utils.translation import gettext_lazy as _
from .models import Piece, Recording, Score, ScoreUserMapping, YoutubeRecording
def manage_repertoire(request):
"""View for managing the repertoire order and contents."""
context = {
"repertoire": Piece.get_repertoire(),
"allPieces": Piece.objects.all().order_by("title"),
}
return render(request, "scoremanager/manage_repertoire.html", context)
def manage_repertoire_ajax_save(request):
"""AJAX endpoint for saving repertoire order changes."""
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
if request.method == "POST":
try:
result = json.loads(request.body)
parsed_result = {int(key): value for (key, value) in result.items()}
# Clear all repertoire numbers first
Piece.objects.all().update(repertoire_nr=None)
# Set new repertoire numbers
for piece in Piece.objects.all():
if piece.pk in parsed_result:
piece.repertoire_nr = parsed_result[piece.pk]
piece.save()
except (json.JSONDecodeError, ValueError) as e:
return HttpResponse(f"Error: {e}", status=400)
return HttpResponse("OK")
def list_repertoire(request):
"""View for listing the current repertoire."""
context = {"repertoire": Piece.get_repertoire()}
return render(request, "scoremanager/list_repertoire.html", context)
# ----------------------------- Piece View + Ajax views + Forms ------------------------------------------
def score_user_mapping_save(request):
"""AJAX endpoint for saving a user's preferred score for a piece."""
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
if request.method == "POST":
try:
result = json.loads(request.body)
score_id = int(result["myScore"])
score = Score.objects.get(pk=score_id)
ScoreUserMapping.add_user_score_mapping(user=request.user, score=score)
except (
json.JSONDecodeError,
KeyError,
ValueError,
Score.DoesNotExist,
) as e:
return HttpResponse(f"Error: {e}", status=400)
return HttpResponse("OK")
def score(request, pk):
"""View for serving or deleting a score's preview image."""
try:
requested_score = Score.objects.get(pk=pk)
except Score.DoesNotExist:
return HttpResponse("Score not found", status=404)
if request.method == "GET":
image_file = requested_score.get_image_file()
image_path = os.path.join(settings.MEDIA_ROOT, image_file)
try:
with open(image_path, "rb") as f:
image_data = f.read()
return HttpResponse(image_data, content_type="image/jpeg")
except FileNotFoundError:
return HttpResponse("Image not found", status=404)
if request.method == "DELETE":
if not (
requested_score.uploaded_by == request.user
or request.user.has_perm("scoremanager.manage_scores")
):
raise PermissionDenied
requested_score.delete()
return HttpResponse("OK")
return HttpResponse("Method not allowed", status=405)
def youtube_link(request):
"""AJAX endpoint for adding or deleting YouTube links."""
try:
result = json.loads(request.body)
except json.JSONDecodeError:
return HttpResponse("Invalid JSON", status=400)
if request.method == "DELETE":
try:
link_id = int(result["linkid"])
youtube_recording = YoutubeRecording.objects.get(pk=link_id)
if youtube_recording.uploaded_by == request.user or request.user.has_perm(
"scoremanager.manage_scores"
):
youtube_recording.delete()
else:
raise PermissionDenied
return HttpResponse("OK")
except (KeyError, ValueError, YoutubeRecording.DoesNotExist) as e:
return HttpResponse(f"Error: {e}", status=400)
elif request.method == "POST":
try:
link = str(result["link"])
piece_id = int(result["pieceId"])
piece = Piece.objects.get(pk=piece_id)
new_recording = YoutubeRecording(piece=piece, link=link)
new_recording.uploaded_by = request.user
new_recording.save()
return HttpResponse("OK")
except (KeyError, ValueError, Piece.DoesNotExist) as e:
return HttpResponse(f"Error: {e}", status=400)
return HttpResponse("Method not allowed", status=405)
class UploadFileForm(forms.Form):
"""Form for uploading score and recording files."""
file = forms.FileField(max_length=80)
def clean_file(self):
f = self.cleaned_data["file"]
extension = os.path.splitext(f.name)[1].lower()
allowed_extensions = [".pdf", ".mp3", ".zip"]
if extension not in allowed_extensions:
raise forms.ValidationError(
_("Unknown extension. Allowed extensions are mp3, pdf and zip")
)
return f
def piece_view(request, pk):
"""View for displaying a piece's details and handling file uploads."""
try:
current_piece = Piece.objects.get(pk=pk)
except Piece.DoesNotExist:
return HttpResponse("Piece not found", status=404)
context = {"piece": current_piece}
# Find active score for user
for score_obj in current_piece.scores.all():
if score_obj.is_active_score(request.user):
context["activeScore"] = score_obj
context["pictureScore"] = score_obj
break
# Fall back to first score if no active score
if "pictureScore" not in context and current_piece.scores.exists():
context["pictureScore"] = current_piece.scores.first()
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
f = request.FILES["file"]
basename, extension = os.path.splitext(f.name)
extension = extension.lower()
if extension == ".mp3":
recording = Recording(piece=current_piece, artist=basename, file=f)
recording.uploaded_by = request.user
recording.save()
elif extension == ".pdf":
score_obj = Score(piece=current_piece, score_type=basename, file=f)
score_obj.uploaded_by = request.user
score_obj.save()
elif extension == ".zip":
# TODO: Handle zip file uploads
pass
else:
form = UploadFileForm()
context["form"] = form
return render(request, "scoremanager/piece_view.html", context)