""" 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)