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 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): def get_queryset(self): return Work.objects.filter(ensemble=self.request.ensemble_id).order_by('name') 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) return f def form_valid(self, form): obj = form.save(commit=False) obj.ensemble_id = self.request.ensemble_id try: obj.save() except IntegrityError: form.add_error('name', 'Name must be unique') return self.form_invalid(form) # handle the files uploads = self.request.FILES.getlist('uploads') docs = [] for f in uploads: docs.append(obj.docs.create(upload=f).pk) if len(docs) == 1: return redirect('document_annotate', docs[0]) else: return redirect('work_detail', pk=obj.pk) class WorkDetailView(EnsembleMixin, DetailView): def get_queryset(self): return Work.objects.filter(ensemble=self.request.ensemble_id) 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 Work.objects.filter(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__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.instruments} return data def get_queryset(self): return Document.objects.filter(work__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__ensemble=self.request.ensemble_id).select_related('doc', 'doc__work')