Newer
Older
from collections import defaultdict
Fredrik Jonsson
committed
from django.contrib.auth.decorators import login_required
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
from wagtail.core.blocks import RichTextBlock
from opentech.apply.activity.messaging import messenger, MESSAGES
from opentech.apply.funds.models import ApplicationSubmission
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.models import User
from opentech.apply.utils.views import CreateOrUpdateView
from .models import Review
from .options import DISAGREE
class ReviewContextMixin:
def get_context_data(self, **kwargs):
assigned = self.object.assigned.order_by('role__order').select_related('reviewer')
reviews = self.object.reviews.submitted().select_related('author')
reviews_dict = {}
for review in reviews:
reviews_dict[review.author.pk] = review
# Get all the authors of opinions, these authors should not show up in the 'xxx_not_reviewed' lists
opinion_authors = User.objects.filter(pk__in=self.object.reviews.opinions().values('author')).distinct()
reviews_block = defaultdict(list)
for assigned_reviewer in assigned:
reviewer = assigned_reviewer.reviewer
role = assigned_reviewer.role
key = None
if role:
if review:
key = 'role_reviewed'
elif reviewer not in opinion_authors:
key = 'role_not_reviewed'
elif reviewer.is_apply_staff:
if review:
key = 'staff_reviewed'
elif review not in opinion_authors:
key = 'staff_not_reviewed'
else:
if review:
key = 'external_reviewed'
Erin Mullaney
committed
else:
key = 'external_not_reviewed'
if key: # Do not add this reviewer to any list if they haven't reviewed but have left an opinion
review_info_dict = {
'reviewer': reviewer,
'review': review,
'role': role,
}
reviews_block[key].append(review_info_dict)
Erin Mullaney
committed
# Calculate the recommendation based on role and staff reviews
recommendation = self.object.reviews.by_staff().recommendation()
return super().get_context_data(
reviews_block=reviews_block,
Erin Mullaney
committed
recommendation=recommendation,
reviews_exist=reviews.count(),
assigned_staff=assigned.staff().exists(),
def get_fields_for_stage(submission):
forms = submission.get_from_parent('review_forms').all()
index = submission.workflow.stages.index(submission.stage)
return forms[index].form.form_fields
return forms[0].form.form_fields
Fredrik Jonsson
committed
@method_decorator(login_required, name='dispatch')
class ReviewCreateOrUpdateView(BaseStreamForm, CreateOrUpdateView):
submission_form_class = ReviewModelForm
template_name = 'review/review_form.html'
def get_object(self, queryset=None):
return self.model.objects.get(submission=self.submission, author=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
kwargs['initial'] = self.object.form_data
def form_valid(self, form):
form.instance.form_fields = self.get_defined_fields()
response = super().form_valid(form)
if not self.object.is_draft:
messenger(
MESSAGES.NEW_REVIEW,
request=self.request,
user=self.object.author,
submission=self.submission,
related=self.object,
)
return response
def get_success_url(self):
return self.submission.get_absolute_url()
class ReviewDisplay(DetailView):
def get_context_data(self, **kwargs):
review = self.get_object()
if review.author != self.request.user:
consensus_form = ReviewOpinionForm(
instance=review.opinions.filter(author=self.request.user).first(),
)
else:
consensus_form = None
return super().get_context_data(
form=consensus_form,
**kwargs,
)
def dispatch(self, request, *args, **kwargs):
review = self.get_object()
author = review.author
Fredrik Jonsson
committed
if request.user != author and not request.user.is_superuser and not request.user.is_apply_staff:
raise PermissionDenied
if review.is_draft:
return HttpResponseRedirect(reverse_lazy('apply:submissions:reviews:form', args=(review.submission.id,)))
return super().dispatch(request, *args, **kwargs)
class ReviewOpinionFormView(CreateView):
template_name = 'review/review_detail.html'
form_class = ReviewOpinionForm
model = Review
def get_form_kwargs(self):
self.object = self.get_object()
kwargs = super().get_form_kwargs()
instance = kwargs['instance']
kwargs['instance'] = instance.opinions.filter(author=self.request.user).first()
return kwargs
def form_valid(self, form):
self.review = self.get_object()
form.instance.author = self.request.user
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,
if opinion == DISAGREE:
return HttpResponseRedirect(reverse('apply:submissions:reviews:form', args=(self.review.submission.pk,)))
else:
return response
def get_success_url(self):
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)
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()
for i, review in enumerate(self.object_list):
Fredrik Jonsson
committed
review_data['title']['answers'].append('<a href="{}">{}</a>'.format(review.get_absolute_url(), review.author))
Erin Mullaney
committed
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(str(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)
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
)