Add a filter to the review queue for due date reasons (#22558)

* Add a filter to the review queue for due date reasons

* use cleaned values of due_date_reasons for safety
This commit is contained in:
Andrew Williamson 2024-08-09 17:01:28 +01:00 коммит произвёл GitHub
Родитель ddfd5e02c7
Коммит fe1a9e3ef9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 134 добавлений и 14 удалений

Просмотреть файл

@ -22,7 +22,12 @@ from olympia.constants.abuse import DECISION_ACTIONS
from olympia.constants.reviewers import REVIEWER_DELAYED_REJECTION_PERIOD_DAYS_DEFAULT
from olympia.ratings.models import Rating
from olympia.ratings.permissions import user_can_delete_rating
from olympia.reviewers.models import NeedsHumanReview, ReviewActionReason, Whiteboard
from olympia.reviewers.models import (
VIEW_QUEUE_FLAGS,
NeedsHumanReview,
ReviewActionReason,
Whiteboard,
)
from olympia.versions.models import Version
@ -582,3 +587,18 @@ class BaseRatingFlagFormSet(BaseModelFormSet):
RatingFlagFormSet = modelformset_factory(
Rating, extra=0, form=ModerateRatingFlagForm, formset=BaseRatingFlagFormSet
)
class ReviewQueueFilter(forms.Form):
due_date_reasons = forms.MultipleChoiceField(
choices=(), widget=forms.CheckboxSelectMultiple, required=False
)
def __init__(self, data, *args, **kw):
due_date_reasons = list(Version.objects.get_due_date_reason_q_objects().keys())
kw['initial'] = {'due_date_reasons': due_date_reasons}
super().__init__(data, *args, **kw)
labels = {reason: label for reason, label in VIEW_QUEUE_FLAGS}
self.fields['due_date_reasons'].choices = [
(reason, labels.get(reason, reason)) for reason in due_date_reasons
]

Просмотреть файл

@ -69,23 +69,23 @@ VIEW_QUEUE_FLAGS = (
# See VersionManager.get_due_date_reason_q_objects for the names
(
'needs_human_review_from_cinder',
'Abuse report forwarded from Cinder present',
'Abuse report forwarded from Cinder',
),
(
'needs_human_review_from_abuse',
'Abuse report present',
'Abuse report',
),
(
'needs_human_review_from_appeal',
'Appeal on decision present',
'Appeal on decision',
),
(
'needs_human_review_other',
'Other NeedsHumanReview flag set',
'Other NeedsHumanReview flag',
),
(
'is_pre_review_version',
'Version(s) awaiting pre-approval review',
'Version awaiting pre-approval review',
),
(
'has_developer_reply',

Просмотреть файл

@ -78,6 +78,13 @@
</form>
</div>
{% else %}
<div id="addon-queue-filter-form">
<button class="show-hide-toggle">Show/Hide Filter Selections</button>
<form action="" method="get">
{{ filter_form }}
<input type="submit" value="Update Filters">
</form>
</div>
<table id="addon-queue" class="data-grid" data-url="{{ url('reviewers.queue_viewing') }}">
<thead>
<tr class="listing-header">

Просмотреть файл

@ -1717,12 +1717,12 @@ class TestGetFlags(TestCase):
for attribute, title in (
(
'needs_human_review_from_cinder',
'Abuse report forwarded from Cinder present',
'Abuse report forwarded from Cinder',
),
('needs_human_review_from_abuse', 'Abuse report present'),
('needs_human_review_from_appeal', 'Appeal on decision present'),
('needs_human_review_other', 'Other NeedsHumanReview flag set'),
('is_pre_review_version', 'Version(s) awaiting pre-approval review'),
('needs_human_review_from_abuse', 'Abuse report'),
('needs_human_review_from_appeal', 'Appeal on decision'),
('needs_human_review_other', 'Other NeedsHumanReview flag'),
('is_pre_review_version', 'Version awaiting pre-approval review'),
('has_developer_reply', 'Outstanding developer reply'),
):
self.addon.needs_human_review_from_abuse = False

Просмотреть файл

@ -83,6 +83,7 @@ from olympia.users.models import UserProfile
from olympia.versions.models import (
ApplicationsVersions,
AppVersion,
VersionManager,
VersionReviewerFlags,
)
from olympia.versions.utils import get_review_due_date
@ -1673,6 +1674,53 @@ class TestExtensionQueue(QueueTest):
self.expected_versions = self.get_expected_versions(self.expected_addons)
self._test_results()
def test_due_date_reason_filter_form(self):
response = self.client.get(self.url)
assert response.status_code == 200
doc = pq(response.content)
# the form is present
assert doc('#addon-queue-filter-form').length
# and all the checkboxes are checked by default if there are not GET params
assert doc(
'#addon-queue-filter-form input[type="checkbox"]:checked'
).length == len(VersionManager.get_due_date_reason_q_objects())
def test_due_date_reason_filtering(self):
self.url += '?due_date_reasons=needs_human_review_from_abuse'
self.expected_addons = self.get_expected_addons_by_names(
['Pending One', 'Nominated Two'],
auto_approve_disabled=True,
)
self.expected_versions = self.get_expected_versions(self.expected_addons)
for _, version in self.expected_versions.items():
NeedsHumanReview.objects.create(
version=version, reason=NeedsHumanReview.REASONS.ABUSE_ADDON_VIOLATION
)
self._test_results()
def test_due_date_reason_with_two_filters(self):
# test with two filters applied
self.url += (
'?due_date_reasons=needs_human_review_from_abuse'
'&due_date_reasons=has_developer_reply'
)
self.expected_addons = self.get_expected_addons_by_names(
['Pending One', 'Nominated One', 'Nominated Two'],
auto_approve_disabled=True,
)
self.expected_versions = self.get_expected_versions(self.expected_addons)
# pending one and nominated one have a report
for _, version in list(self.expected_versions.items())[0:2]:
NeedsHumanReview.objects.create(
version=version, reason=NeedsHumanReview.REASONS.ABUSE_ADDON_VIOLATION
)
# and nominated one and nominated two have a developer reply
for _, version in list(self.expected_versions.items())[1:]:
NeedsHumanReview.objects.create(
version=version, reason=NeedsHumanReview.REASONS.DEVELOPER_REPLY
)
self._test_results()
class TestThemeQueue(QueueTest):
def setUp(self):

Просмотреть файл

@ -1,3 +1,5 @@
import functools
import operator
from collections import OrderedDict
from datetime import date, datetime
from urllib.parse import quote
@ -76,6 +78,7 @@ from olympia.reviewers.forms import (
RatingModerationLogForm,
ReviewForm,
ReviewLogForm,
ReviewQueueFilter,
WhiteboardForm,
)
from olympia.reviewers.models import (
@ -322,6 +325,14 @@ def queue(request, tab):
order_by = TableObj.default_order_by()
if order_by is not None:
params['order_by'] = order_by
filter_form = ReviewQueueFilter(
request.GET if 'due_date_reasons' in request.GET else None
)
if filter_form.is_valid() and (
due_date_reasons := filter_form.cleaned_data['due_date_reasons']
):
filters = [Q(**{reason: True}) for reason in due_date_reasons]
qs = qs.filter(functools.reduce(operator.or_, filters))
table = TableObj(data=qs, **params)
per_page = request.GET.get('per_page', REVIEWS_PER_PAGE)
try:
@ -337,11 +348,12 @@ def queue(request, tab):
request,
'reviewers/queue.html',
context=context(
table=table,
page=page,
tab=tab,
title=TableObj.title,
registry=reviewer_tables_registry,
filter_form=filter_form,
tab=tab,
table=table,
title=TableObj.title,
),
)

Просмотреть файл

@ -189,6 +189,7 @@ class VersionManager(ManagerBase):
.distinct()
)
@classmethod
def get_due_date_reason_q_objects(cls):
"""Class method that returns a dict with all the Q objects needed to determine
if a version should_have_due_date is true as values, and the annotation names

Просмотреть файл

@ -1378,3 +1378,17 @@ table.abuse_reports {
border-left: 0;
}
}
#addon-queue-filter-form {
margin: 1em;
padding: 1em;
background-color: #ecf5fe;
#id_due_date_reasons {
column-count: 3;
}
form {
margin-bottom: 0;
}
}

Просмотреть файл

@ -56,6 +56,24 @@ $(document).ready(function () {
window.location.hash = 'id=' + $('#addon, #persona').attr('data-id');
});
}
if ($('#addon-queue-filter-form').length) {
let filter_form = $('#addon-queue-filter-form form')[0];
$('#addon-queue-filter-form button').click(function () {
if (filter_form.hidden) {
filter_form.hidden = false;
} else {
filter_form.hidden = true;
}
});
if (
$('#addon-queue-filter-form input[type="checkbox"]').length ==
$('#addon-queue-filter-form input[type="checkbox"]:checked').length
) {
filter_form.hidden = true;
}
}
});
function initReviewActions() {