Skip to content
Snippets Groups Projects
views.py 14.9 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.decorators import method_decorator
    
    from django.utils.translation import ugettext_lazy as _
    
    from django.views.generic import DetailView, CreateView
    
    from opentech.apply.activity.models import Activity
    
    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
    
    from opentech.apply.utils.views import CreateOrUpdateView, ViewDispatcher
    from opentech.apply.users.decorators import staff_required
    
    from .forms import (
        BatchConceptDeterminationForm,
        BatchProposalDeterminationForm,
        ConceptDeterminationForm,
        ProposalDeterminationForm,
    )
    
    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,
                            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,
    
                        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)
    
        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