from django.shortcuts import render, get_object_or_404, redirect, resolve_url from django.views.generic import TemplateView, View, RedirectView from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.list import ListView from django.views.generic.edit import CreateView from django.views.generic.base import ContextMixin from django.http import HttpResponseRedirect from django.core.exceptions import SuspiciousOperation from markdown2 import markdown from datetime import datetime from urllib.parse import urlparse from . import models, forms from base64 import b64decode import logging logger = logging.getLogger(__name__) class EnsembleMixin(object): def dispatch(self, request, *args, **kwargs): request.ensemble_id = request.session.get('ensemble') if not request.ensemble_id: return redirect('register') return super().dispatch(request, *args, **kwargs) class ProjectMixin(EnsembleMixin): def get_project(self): if not hasattr(self, '_project'): self._project = get_object_or_404(models.Project, pk=self.kwargs['project'], ensemble=self.request.ensemble_id) return self._project def get_queryset(self): return super().get_queryset().filter(project=self.get_project()) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['project'] = self.get_project() return context class S3UploadMixin(ProjectMixin): def get_cancel_url(self): return self.cancel_url def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) success_url = self.request.build_absolute_uri(self.get_success_url()) key_template = self.object.key_template() project = self.get_project() context['upload'] = project.presigned_post(key_template, fields={'success_action_redirect': success_url}, conditions=[["starts-with", "$success_action_redirect", ""]]) context['ajax_upload'] = project.presigned_post(key_template) context['success_url'] = success_url context['cancel_url'] = self.get_cancel_url() return context class S3CompleteMixin(View): always_set = False def complete(self, key): self.object.key = key self.object.save() def get(self, request, *args, **kwargs): self.object = self.get_object() if self.always_set or not self.object.key: if 'location' in request.GET: uri = urlparse(request.GET['location']) self.complete(uri.path[1:]) elif 'key' in request.GET: self.complete(request.GET['key']) else: raise KeyError("No key or location found") return super().get(request, *args, **kwargs) def register(request): if 'clear' in request.GET: request.session.clear() request.ensemble_id = request.session.get('ensemble') registered = request.session.setdefault('registered', {}) if request.method == "POST": form = forms.CodeForm(request.POST) if form.is_valid(): data = form.cleaned_data try: ensemble = models.Ensemble.objects.get(code=data['code'].replace('-', '')) if ensemble.passphrase == data['passphrase']: request.session['ensemble'] = ensemble.pk registered[ensemble.code] = ensemble.pk return redirect('ensemble_detail') except models.Ensemble.DoesNotExist: form.add_error(None, "Incorrect code or passphrase") else: form = forms.CodeForm(initial=request.GET) current = models.Ensemble.objects.filter(pk__in=registered.values()) return render(request, 'interface/register.html', {'form': form, 'current': current}) class EnsembleDetailView(EnsembleMixin, DetailView): def get_object(self): return models.Ensemble.objects.get(pk=self.request.ensemble_id) class ProjectDetailView(EnsembleMixin, DetailView): def get_queryset(self): return models.Project.objects.filter(ensemble=self.request.ensemble_id) class WikiView(ProjectMixin, DetailView): template_name = 'interface/wiki.html' model = models.WikiPage def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) data['wiki_html'] = markdown(self.object.markdown) return data class SubmissionCreateView(ProjectMixin, CreateView): model = models.Submission fields = ['name', 'instrument', 'notes'] template_name = "interface/submission_create.html" def form_valid(self, form): self.object = form.save(commit=False) self.object.project = self.get_project() self.object.save() self.request.session['name'] = self.object.name self.request.session['instrument'] = self.object.instrument return redirect('submission_upload', project=self.object.project.pk, pk=self.object.pk) def get_initial(self): return { k: self.request.session.get(k) for k in ('name', 'instrument') } class SubmissionDetailView(ProjectMixin, S3CompleteMixin, DetailView): model = models.Submission def complete(self, key): self.object.complete = True super().complete(key) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if self.request.user.is_authenticated: context['download'] = self.object.presigned_url() return context class SubmissionUploadView(S3UploadMixin, DetailView): template_name = 'interface/s3_upload.html' model = models.Submission def get_success_url(self): return resolve_url('submission_detail', **self.kwargs) def get_cancel_url(self): return resolve_url('submission_cancel', **self.kwargs) class SubmissionCancelView(ProjectMixin, SingleObjectMixin, View): model = models.Submission def get(self, request, *args, **kwargs): self.object = self.get_object() self.object.delete() return redirect('project_detail', pk=kwargs['project']) class ResourceCreateView(ProjectMixin, CreateView): model = models.Resource fields = ['name', 'media_type', 'description'] template_name = 'interface/project_form.html' title = "Add a new resource" def form_valid(self, form): if not self.request.user.is_authenticated: raise SuspiciousOperation("Must be logged in to create resources") self.object = form.save(commit=False) self.object.project = self.get_project() self.object.save() return redirect('resource_upload', project=self.object.project_id, pk=self.object.pk) class ResourceUploadView(S3UploadMixin, DetailView): model = models.Resource template_name = 'interface/submission_upload.html' def get_success_url(self): return resolve_url('resource_complete', **self.kwargs) def get_cancel_url(self): return resolve_url('resource_list', project=self.kwargs['project']) class ResourceCompleteView(S3CompleteMixin, SingleObjectMixin, RedirectView): model = models.Resource always_set = True def get_redirect_url(self, **kwargs): return resolve_url('resource_list', project=self.kwargs['project']) class ResourceListView(ProjectMixin, ListView): model = models.Resource