Upload new version: generate approval activity item #95

Merged
Oleg-Komarov merged 4 commits from upload-new-version-activity into main 2024-04-30 10:49:05 +02:00
6 changed files with 83 additions and 44 deletions
Showing only changes of commit 0ce22fee06 - Show all commits

View File

@ -4,6 +4,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.db import transaction from django.db import transaction
from django.shortcuts import get_object_or_404, reverse from django.shortcuts import get_object_or_404, reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
@ -358,6 +359,7 @@ class DraftExtensionView(
): ):
template_name = 'extensions/draft_finalise.html' template_name = 'extensions/draft_finalise.html'
form_class = VersionForm form_class = VersionForm
msg_awaiting_review = _('Extension is ready for initial review')
@property @property
def success_message(self) -> str: def success_message(self) -> str:
@ -423,7 +425,7 @@ class DraftExtensionView(
user=self.request.user, user=self.request.user,
extension=extension_form.instance, extension=extension_form.instance,
type=ApprovalActivity.ActivityType.AWAITING_REVIEW, type=ApprovalActivity.ActivityType.AWAITING_REVIEW,
message="initial submission", message=self.msg_awaiting_review,
).save() ).save()
return super().form_valid(form) return super().form_valid(form)
except forms.ValidationError as e: except forms.ValidationError as e:

View File

@ -6,27 +6,25 @@
{{ extension.name }} {{ extension.name }}
</a> </a>
</td> </td>
<td> <td>{% include "extensions/components/authors.html" %}</td>
{% if extension.authors.count %}
{% include "extensions/components/authors.html" %}
{% endif %}
</td>
<td title="{{ extension.date_created }}">{{ extension.date_created|naturaltime_compact }}</td> <td title="{{ extension.date_created }}">{{ extension.date_created|naturaltime_compact }}</td>
<td class="d-flex"> <td class="d-flex">
<a href="{{ extension.get_review_url }}#activity"> <a href="{{ extension.get_review_url }}#activity">
<span>{{ extension.review_activity.all|length }}</span> <span>{{ stats.count }}</span>
</a> </a>
<a href="{{ extension.get_review_url }}#activity-{{ stats.last_activity.id }}" class="ml-3">
{% if extension.review_activity.all %} <span>{{ stats.last_activity.date_created|naturaltime_compact }}</span>
<a href="{{ extension.get_review_url }}#activity-{{ extension.review_activity.all.last.id }}" class="ml-3">
<span>{{ extension.review_activity.all.last.date_created|naturaltime_compact }}</span>
</a> </a>
{% endif %}
{% include "files/components/scan_details_flag.html" with suspicious_files=extension.suspicious_files %} {% include "files/components/scan_details_flag.html" with suspicious_files=extension.suspicious_files %}
</td> </td>
<td> <td>
<a href="{{ extension.get_review_url }}" class="text-decoration-none"> <a href="{{ extension.get_review_url }}" class="text-decoration-none">
{% include "common/components/status.html" with object=extension class="d-block" %} {% with last_type=stats.last_type_display|default:"Awaiting Review" %}
<div class="d-block badge badge-status-{{ last_type|slugify }}">
<i class="i-eye"></i>
<span>{{ last_type }}</span>
</div>
{% endwith %}
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -101,8 +101,7 @@
{% for activity in extension.review_activity.all %} {% for activity in extension.review_activity.all %}
<li id="activity-{{ activity.id }}"> <li id="activity-{{ activity.id }}">
{# All activities except comments. #} {% if activity.type in status_change_types %}
{% if activity.type != 'COM' %}
<div class="activity-item activity-status-change activity-status-{{ activity.get_type_display|slugify }}"> <div class="activity-item activity-status-change activity-status-{{ activity.get_type_display|slugify }}">
<i class="activity-icon i-activity-{{ activity.get_type_display|slugify }}"></i> <i class="activity-icon i-activity-{{ activity.get_type_display|slugify }}"></i>

View File

@ -34,12 +34,10 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for extension in object_list %} {% for stats in object_list %}
{% if user.is_moderator %} {% with extension=stats.extension %}
{% include 'reviewers/components/review_list_item.html' %} {% include 'reviewers/components/review_list_item.html' with extension=extension stats=stats %}
{% elif extension.status_slug == 'awaiting-review' %} {% endwith %}
{% include 'reviewers/components/review_list_item.html' %}
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -3,13 +3,21 @@ from django.shortcuts import reverse
from common.tests.factories.extensions import create_version from common.tests.factories.extensions import create_version
from files.models import File from files.models import File
from reviewers.models import ApprovalActivity
class CommentsViewTest(TestCase): class CommentsViewTest(TestCase):
fixtures = ['licenses'] fixtures = ['licenses']
def setUp(self): def setUp(self):
self.default_version = create_version(file__status=File.STATUSES.AWAITING_REVIEW) version = create_version(file__status=File.STATUSES.AWAITING_REVIEW)
self.default_version = version
ApprovalActivity(
type=ApprovalActivity.ActivityType.COMMENT,
user=version.file.user,
extension=version.extension,
message='test comment',
).save()
# List of extensions under review does not require authentication # List of extensions under review does not require authentication
def test_list_visibility(self): def test_list_visibility(self):

