Oleg Komarov
13ac2436ad
see #238 This change improves the listing performance: old code had to process all ApprovalActivity to compute an extension's moderation status and position in the queue. Now we maintain a sortkey, a reference to the latest "meaningful" activity object, and a total comment count. These fields are updated in a post_save signal. "Meaningful" activity means moderation status changes: approved, awaiting changes, awaiting review. "Non-meaningful" activity shouldn't affect queue position anymore and extensions without "meaningful" activity should not appear in the queue, but their respective detail pages should still be reachable via a direct link. This UX may still need improvement, and #210 may be relevant here. Reviewed-on: #240 Reviewed-by: Anna Sirota <annasirota@noreply.localhost>
144 lines
5.2 KiB
Python
144 lines
5.2 KiB
Python
import logging
|
|
|
|
from actstream.actions import follow, is_following, unfollow
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.views.generic.list import ListView
|
|
from django.views.generic import DetailView, FormView
|
|
from django.shortcuts import redirect, reverse
|
|
import django.forms
|
|
|
|
from constants.activity import Flag
|
|
from extensions.models import Extension
|
|
from reviewers.forms import CommentForm
|
|
from reviewers.models import ApprovalActivity, ApprovalQueue
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class ApprovalQueueView(ListView):
|
|
model = Extension
|
|
paginate_by = 50
|
|
|
|
def get_queryset(self):
|
|
return (
|
|
ApprovalQueue.objects.select_related(
|
|
'extension',
|
|
'latest_activity',
|
|
)
|
|
.prefetch_related(
|
|
'extension__authors',
|
|
'extension__icon',
|
|
'extension__versions',
|
|
'extension__versions__files',
|
|
'extension__versions__files__validation',
|
|
)
|
|
.order_by('-sortkey')
|
|
)
|
|
|
|
template_name = 'reviewers/extensions_review_list.html'
|
|
|
|
|
|
class ExtensionsApprovalDetailView(DetailView):
|
|
model = Extension
|
|
|
|
template_name = 'reviewers/extensions_review_detail.html'
|
|
|
|
def get_queryset(self):
|
|
return self.model.objects.prefetch_related(
|
|
'authors',
|
|
'latest_version__files',
|
|
'latest_version__files__validation',
|
|
'latest_version__permissions',
|
|
'latest_version__platforms',
|
|
'latest_version__tags',
|
|
'preview_set',
|
|
'preview_set__file',
|
|
).all()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
review_activity = (
|
|
self.object.review_activity.select_related('user')
|
|
.prefetch_related('user__groups')
|
|
.order_by('date_created')
|
|
.all()
|
|
)
|
|
maintainers = self.object.all_maintainers()
|
|
for activity in review_activity:
|
|
if activity.user in maintainers:
|
|
activity.user_is_maintainer = True
|
|
|
|
ctx['review_activity'] = review_activity
|
|
ctx['status_change_types'] = ApprovalActivity.STATUS_CHANGE_TYPES
|
|
|
|
if self.request.user.is_authenticated:
|
|
initial = {}
|
|
if 'report_compatibility_issue' in self.request.GET:
|
|
version = self.request.GET.get('version')
|
|
initial['message'] = (
|
|
f'Compatibility range for version {version} is outdated\n'
|
|
'Platform: ...\n'
|
|
'Version of Blender where the add-on is **no longer** working: ?????'
|
|
)
|
|
form = ctx['comment_form'] = CommentForm(initial=initial)
|
|
# anyone can comment
|
|
filtered_activity_types = {ApprovalActivity.ActivityType.COMMENT}
|
|
user = self.request.user
|
|
if self.object.has_maintainer(user):
|
|
ctx['is_maintainer'] = self.object.has_maintainer(self.request.user)
|
|
filtered_activity_types.add(ApprovalActivity.ActivityType.AWAITING_REVIEW)
|
|
if user.is_moderator or user.is_superuser:
|
|
filtered_activity_types.update(
|
|
[
|
|
ApprovalActivity.ActivityType.APPROVED,
|
|
ApprovalActivity.ActivityType.AWAITING_CHANGES,
|
|
]
|
|
)
|
|
choices = list(
|
|
filter(
|
|
lambda c: c[0] in filtered_activity_types, ApprovalActivity.ActivityType.choices
|
|
)
|
|
)
|
|
form.fields['type'].choices = choices
|
|
form.fields['type'].widget.choices = choices
|
|
if len(choices) == 1:
|
|
form.fields['type'].widget = django.forms.HiddenInput()
|
|
ctx['user_is_following'] = is_following(user, self.object, flag=Flag.REVIEWER)
|
|
return ctx
|
|
|
|
|
|
class ExtensionsApprovalFormView(LoginRequiredMixin, FormView):
|
|
form_class = CommentForm
|
|
http_method_names = ['post']
|
|
|
|
def get_success_url(self):
|
|
return reverse('reviewers:approval-detail', kwargs={'slug': self.kwargs['slug']})
|
|
|
|
def approve_if_allowed(self, form):
|
|
if form.cleaned_data['type'] != ApprovalActivity.ActivityType.APPROVED:
|
|
return
|
|
if not (self.request.user.is_moderator or self.request.user.is_superuser):
|
|
log.error('Non-moderator tried to approve extension %s' % form.instance.extension)
|
|
return
|
|
form.instance.extension.approve()
|
|
|
|
def form_valid(self, form):
|
|
form.instance.user = self.request.user
|
|
form.instance.extension = Extension.objects.get(slug=self.kwargs['slug'])
|
|
form.save()
|
|
self.approve_if_allowed(form)
|
|
return super().form_valid(form)
|
|
|
|
|
|
class ExtensionsApprovalFollowView(LoginRequiredMixin, DetailView):
|
|
model = Extension
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
extension = self.get_object()
|
|
user = request.user
|
|
if request.POST.get('follow'):
|
|
follow(user, extension, send_action=False, flag=Flag.REVIEWER)
|
|
else:
|
|
unfollow(user, extension, send_action=False, flag=Flag.REVIEWER)
|
|
return redirect(extension.get_review_url() + '#activity')
|