Skip to content
Snippets Groups Projects
views.py 15 KiB
Newer Older
  • Learn to ignore specific revisions
  • from urllib import parse
    
    
    from django.contrib import messages
    
    from django.contrib.auth.decorators import login_required
    
    from django.core.exceptions import PermissionDenied
    
    Dan Braghis's avatar
    Dan Braghis committed
    from django.http import HttpResponseRedirect
    from django.shortcuts import get_object_or_404
    from django.urls import reverse_lazy
    
    from django.utils import timezone
    
    Dan Braghis's avatar
    Dan Braghis committed
    from django.utils.decorators import method_decorator
    
    from django.utils.translation import ugettext_lazy as _
    
    Fredrik Jonsson's avatar
    Fredrik Jonsson committed
    from django.views.generic import DetailView, CreateView
    
    from opentech.apply.activity.models import Activity
    
    Fredrik Jonsson's avatar
    Fredrik Jonsson committed
    from opentech.apply.activity.messaging import messenger, MESSAGES
    
    Dan Braghis's avatar
    Dan Braghis committed
    from opentech.apply.funds.models import ApplicationSubmission
    
    from opentech.apply.funds.workflow import DETERMINATION_OUTCOMES
    
    George Hickman's avatar
    George Hickman committed
    from opentech.apply.utils.views import CreateOrUpdateView, ViewDispatcher
    
    Fredrik Jonsson's avatar
    Fredrik Jonsson committed
    from opentech.apply.users.decorators import staff_required
    
    from .forms import (
        BatchConceptDeterminationForm,
        BatchProposalDeterminationForm,
        ConceptDeterminationForm,
        ProposalDeterminationForm,
    )
    
    Fredrik Jonsson's avatar
    Fredrik Jonsson committed
    from .models import Determination, DeterminationMessageSettings, NEEDS_MORE_INFO, TRANSITION_DETERMINATION
    
    
    from .utils import (
        can_create_determination,
        can_edit_determination,
        has_final_determination,
        outcome_from_actions,
        transition_from_outcome,
    )
    
    def get_form_for_stages(submissions):
        forms = [
            get_form_for_stage(submission, batch=True)
            for submission in submissions
        ]
        if len(set(forms)) != 1:
            raise ValueError('Submissions expect different forms - please contact and admin')
    
        return forms[0]
    
    
    def get_form_for_stage(submission, batch=False):
        if batch:
            forms = [BatchConceptDeterminationForm, BatchProposalDeterminationForm]
        else:
            forms = [ConceptDeterminationForm, ProposalDeterminationForm]
    
        index = submission.workflow.stages.index(submission.stage)
    
    Dan Braghis's avatar
    Dan Braghis committed
        return forms[index]
    
    
    
    @method_decorator(staff_required, name='dispatch')
    class BatchDeterminationCreateView(CreateView):
    
        template_name = 'determinations/batch_determination_form.html'
    
    
        def dispatch(self, *args, **kwargs):
    
            self._submissions = None
    
            if not self.get_action() or not self.get_submissions():
                messages.warning(self.request, 'Improperly configured request, please try again.')
                return HttpResponseRedirect(self.get_success_url())
    
            return super().dispatch(*args, **kwargs)
    
    
        def get_action(self):
            return self.request.GET.get('action', '')
    
    
        def get_submissions(self):
    
            if not self._submissions:
                try:
                    submission_ids = self.request.GET.get('submissions').split(',')
                except AttributeError:
                    return None
                try:
                    ids = [int(pk) for pk in submission_ids]
                except ValueError:
                    return None
                self._submissions = ApplicationSubmission.objects.filter(id__in=ids)
            return self._submissions
    
        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
            kwargs['user'] = self.request.user
    
            kwargs['submissions'] = self.get_submissions()
    
            kwargs['action'] = self.get_action()
    
            kwargs.pop('instance')
    
        def get_form_class(self):
            return get_form_for_stages(self.get_submissions())
    
    
        def get_context_data(self, **kwargs):
    
            outcome = TRANSITION_DETERMINATION[self.get_action()]
            submission = self.get_submissions()[0]
            transition = transition_from_outcome(outcome, submission)
            action_name = submission.workflow[transition].display_name
    
            return super().get_context_data(
                action_name=action_name,
                submissions=self.get_submissions(),
                **kwargs,
            )
    
        def form_valid(self, form):
            submissions = self.get_submissions()
    
            response = super().form_valid(form)
    
            determinations = {
                determination.submission.id: determination
                for determination in form.instances
            }
    
    
            messenger(
                MESSAGES.BATCH_DETERMINATION_OUTCOME,
                request=self.request,
                user=self.request.user,
    
                submissions=submissions.filter(id__in=list(determinations)),
    
                related=determinations,
            )
    
            for submission in submissions:
    
                try:
                    determination = determinations[submission.id]
                except KeyError:
                    messages.warning(
                        self.request,
                        'Unable to determine submission "{title}" as already determined'.format(title=submission.title),
    
                else:
                    transition = transition_from_outcome(form.cleaned_data.get('outcome'), submission)
    
                    if determination.outcome == NEEDS_MORE_INFO:
                        # We keep a record of the message sent to the user in the comment
                        Activity.comments.create(
                            message=determination.stripped_message,
    
                            timestamp=timezone.now(),
    
                            user=self.request.user,
                            submission=submission,
                            related_object=determination,
                        )
    
                    submission.perform_transition(transition, self.request.user, request=self.request, notify=False)
    
        @classmethod
        def should_redirect(cls, request, submissions, actions):
            excluded = []
            for submission in submissions:
                if has_final_determination(submission):
                    excluded.append(submission)
    
    
            non_determine_states = set(actions) - set(DETERMINATION_OUTCOMES.keys())
            if not any(non_determine_states):
    
                if excluded:
                    messages.warning(
                        request,
                        _('A determination already exists for the following submissions and they have been excluded: {submissions}').format(
                            submissions=', '.join([submission.title for submission in excluded]),
                        )
                    )
    
                submissions = submissions.exclude(id__in=[submission.id for submission in excluded])
                action = outcome_from_actions(actions)
                return HttpResponseRedirect(
                    reverse_lazy('apply:submissions:determinations:batch') +
    
                    "&submissions=" + ','.join([str(submission.id) for submission in submissions]) +
                    "&next=" + parse.quote_plus(request.get_full_path()),
    
            elif set(actions) != non_determine_states:
                raise ValueError('Inconsistent states provided - please talk to an admin')
    
            try:
                return self.request.GET['next']
            except KeyError:
                return reverse_lazy('apply:submissions:list')
    
    @method_decorator(staff_required, name='dispatch')
    
    Dan Braghis's avatar
    Dan Braghis committed
    class DeterminationCreateOrUpdateView(CreateOrUpdateView):
        model = Determination
        template_name = 'determinations/determination_form.html'
    
        def get_object(self, queryset=None):
    
            return self.model.objects.get(submission=self.submission, is_draft=True)
    
    Dan Braghis's avatar
    Dan Braghis committed
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
    
    
            if not can_create_determination(request.user, self.submission):
    
                return self.back_to_submission(_('You do not have permission to create that determination.'))
    
            if has_final_determination(self.submission):
                return self.back_to_submission(_('A final determination has already been submitted.'))
    
                self.determination = self.get_object()
    
            except Determination.DoesNotExist:
                pass
            else:
    
                if not can_edit_determination(request.user, self.determination, self.submission):
                    return self.back_to_detail(_('There is a draft determination you do not have permission to edit.'))
    
    Dan Braghis's avatar
    Dan Braghis committed
            return super().dispatch(request, *args, **kwargs)
    
    
        def back_to_submission(self, message):
            messages.warning(self.request, message)
            return HttpResponseRedirect(self.submission.get_absolute_url())
    
        def back_to_detail(self, message):
            messages.warning(self.request, message)
            return HttpResponseRedirect(self.determination.get_absolute_url())
    
    
    Dan Braghis's avatar
    Dan Braghis committed
        def get_context_data(self, **kwargs):
    
            determination_messages = DeterminationMessageSettings.for_site(self.request.site)
    
    Dan Braghis's avatar
    Dan Braghis committed
            return super().get_context_data(
                submission=self.submission,
    
                message_templates=determination_messages.get_for_stage(self.submission.stage.name),
    
    Dan Braghis's avatar
    Dan Braghis committed
                **kwargs
            )
    
        def get_form_class(self):
            return get_form_for_stage(self.submission)
    
        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
    
            kwargs['user'] = self.request.user
    
    Dan Braghis's avatar
    Dan Braghis committed
            kwargs['submission'] = self.submission
    
            kwargs['action'] = self.request.GET.get('action')
    
    Dan Braghis's avatar
    Dan Braghis committed
            return kwargs
    
        def get_success_url(self):
            return self.submission.get_absolute_url()
    
    
        def form_valid(self, form):
    
            super().form_valid(form)
    
            if not self.object.is_draft:
    
                messenger(
                    MESSAGES.DETERMINATION_OUTCOME,
                    request=self.request,
                    user=self.object.author,
                    submission=self.object.submission,
    
                transition = transition_from_outcome(form.cleaned_data.get('outcome'), self.submission)
    
    
                if self.object.outcome == NEEDS_MORE_INFO:
                    # We keep a record of the message sent to the user in the comment
    
                        message=self.object.stripped_message,
    
                        timestamp=timezone.now(),
    
                        user=self.request.user,
                        submission=self.submission,
    
    Todd Dembrey's avatar
    Todd Dembrey committed
                        related_object=self.object,
    
                self.submission.perform_transition(transition, self.request.user, request=self.request, notify=False)
    
            return HttpResponseRedirect(self.submission.get_absolute_url())
    
        @classmethod
        def should_redirect(cls, request, submission, action):
            if has_final_determination(submission):
                determination = submission.determinations.final().first()
                if determination.outcome == TRANSITION_DETERMINATION[action]:
                    # We want to progress as normal so don't redirect through form
                    return False
                else:
    
                    if request:
                        # Add a helpful message to prompt them to select the correct option
                        messages.warning(
                            request,
                            _('A determination of "{current}" exists but you tried to progress as "{target}"').format(
                                current=determination.get_outcome_display(),
                                target=action,
                            )
    
                        )
    
            if action in DETERMINATION_OUTCOMES:
                return HttpResponseRedirect(reverse_lazy(
                    'apply:submissions:determinations:form',
    
                    args=(submission.id,)) + "?action=" + action
                )
    
    @method_decorator(staff_required, name='dispatch')
    class AdminDeterminationDetailView(DetailView):
        model = Determination
    
        def get_object(self, queryset=None):
    
            return self.model.objects.get(submission=self.submission, id=self.kwargs['pk'])
    
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
            determination = self.get_object()
    
    
            if can_edit_determination(request.user, determination, self.submission) and determination.is_draft:
    
                return HttpResponseRedirect(reverse_lazy('apply:submissions:determinations:form', args=(self.submission.id,)))
    
            return super().dispatch(request, *args, **kwargs)
    
    
    
    @method_decorator(login_required, name='dispatch')
    class ReviewerDeterminationDetailView(DetailView):
        model = Determination
    
        def get_object(self, queryset=None):
    
            return self.model.objects.get(submission=self.submission, id=self.kwargs['pk'])
    
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
            determination = self.get_object()
    
            if not determination.submitted:
                return HttpResponseRedirect(reverse_lazy('apply:submissions:detail', args=(self.submission.id,)))
    
            return super().dispatch(request, *args, **kwargs)
    
    
    
    @method_decorator(login_required, name='dispatch')
    class PartnerDeterminationDetailView(DetailView):
        model = Determination
    
    
        def get_queryset(self):
            return super().get_queryset().filter(submission=self.submission)
    
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, pk=self.kwargs['submission_pk'])
    
            if self.submission.user == request.user:
                return ApplicantDeterminationDetailView.as_view()(request, *args, **kwargs)
    
            # Only allow partners in the submission they are added as partners
            partner_has_access = self.submission.partners.filter(pk=request.user.pk).exists()
            if not partner_has_access:
                raise PermissionDenied
    
            return super().dispatch(request, *args, **kwargs)
    
    
    @method_decorator(login_required, name='dispatch')
    class CommunityDeterminationDetailView(DetailView):
        model = Determination
    
    
        def get_queryset(self):
            return super().get_queryset().filter(submission=self.submission)
    
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, pk=self.kwargs['submission_pk'])
    
            if self.submission.user == request.user:
                return ApplicantDeterminationDetailView.as_view()(request, *args, **kwargs)
    
            # Only allow community reviewers in submission with a community review state.
            if not self.submission.community_review:
                raise PermissionDenied
    
            return super().dispatch(request, *args, **kwargs)
    
    
    
    @method_decorator(login_required, name='dispatch')
    
    class ApplicantDeterminationDetailView(DetailView):
    
    Dan Braghis's avatar
    Dan Braghis committed
        model = Determination
    
    
    Dan Braghis's avatar
    Dan Braghis committed
        def get_object(self, queryset=None):
    
            return self.model.objects.get(submission=self.submission, id=self.kwargs['pk'])
    
    Dan Braghis's avatar
    Dan Braghis committed
    
    
    Dan Braghis's avatar
    Dan Braghis committed
        def dispatch(self, request, *args, **kwargs):
    
    Dan Braghis's avatar
    Dan Braghis committed
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
    
    Dan Braghis's avatar
    Dan Braghis committed
            determination = self.get_object()
    
    
            if request.user != self.submission.user:
    
    Dan Braghis's avatar
    Dan Braghis committed
                raise PermissionDenied
    
            if determination.is_draft:
    
                return HttpResponseRedirect(reverse_lazy('apply:submissions:determinations:detail', args=(self.submission.id,)))
    
    Dan Braghis's avatar
    Dan Braghis committed
    
            return super().dispatch(request, *args, **kwargs)
    
    
    
    class DeterminationDetailView(ViewDispatcher):
        admin_view = AdminDeterminationDetailView
    
        reviewer_view = ReviewerDeterminationDetailView
    
        partner_view = PartnerDeterminationDetailView
        community_view = CommunityDeterminationDetailView