View File

@ -13,15 +13,51 @@ from reviewers.models import ApprovalActivity
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
STATUS_CHANGE_TYPES = [
ApprovalActivity.ActivityType.APPROVED,
ApprovalActivity.ActivityType.AWAITING_CHANGES,
ApprovalActivity.ActivityType.AWAITING_REVIEW,
]
class ApprovalQueueView(ListView): class ApprovalQueueView(ListView):
model = Extension model = Extension
paginate_by = 100 paginate_by = 100
def get_queryset(self): def get_queryset(self):
return Extension.objects.exclude(status=Extension.STATUSES.APPROVED).order_by( qs = (
'-date_created' ApprovalActivity.objects.prefetch_related(
'extension',
'extension__authors',
'extension__versions',
'extension__versions__file',
'extension__versions__file__validation',
)
.order_by('-date_created')
.all()
) )
by_extension = {}
result = []
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 result also ordered by item.date_created
stats = {
'count': 0,
'extension': extension,
'last_activity': None,
'last_type_display': None,
}
by_extension[extension] = stats
result.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
return result
template_name = 'reviewers/extensions_review_list.html' template_name = 'reviewers/extensions_review_list.html'
@ -36,33 +72,31 @@ class ExtensionsApprovalDetailView(DetailView):
ctx['pending_previews'] = self.object.preview_set.exclude( ctx['pending_previews'] = self.object.preview_set.exclude(
file__status=File.STATUSES.APPROVED file__status=File.STATUSES.APPROVED
) )
ctx['status_change_types'] = STATUS_CHANGE_TYPES
if self.request.user.is_authenticated: if self.request.user.is_authenticated:
form = ctx['comment_form'] = CommentForm() form = ctx['comment_form'] = CommentForm()
# Remove 'Approved' status from dropdown it not moderator # anyone can comment
filtered_activity_types = ApprovalActivity.ActivityType.choices filtered_activity_types = {ApprovalActivity.ActivityType.COMMENT}
user = self.request.user user = self.request.user
if not (user.is_moderator or user.is_superuser): if self.object.has_maintainer(user):
filtered_activity_types = [ filtered_activity_types.add(ApprovalActivity.ActivityType.AWAITING_REVIEW)
t if user.is_moderator or user.is_superuser:
for t in ApprovalActivity.ActivityType.choices filtered_activity_types.update(
if t[0] [
not in [
ApprovalActivity.ActivityType.APPROVED, ApprovalActivity.ActivityType.APPROVED,
ApprovalActivity.ActivityType.AWAITING_CHANGES, ApprovalActivity.ActivityType.AWAITING_CHANGES,
] ]
] )
if not self.object.has_maintainer(user): choices = list(
# Other accounts can only comment filter(
filtered_activity_types = [ lambda c: c[0] in filtered_activity_types, ApprovalActivity.ActivityType.choices
t )
for t in ApprovalActivity.ActivityType.choices )
if t[0] == ApprovalActivity.ActivityType.COMMENT form.fields['type'].choices = choices
] form.fields['type'].widget.choices = choices
form.fields['type'].choices = filtered_activity_types if len(choices) == 1:
form.fields['type'].widget.choices = filtered_activity_types form.fields['type'].widget = django.forms.HiddenInput()
if len(filtered_activity_types) == 1:
form.fields['type'].widget = django.forms.HiddenInput()
return ctx return ctx