from django.shortcuts import render, redirect, resolve_url from django.views.generic.detail import DetailView, SingleObjectMixin, View from django.views.generic.list import ListView, MultipleObjectMixin from django.views.generic.edit import CreateView, FormView, UpdateView from django.http import FileResponse, HttpResponse from django.db import IntegrityError from django.db.models import Q, Count from django.utils.timezone import now import json from interface.views import EnsembleMixin, ProjectMixin from interface.models import Project from .models import Work, Document, Part, INSTRUMENTS from . import forms, models from .pdf_utils import extract_pages, extract_and_concat class ProjectItemListView(ProjectMixin, ListView): template_name = "library/item_list.html" model = models.Item def post(self, request, **kwargs): project = self.get_project() project_works = project.works.all() instruments = request.POST.getlist('instruments') works = request.POST.getlist('works') self.request.session['part'] = request.POST.get('part', '') self.request.session['instrument'] = request.POST.get('instrument') valid_pks = [ x.pk for x in project_works ] sections = [] for i, pk in enumerate(works): if int(pk) not in valid_pks: raise Exception(f"Not a valid work pk: {pk}") tag = instruments[i] if tag == '-': continue part = Part.objects.filter(tag=tag, doc__work=pk).select_related('doc').get() sections.append((part.doc.upload.path, part.doc.work.name, part.start, part.end, 1)) result = extract_and_concat(sections) download_name = f'{project.name}.pdf' response = FileResponse(result, content_type="application/pdf") response['Content-Disposition'] = f'inline; filename="{download_name}"' return response def get_queryset(self): return super(ProjectItemListView, self).get_queryset().select_related('project', 'work') def get_context_data(self, **kwargs): data = super(ProjectItemListView, self).get_context_data(**kwargs) data['instruments'] = INSTRUMENTS data['instrument'] = self.request.session.get('instrument', 'Score') data['part'] = self.request.session.get('part', '0') return data class ProjectItemManageView(ProjectMixin, ListView): template_name = "library/item_list_manage.html" model = models.Item def post(self, request, **kwargs): self.request = request self.kwargs = kwargs data = json.loads(request.body) q = self.get_queryset() for pk, order in data.items(): order = int(order) if order == -1: q.filter(pk=pk).delete() else: i = q.filter(pk=pk).update(order=order) return HttpResponse(status=204) def get_queryset(self): return super(ProjectItemManageView, self).get_queryset().select_related('project', 'work') class ProjectItemAddView(ProjectMixin, UpdateView): form_class = forms.PlaylistAddForm template_name = "interface/default_form.html" def get_success_url(self): return resolve_url('item_list_manage', project=self.kwargs['project']) def get_object(self): return self.get_project() class WorkListView(EnsembleMixin, ListView): paginate_by = 20 def get_queryset(self): #works = Work.objects.filter(collection__ensembles=self.request.ensemble_id).order_by('name').select_related('collection') works = Work.objects.filter(collection__allowed_ensembles__ensemble=self.request.ensemble_id).order_by('name').select_related('collection') loan_count = Count('project_items', Q(project_items__checkout__lte=now(), project_items__returned=None)) works = works.annotate(loan_count=loan_count) q = self.request.GET.get('filter') if q: works = works.filter(Q(name__contains=q) | Q(composer__contains=q) | Q(tag_list__contains=q)) return works class WorkAddView(EnsembleMixin, FormView): template_name = "interface/default_form.html" form_class = forms.WorkCreateForm def get_form(self): f = super(WorkAddView, self).get_form() #qs = f.fields['orchestration'].queryset #f.fields['orchestration'].queryset = qs.filter(ensemble_id__isnull=True) | qs.filter(ensemble_id=self.request.ensemble_id) qs = f.fields['collection'].queryset qs = qs.filter(administrators=self.request.user) f.fields['collection'].queryset = qs return f def form_valid(self, form): work = form.save(commit=False) work.ensemble_id = self.request.ensemble_id work.save() # handle the files uploads = self.request.FILES.getlist('uploads') docs = [] for f in uploads: docs.append(work.docs.create(upload=f).pk) if len(docs) == 1: return redirect('document_annotate', docs[0]) else: return redirect('work_detail', pk=work.pk) class WorkDetailView(EnsembleMixin, DetailView): def get_queryset(self): return Work.objects.filter(collection__allowed_ensembles__ensemble=self.request.ensemble_id) class WorkAddToProject(EnsembleMixin, FormView): form_class = forms.ProjectSelectForm template_name = "interface/default_form.html" title = "Select project to add work to" def get_object(self): return Work.objects.get(pk=self.kwargs['pk']) def get_form(self): f = super(WorkAddToProject, self).get_form() qs = f.fields['project'].queryset work = self.get_object() qs = qs.filter(ensemble_id=self.request.ensemble_id).exclude(pk__in=work.projects.all()) f.fields['project'].queryset = qs return f def form_valid(self, form): work = self.get_object() project = form.cleaned_data['project'] work.project_items.create(project=project, approved_by=self.request.user, checkout=now()) return redirect('item_list', project=project.pk) class WorkPartSetView(EnsembleMixin, DetailView): template_name = "library/work_partset.html" def post(self, request, *args, **kwargs): work = self.get_object() parts = request.POST.getlist('parts') copies = request.POST.getlist('copies') sections = [] for i, tag in enumerate(parts): part = work.parts.select_related('doc').get(tag=tag) sections.append((part.doc.upload.path, part.instrument, part.start, part.end, int(copies[i]))) result = extract_and_concat(sections) download_name = f'{work.name}.pdf' response = FileResponse(result, content_type="application/pdf") response['Content-Disposition'] = f'inline; filename="{download_name}"' return response def get_queryset(self): return Version.objects.filter(work__ensemble_id=self.request.ensemble_id) class DocumentDetailView(EnsembleMixin, DetailView): def get_queryset(self): return Document.objects.filter(work__ensemble=self.request.ensemble_id).select_related('work') class DocumentAddView(EnsembleMixin, CreateView): template_name = "interface/default_form.html" model = Document fields = ['upload'] def form_valid(self, form): self.object = form.save(commit=False) self.object.work_id = self.kwargs['pk'] self.object.save() return redirect('document_annotate', self.object.pk) class DocumentDownloadView(EnsembleMixin, SingleObjectMixin, View): def get(self, request, **args): self.request = request self.args = args self.object = self.get_object() #response = FileResponse(self.object.upload, content_type="application/pdf") #return response return redirect(self.object.upload.url) def get_queryset(self): return Document.objects.filter(work__collection__allowed_ensembles__ensemble=self.request.ensemble_id) class DocumentAnnotateView(EnsembleMixin, DetailView): template_name = 'library/document_annotate.html' def post(self, request, **args): self.request = request self.args = args self.object = self.get_object() data = json.loads(request.body) tags = {} for page, tag in data.items(): tags.setdefault(tag, []).append(int(page)) try: del(tags['None']) except KeyError: pass self.object.parts.all().delete() for tag, pages in tags.items(): pages.sort() end = pages[-1] if len(pages) > 1 else None o = self.object.parts.create(tag=tag, start=pages[0], end=end) return HttpResponse(status=204) def get_context_data(self, **kwargs): data = super(DocumentAnnotateView, self).get_context_data(**kwargs) pages = {} for part in data['document'].parts.all(): for i in range(part.start, (part.end or part.start)+1): pages[i] = part.tag data['json_data'] = {'pageTags': pages, 'instruments': data['document'].work.orchestration} return data def get_queryset(self): return Document.objects.filter(work__collection__allowed_ensembles__ensemble=self.request.ensemble_id).select_related('work') class PartDownloadView(EnsembleMixin, SingleObjectMixin, View): def get(self, request, **args): self.request = request self.args = args self.object = self.get_object() result = extract_pages(self.object.doc.upload.path, self.object.doc.work.name, self.object.start, self.object.end) download_name = f'{self.object.doc.work.name}_{self.object.instrument}.pdf' response = FileResponse(result, content_type="application/pdf") response['Content-Disposition'] = f'inline; filename="foo.pdf"' return response def get_queryset(self): return Part.objects.filter(doc__work__collection__allowed_ensembles__ensemble=self.request.ensemble_id).select_related('doc', 'doc__work')