150 lines
5.6 KiB
Python
150 lines
5.6 KiB
Python
from collections import defaultdict
|
|
import logging
|
|
|
|
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 reverse
|
|
import django.forms
|
|
|
|
from extensions.models import Extension
|
|
from reviewers.forms import CommentForm
|
|
from reviewers.models import ApprovalActivity
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# the ordering of this list determines the order of rows in approval queue listing
|
|
STATUS_CHANGE_TYPES = [
|
|
ApprovalActivity.ActivityType.AWAITING_REVIEW,
|
|
ApprovalActivity.ActivityType.AWAITING_CHANGES,
|
|
ApprovalActivity.ActivityType.APPROVED,
|
|
]
|
|
|
|
|
|
class ApprovalQueueView(ListView):
|
|
model = Extension
|
|
paginate_by = 50
|
|
|
|
def get_queryset(self):
|
|
qs = ApprovalActivity.objects.prefetch_related(
|
|
'extension',
|
|
'extension__authors',
|
|
'extension__icon',
|
|
'extension__versions',
|
|
'extension__versions__file',
|
|
'extension__versions__file__validation',
|
|
).order_by('-id')
|
|
by_extension = {}
|
|
by_date_created = []
|
|
for item in qs:
|
|
extension = item.extension
|
|
stats = by_extension.get(extension, None)
|
|
if not stats:
|
|
# this check guarantees that we add a record only once per extension,
|
|
# and iterating over qs we get by_date_created also ordered by item.date_created
|
|
stats = {
|
|
'count': 0,
|
|
'extension': extension,
|
|
'last_activity': None,
|
|
'last_type_display': None,
|
|
'type': None,
|
|
}
|
|
by_extension[extension] = stats
|
|
by_date_created.append(stats)
|
|
stats['count'] += 1
|
|
if not stats.get('last_activity', None):
|
|
stats['last_activity'] = item
|
|
if not stats.get('last_type_display', None) and item.type in STATUS_CHANGE_TYPES:
|
|
stats['last_type_display'] = item.get_type_display()
|
|
stats['type'] = item.type
|
|
groupped_by_type = defaultdict(list)
|
|
for stats in by_date_created:
|
|
type = stats['type'] or ApprovalActivity.ActivityType.AWAITING_REVIEW
|
|
groupped_by_type[type].append(stats)
|
|
result = []
|
|
for type in STATUS_CHANGE_TYPES:
|
|
result.extend(groupped_by_type[type])
|
|
return result
|
|
|
|
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',
|
|
'preview_set',
|
|
'preview_set__file',
|
|
'versions',
|
|
).all()
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx['review_activity'] = (
|
|
self.object.review_activity.select_related('user')
|
|
.prefetch_related('user__groups')
|
|
.order_by('date_created')
|
|
.all()
|
|
)
|
|
ctx['status_change_types'] = 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):
|
|
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()
|
|
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)
|