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:
Родитель
ddfd5e02c7
Коммит
fe1a9e3ef9
|
@ -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() {
|
||||
|
|
Загрузка…
Ссылка в новой задаче