Skip to content
Snippets Groups Projects
views.py 12.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • from django.contrib.auth.decorators import login_required
    
    from django.contrib.auth.mixins import UserPassesTestMixin
    
    from django.core.exceptions import PermissionDenied
    
    from django.http import HttpResponseRedirect
    
    from django.shortcuts import get_object_or_404
    
    from django.template.loader import get_template
    
    from django.urls import reverse_lazy
    
    from django.utils.decorators import method_decorator
    
    from django.views.generic import CreateView, ListView, DetailView, DeleteView
    
    from wagtail.core.blocks import RichTextBlock
    
    
    from opentech.apply.activity.messaging import messenger, MESSAGES
    
    from opentech.apply.funds.models import ApplicationSubmission, AssignedReviewers
    
    from opentech.apply.review.blocks import RecommendationBlock, RecommendationCommentsBlock
    
    from opentech.apply.review.forms import ReviewModelForm, ReviewOpinionForm
    
    from opentech.apply.stream_forms.models import BaseStreamForm
    
    from opentech.apply.users.decorators import staff_required
    
    from opentech.apply.users.groups import REVIEWER_GROUP_NAME
    
    from opentech.apply.utils.views import CreateOrUpdateView
    
    from opentech.apply.utils.image import generate_image_tag
    
    from .options import DISAGREE
    
    class ReviewContextMixin:
        def get_context_data(self, **kwargs):
    
            assigned_reviewers = self.object.assigned.review_order()
    
    
            if not self.object.stage.has_external_review:
                assigned_reviewers = assigned_reviewers.staff()
    
            # Calculate the recommendation based on role and staff reviews
            recommendation = self.object.reviews.by_staff().recommendation()
    
    
            return super().get_context_data(
    
                hidden_types=[REVIEWER_GROUP_NAME],
                staff_reviewers_exist=assigned_reviewers.staff().exists(),
                assigned_reviewers=assigned_reviewers,
    
    def get_fields_for_stage(submission):
    
        forms = submission.get_from_parent('review_forms').all()
    
        index = submission.workflow.stages.index(submission.stage)
    
    Dan Braghis's avatar
    Dan Braghis committed
        try:
    
            return forms[index].form.form_fields
    
    Dan Braghis's avatar
    Dan Braghis committed
        except IndexError:
    
            return forms[0].form.form_fields
    
    @method_decorator(login_required, name='dispatch')
    
    class ReviewCreateOrUpdateView(BaseStreamForm, CreateOrUpdateView):
        submission_form_class = ReviewModelForm
    
        model = Review
    
        template_name = 'review/review_form.html'
    
        def get_object(self, queryset=None):
    
            return self.model.objects.get(submission=self.submission, author__reviewer=self.request.user)
    
    
        def dispatch(self, request, *args, **kwargs):
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
    
    
            if not self.submission.phase.permissions.can_review(request.user) or not self.submission.has_permission_to_review(request.user):
    
                raise PermissionDenied()
    
            if self.request.POST and self.submission.reviewed_by(request.user):
                return self.get(request, *args, **kwargs)
    
            return super().dispatch(request, *args, **kwargs)
    
        def get_context_data(self, **kwargs):
            has_submitted_review = self.submission.reviewed_by(self.request.user)
            return super().get_context_data(
                submission=self.submission,
                has_submitted_review=has_submitted_review,
    
                title="Update Review draft" if self.object else 'Create Review',
    
        def get_defined_fields(self):
            return get_fields_for_stage(self.submission)
    
    
        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
    
            kwargs['user'] = self.request.user
    
            kwargs['submission'] = self.submission
    
    
            if self.object:
    
                kwargs['initial'] = self.object.form_data
    
        def form_valid(self, form):
    
            form.instance.form_fields = self.get_defined_fields()
    
            form.instance.author, _ = AssignedReviewers.objects.get_or_create_for_user(
                submission=self.submission,
                reviewer=self.request.user,
            )
    
    
            response = super().form_valid(form)
    
    
            if not self.object.is_draft:
    
                messenger(
                    MESSAGES.NEW_REVIEW,
                    request=self.request,
    
                    user=self.request.user,
    
                    submission=self.submission,
    
        def get_success_url(self):
            return self.submission.get_absolute_url()
    
    
    
    class ReviewDisplay(UserPassesTestMixin, DetailView):
    
        model = Review
    
        raise_exception = True
    
        def get_context_data(self, **kwargs):
    
            if review.author.reviewer != self.request.user:
    
                consensus_form = ReviewOpinionForm(
    
                    instance=review.opinions.filter(author__reviewer=self.request.user).first(),
    
                )
            else:
                consensus_form = None
            return super().get_context_data(
                form=consensus_form,
                **kwargs,
            )
    
        def test_func(self):
    
            review = self.get_object()
    
            user = self.request.user
    
            author = review.author
    
            partner_has_access = submission.partners.filter(pk=user.pk).exists()
    
            if user.is_apply_staff:
    
                return True
    
            if user == author:
                return True
    
            if user.is_reviewer and review.reviewer_visibility:
                return True
    
            if user.is_partner and partner_has_access and review.reviewer_visibility and submission.user != user:
                return True
    
            if user.is_community_reviewer and submission.community_review and review.reviewer_visibility and submission.user != user:
                return True
    
            return False
    
        def dispatch(self, request, *args, **kwargs):
            review = self.get_object()
    
    
            if review.is_draft:
    
                return HttpResponseRedirect(reverse_lazy('apply:submissions:reviews:form', args=(review.submission.id,)))
    
    
            return super().dispatch(request, *args, **kwargs)
    
    
    
    class ReviewOpinionFormView(UserPassesTestMixin, CreateView):
    
        template_name = 'review/review_detail.html'
        form_class = ReviewOpinionForm
        model = Review
    
            self.object = self.get_object()
    
            kwargs = super().get_form_kwargs()
            instance = kwargs['instance']
    
            kwargs['instance'] = instance.opinions.filter(author__reviewer=self.request.user).first()
    
        def test_func(self):
            review = self.get_object()
            user = self.request.user
            author = review.author
            submission = review.submission
            partner_has_access = submission.partners.filter(pk=user.pk).exists()
    
            if user.is_apply_staff:
                return True
    
            if user == author:
                return False
    
            if user.is_reviewer and review.reviewer_visibility:
                return True
    
            if user.is_partner and partner_has_access and review.reviewer_visibility and submission.user != user:
                return True
    
            if user.is_community_reviewer and submission.community_review and review.reviewer_visibility and submission.user != user:
                return True
    
            return False
    
    
        def form_valid(self, form):
            self.review = self.get_object()
    
            author, _ = AssignedReviewers.objects.get_or_create_for_user(
    
                submission=self.review.submission,
                reviewer=self.request.user,
            )
            form.instance.author = author
    
            form.instance.review = self.review
    
            response = super().form_valid(form)
    
            opinion = form.instance
    
    
            messenger(
                MESSAGES.REVIEW_OPINION,
                request=self.request,
                user=self.request.user,
    
                submission=self.review.submission,
    
    Todd Dembrey's avatar
    Todd Dembrey committed
            if opinion.opinion == DISAGREE:
    
                return HttpResponseRedirect(reverse_lazy('apply:submissions:reviews:form', args=(self.review.submission.pk,)))
    
            return self.review.get_absolute_url()
    
    
    
    @method_decorator(login_required, name='dispatch')
    class ReviewDetailView(DetailView):
        def get(self, request, *args, **kwargs):
            view = ReviewDisplay.as_view()
            return view(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            view = ReviewOpinionFormView.as_view()
            return view(request, *args, **kwargs)
    
    
    
    @method_decorator(staff_required, name='dispatch')
    
    class ReviewListView(ListView):
        model = Review
    
        def get_queryset(self):
            self.submission = get_object_or_404(ApplicationSubmission, id=self.kwargs['submission_pk'])
    
            self.queryset = self.model.objects.filter(submission=self.submission, is_draft=False)
    
            return super().get_queryset()
    
    
        def should_display(self, field):
            return not isinstance(field.block, (RecommendationBlock, RecommendationCommentsBlock, RichTextBlock))
    
    
        def get_context_data(self, **kwargs):
            review_data = {}
    
    
            # Add the header rows
            review_data['title'] = {'question': '', 'answers': list()}
    
            review_data['opinions'] = {'question': 'Opinions', 'answers': list()}
    
            review_data['score'] = {'question': 'Overall Score', 'answers': list()}
            review_data['recommendation'] = {'question': 'Recommendation', 'answers': list()}
    
            review_data['revision'] = {'question': 'Revision', 'answers': list()}
    
            review_data['comments'] = {'question': 'Comments', 'answers': list()}
    
            responses = self.object_list.count()
    
            ordered_reviewers = AssignedReviewers.objects.filter(submission=self.submission).reviewed().review_order()
    
            reviews = {review.author: review for review in self.object_list}
            for i, reviewer in enumerate(ordered_reviewers):
                review = reviews[reviewer]
    
                author = '<a href="{}"><span>{}</span></a>'.format(review.get_absolute_url(), review.author)
                if review.author.role:
                    author += generate_image_tag(review.author.role.icon, '12x12')
                author = f'<div>{author}</div>'
    
                review_data['title']['answers'].append(author)
    
                opinions_template = get_template('review/includes/review_opinions_list.html')
                opinions_html = opinions_template.render({'opinions': review.opinions.select_related('author').all()})
                review_data['opinions']['answers'].append(opinions_html)
    
                review_data['score']['answers'].append(review.get_score_display)
    
                review_data['recommendation']['answers'].append(review.get_recommendation_display())
    
                review_data['comments']['answers'].append(review.get_comments_display(include_question=False))
    
                if review.for_latest:
                    revision = 'Current'
                else:
                    revision = '<a href="{}">Compare</a>'.format(review.get_compare_url())
                review_data['revision']['answers'].append(revision)
    
                for field_id in review.fields:
                    field = review.field(field_id)
                    data = review.data(field_id)
    
                    if self.should_display(field):
    
                        question = field.value['field_label']
    
                        review_data.setdefault(field.id, {'question': question, 'answers': [''] * responses})
                        review_data[field.id]['answers'][i] = field.block.render(None, {'data': data})
    
    
            return super().get_context_data(
                submission=self.submission,
                review_data=review_data,
                **kwargs
            )
    
    class ReviewDeleteView(UserPassesTestMixin, DeleteView):
    
        raise_exception = True
    
        def test_func(self):
            review = self.get_object()
            return self.request.user.has_perm('review.delete_review') or self.request.user == review.author
    
    
        def delete(self, request, *args, **kwargs):
            review = self.get_object()
            messenger(
                MESSAGES.DELETE_REVIEW,
                user=request.user,
                request=request,
                submission=review.submission,
                related=review,
            )
            response = super().delete(request, *args, **kwargs)
            return response
    
        def get_success_url(self):
            review = self.get_object()
            return reverse_lazy('funds:submissions:detail', args=(review.submission.id,))