Converted apps review queue to use ES (bug 746764)
This commit is contained in:
Родитель
6cf43197e9
Коммит
f802e8c358
|
@ -0,0 +1,68 @@
|
|||
[
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "addons.devicetype",
|
||||
"fields": {
|
||||
"name": 2425886,
|
||||
"class_name": "desktop",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2527136,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"id": 2425886,
|
||||
"localized_string": "Desktop",
|
||||
"localized_string_clean": null,
|
||||
"locale": "en-US",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "addons.devicetype",
|
||||
"fields": {
|
||||
"name": 2425887,
|
||||
"class_name": "mobile",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2527137,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"id": 2425887,
|
||||
"localized_string": "Mobile",
|
||||
"localized_string_clean": null,
|
||||
"locale": "en-US",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 3,
|
||||
"model": "addons.devicetype",
|
||||
"fields": {
|
||||
"name": 2425888,
|
||||
"class_name": "tablet",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2527138,
|
||||
"model": "translations.translation",
|
||||
"fields": {
|
||||
"id": 2425888,
|
||||
"localized_string": "Tablet",
|
||||
"localized_string_clean": null,
|
||||
"locale": "en-US",
|
||||
"created": "2012-05-16 22:36:00",
|
||||
"modified": "2012-05-16 22:36:00"
|
||||
}
|
||||
}
|
||||
]
|
|
@ -342,66 +342,6 @@ h1 .num {
|
|||
}
|
||||
}
|
||||
|
||||
.device-list {
|
||||
margin-top: 10px;
|
||||
h4, ul {
|
||||
display: inline-block;
|
||||
line-height: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
ul {
|
||||
color: @medium-gray;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
height: 23px;
|
||||
position: relative;
|
||||
text-indent: -99999px;
|
||||
width: 26px;
|
||||
background: url(../../img/mkt/icons/device_icons.png) no-repeat 0 0;
|
||||
&.mobile.unavailable {
|
||||
background-position: 0 -23px;
|
||||
}
|
||||
&.tablet {
|
||||
background-position: 0 -46px;
|
||||
&.unavailable {
|
||||
background-position: 0 -69px;
|
||||
}
|
||||
}
|
||||
&.desktop {
|
||||
background-position: 0 -92px;
|
||||
&.unavailable {
|
||||
background-position: 0 -115px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listing .device-list {
|
||||
text-align: center;
|
||||
h4, ul {
|
||||
display: block;
|
||||
}
|
||||
h4 {
|
||||
color: @gray;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
ul {
|
||||
color: @medium-gray;
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.html-rtl .device-list {
|
||||
li {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.support ul {
|
||||
margin-top: 47px;
|
||||
list-style-type: none;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
@import 'lib';
|
||||
|
||||
.device-list {
|
||||
margin-top: 10px;
|
||||
h4, ul {
|
||||
display: inline-block;
|
||||
line-height: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
ul {
|
||||
color: @medium-gray;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
height: 23px;
|
||||
position: relative;
|
||||
text-indent: -99999px;
|
||||
width: 26px;
|
||||
background: url(../../img/mkt/icons/device_icons.png) no-repeat 0 0;
|
||||
&.mobile.unavailable {
|
||||
background-position: 0 -23px;
|
||||
}
|
||||
&.tablet {
|
||||
background-position: 0 -46px;
|
||||
&.unavailable {
|
||||
background-position: 0 -69px;
|
||||
}
|
||||
}
|
||||
&.desktop {
|
||||
background-position: 0 -92px;
|
||||
&.unavailable {
|
||||
background-position: 0 -115px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listing .device-list {
|
||||
text-align: center;
|
||||
h4, ul {
|
||||
display: block;
|
||||
}
|
||||
h4 {
|
||||
color: @gray;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
ul {
|
||||
color: @medium-gray;
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.html-rtl .device-list {
|
||||
li {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ CSS = {
|
|||
|
||||
# Popups, Modals, Tooltips.
|
||||
'css/devreg/devhub-popups.less',
|
||||
'css/mkt/device.less',
|
||||
'css/devreg/tooltips.less',
|
||||
|
||||
# L10n menu ("Localize for ...").
|
||||
|
@ -81,6 +82,7 @@ CSS = {
|
|||
'css/mkt/buttons.less',
|
||||
'css/mkt/detail.less',
|
||||
'css/mkt/ratings.less',
|
||||
'css/mkt/device.less',
|
||||
'css/mkt/slider.less',
|
||||
'css/mkt/promo-grid.less',
|
||||
'css/mkt/overlay.less',
|
||||
|
|
|
@ -18,45 +18,47 @@
|
|||
<thead>
|
||||
<tr class="listing-header">
|
||||
<th> </th>
|
||||
{% for column in table.columns %}
|
||||
{% if column.is_ordered_reverse %}
|
||||
{% set cls, sprite = 'ordered', 'desc' %}
|
||||
{% elif column.is_ordered_straight %}
|
||||
{% set cls, sprite = 'ordered', 'asc' %}
|
||||
{% else %}
|
||||
{% set cls, sprite = '', 'both' %}
|
||||
{% endif %}
|
||||
<th class="{{ cls }}">
|
||||
{% if column.sortable %}
|
||||
<a href="{{ request.get_full_path()|urlparams(sort=column.name_toggled) }}"
|
||||
class="sort-icon ed-sprite-sort-{{ sprite }}">
|
||||
{{ column }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ column }}
|
||||
{% endif %}
|
||||
</th>
|
||||
{% endfor %}
|
||||
<th>{{ _('App') }}</th>
|
||||
<th>{{ _('Flags') }}</th>
|
||||
<th>{{ _('Waiting Time') }}</th>
|
||||
<th>{{ _('Devices') }}</th>
|
||||
<th>{{ _('Payments') }}</th>
|
||||
<th>{{ _('Abuse Reports') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in page.object_list %}
|
||||
<tr data-addon="{{ row.data.id }}" class="addon-row"
|
||||
id="addon-{{ row.data.id }}">
|
||||
{% for addon in pager.object_list %}
|
||||
<tr data-addon="{{ addon.id }}" class="addon-row" id="addon-{{ addon.id }}">
|
||||
<td><div class="addon-locked"></div></td>
|
||||
{% for value in row %}
|
||||
<td>{{ value|xssafe }}</td>
|
||||
{% endfor %}
|
||||
<td><a href="{{ url('reviewers.apps.review', addon.app_slug)|urlparams(num=loop.index) }}">{{ addon.name|xssafe }}</a></td>
|
||||
<td>{# Flags #}
|
||||
{% if addon.admin_review %}
|
||||
<div class="app-icon ed-sprite-admin-review" title="{{ _('Admin Review') }}"></div>
|
||||
{% endif %}
|
||||
{% if addon.current_version.has_info_request %}
|
||||
<div class="app-icon ed-sprite-info" title="{{ _('More Information Requested') }}"></div>
|
||||
{% endif %}
|
||||
{% if addon.current_version.has_editor_comment %}
|
||||
<div class="app-icon ed-sprite-editor" title="{{ _('Contains Editor Comment') }}"></div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ addon.created|timesince }}</td>
|
||||
<td>{{ device_list(addon) }}</td>
|
||||
<td>{{ amo.ADDON_PREMIUM_TYPES[addon.premium_type] }}</td>
|
||||
<td>{{ addon.abuse_reports.count() }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if page.paginator.count == 0 %}
|
||||
|
||||
{% if pager.paginator.count == 0 %}
|
||||
<div class="no-results">
|
||||
{{ _('There are currently no items of this type to review.') }}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ pager|impala_paginator }}
|
||||
{% endif %}
|
||||
{{ page|impala_paginator }}
|
||||
|
||||
</section>
|
||||
|
||||
<p id="helpfulLinks">
|
||||
|
|
|
@ -5,18 +5,19 @@ import time
|
|||
from django.core import mail
|
||||
from django.conf import settings
|
||||
|
||||
from elasticutils import S
|
||||
import mock
|
||||
from nose.tools import eq_, ok_
|
||||
from pyquery import PyQuery as pq
|
||||
|
||||
import amo
|
||||
from abuse.models import AbuseReport
|
||||
from access.models import Group, GroupUser
|
||||
from addons.models import AddonUser
|
||||
from addons.models import AddonDeviceType, AddonUser, DeviceType
|
||||
from amo.tests import app_factory, check_links
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import urlparams
|
||||
from devhub.models import AppLog
|
||||
from editors.forms import MOTDForm
|
||||
from editors.models import CannedResponse
|
||||
from editors.tests.test_views import EditorTest
|
||||
from users.models import UserProfile
|
||||
|
@ -30,12 +31,12 @@ class AppReviewerTest(object):
|
|||
def setUp(self):
|
||||
self.login_as_editor()
|
||||
|
||||
def test_403_for_non_editor(self):
|
||||
def test_403_for_non_editor(self, *args, **kwargs):
|
||||
assert self.client.login(username='regular@mozilla.com',
|
||||
password='password')
|
||||
eq_(self.client.head(self.url).status_code, 403)
|
||||
|
||||
def test_403_for_anonymous(self):
|
||||
def test_403_for_anonymous(self, *args, **kwargs):
|
||||
self.client.logout()
|
||||
eq_(self.client.head(self.url).status_code, 403)
|
||||
|
||||
|
@ -99,20 +100,34 @@ class TestReviewersHome(EditorTest):
|
|||
|
||||
|
||||
class TestAppQueue(AppReviewerTest, EditorTest):
|
||||
fixtures = ['base/devicetypes', 'base/users']
|
||||
|
||||
def setUp(self):
|
||||
self.login_as_editor()
|
||||
|
||||
now = datetime.datetime.now()
|
||||
days_ago = lambda n: now - datetime.timedelta(days=n)
|
||||
|
||||
self.apps = [app_factory(name='XXX',
|
||||
status=amo.WEBAPPS_UNREVIEWED_STATUS),
|
||||
app_factory(name='YYY',
|
||||
status=amo.WEBAPPS_UNREVIEWED_STATUS)]
|
||||
self.apps[0].update(created=days_ago(2))
|
||||
self.apps[1].update(created=days_ago(1))
|
||||
|
||||
self.login_as_editor()
|
||||
self.url = reverse('reviewers.apps.queue_pending')
|
||||
|
||||
# Mock S(...).values() so we don't touch ES.
|
||||
patcher = mock.patch.object(S, 'values')
|
||||
self.s_mock = patcher.start()
|
||||
self.s_mock.return_value = [a.id for a in self.apps]
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def review_url(self, app, num):
|
||||
return urlparams(reverse('reviewers.apps.review', args=[app.app_slug]),
|
||||
num=num)
|
||||
|
||||
def test_restricted_results(self):
|
||||
def test_template_links(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
links = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(2) a')
|
||||
|
@ -123,21 +138,72 @@ class TestAppQueue(AppReviewerTest, EditorTest):
|
|||
]
|
||||
check_links(expected, links, verify=False)
|
||||
|
||||
def test_flag_super_review(self):
|
||||
self.apps[0].update(admin_review=True)
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(3)')
|
||||
flags = tds('div.ed-sprite-admin-review')
|
||||
eq_(flags.length, 1)
|
||||
|
||||
def test_flag_info(self):
|
||||
self.apps[0].current_version.update(has_info_request=True)
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(3)')
|
||||
flags = tds('div.ed-sprite-info')
|
||||
eq_(flags.length, 1)
|
||||
|
||||
def test_flag_comment(self):
|
||||
self.apps[0].current_version.update(has_editor_comment=True)
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(3)')
|
||||
flags = tds('div.ed-sprite-editor')
|
||||
eq_(flags.length, 1)
|
||||
|
||||
def test_devices(self):
|
||||
AddonDeviceType.objects.create(
|
||||
addon=self.apps[0], device_type=DeviceType.objects.get(pk=1))
|
||||
AddonDeviceType.objects.create(
|
||||
addon=self.apps[0], device_type=DeviceType.objects.get(pk=2))
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(5)')
|
||||
eq_(tds('ul li:not(.unavailable)').length, 2)
|
||||
|
||||
def test_payments(self):
|
||||
self.apps[0].update(premium_type=amo.ADDON_PREMIUM)
|
||||
self.apps[1].update(premium_type=amo.ADDON_FREE_INAPP)
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(6)')
|
||||
eq_(tds.eq(0).text(), amo.ADDON_PREMIUM_TYPES[amo.ADDON_PREMIUM])
|
||||
eq_(tds.eq(1).text(), amo.ADDON_PREMIUM_TYPES[amo.ADDON_FREE_INAPP])
|
||||
|
||||
def test_abuse(self):
|
||||
AbuseReport.objects.create(addon=self.apps[0], message='!@#$')
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
tds = pq(r.content)('#addon-queue tbody')('tr td:nth-of-type(7)')
|
||||
eq_(tds.eq(0).text(), '1')
|
||||
|
||||
def test_invalid_page(self):
|
||||
r = self.client.get(self.url, {'page': 999})
|
||||
eq_(r.status_code, 200)
|
||||
eq_(r.context['page'].number, 1)
|
||||
eq_(r.context['pager'].number, 1)
|
||||
|
||||
def test_queue_count(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
eq_(pq(r.content)('.tabnav li a:eq(0)').text(), u'Apps (2)')
|
||||
|
||||
def test_sort(self):
|
||||
r = self.client.get(self.url, {'sort': '-name'})
|
||||
eq_(r.status_code, 200)
|
||||
eq_(pq(r.content)('#addon-queue tbody tr').eq(0).attr('data-addon'),
|
||||
str(self.apps[1].id))
|
||||
# TODO(robhudson): Add sorting back in.
|
||||
#def test_sort(self):
|
||||
# r = self.client.get(self.url, {'sort': '-name'})
|
||||
# eq_(r.status_code, 200)
|
||||
# eq_(pq(r.content)('#addon-queue tbody tr').eq(0).attr('data-addon'),
|
||||
# str(self.apps[1].id))
|
||||
|
||||
def test_redirect_to_review(self):
|
||||
r = self.client.get(self.url, {'num': 2})
|
||||
|
@ -159,7 +225,9 @@ class TestReviewApp(AppReviewerTest, EditorTest):
|
|||
|
||||
def post(self, data):
|
||||
r = self.client.post(self.url, data)
|
||||
self.assertRedirects(r, reverse('reviewers.apps.queue_pending'))
|
||||
# Purposefully not using assertRedirects to avoid having to mock ES.
|
||||
eq_(r.status_code, 302)
|
||||
ok_(reverse('reviewers.apps.queue_pending') in r['Location'])
|
||||
|
||||
@mock.patch.object(settings, 'DEBUG', False)
|
||||
def test_cannot_review_my_app(self):
|
||||
|
|
|
@ -4,17 +4,12 @@ from django.conf import settings
|
|||
from django.utils.datastructures import SortedDict
|
||||
|
||||
import commonware.log
|
||||
import django_tables as tables
|
||||
import jinja2
|
||||
from tower import ugettext as _, ugettext_lazy as _lazy
|
||||
from tower import ugettext_lazy as _lazy
|
||||
|
||||
import amo
|
||||
from amo.helpers import absolutify, timesince
|
||||
from amo.helpers import absolutify
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import send_mail_jinja
|
||||
from editors.helpers import ItemStateTable
|
||||
|
||||
from mkt.webapps.models import Webapp
|
||||
|
||||
|
||||
log = commonware.log.getLogger('z.mailer')
|
||||
|
@ -29,52 +24,6 @@ def send_mail(subject, template, context, emails, perm_setting=None):
|
|||
headers={'Reply-To': settings.MKT_REVIEWERS_EMAIL})
|
||||
|
||||
|
||||
class WebappQueueTable(tables.ModelTable, ItemStateTable):
|
||||
name = tables.Column(verbose_name=_lazy(u'App'))
|
||||
flags = tables.Column(verbose_name=_lazy(u'Flags'), sortable=False)
|
||||
created = tables.Column(verbose_name=_lazy(u'Waiting Time'))
|
||||
abuse_reports__count = tables.Column(verbose_name=_lazy(u'Abuse Reports'))
|
||||
|
||||
def render_name(self, row):
|
||||
url = '%s?num=%s' % (self.review_url(row), self.item_number)
|
||||
self.increment_item()
|
||||
return u'<a href="%s">%s</a>' % (url, jinja2.escape(row.name))
|
||||
|
||||
def render_flags(self, row):
|
||||
o = []
|
||||
|
||||
if row.admin_review:
|
||||
o.append(u'<div class="app-icon ed-sprite-admin-review" '
|
||||
u'title="%s"></div>' % _('Admin Review'))
|
||||
|
||||
return ''.join(o)
|
||||
|
||||
def render_created(self, row):
|
||||
return timesince(row.created)
|
||||
|
||||
def render_abuse_reports__count(self, row):
|
||||
url = reverse('editors.abuse_reports', args=[row.slug])
|
||||
return u'<a href="%s">%s</a>' % (jinja2.escape(url),
|
||||
row.abuse_reports__count)
|
||||
|
||||
@classmethod
|
||||
def translate_sort_cols(cls, colname):
|
||||
return colname
|
||||
|
||||
@classmethod
|
||||
def default_order_by(cls):
|
||||
return 'created'
|
||||
|
||||
@classmethod
|
||||
def review_url(cls, row):
|
||||
return reverse('reviewers.apps.review', args=[row.app_slug])
|
||||
|
||||
class Meta:
|
||||
sortable = True
|
||||
model = Webapp
|
||||
columns = ['name', 'flags', 'created', 'abuse_reports__count']
|
||||
|
||||
|
||||
class ReviewBase:
|
||||
|
||||
def __init__(self, request, addon, version, review_type):
|
||||
|
@ -227,7 +176,7 @@ class ReviewApp(ReviewBase):
|
|||
self.log_action(amo.LOG.COMMENT_VERSION)
|
||||
|
||||
|
||||
class ReviewHelper:
|
||||
class ReviewHelper(object):
|
||||
"""
|
||||
A class that builds enough to render the form back to the user and
|
||||
process off to the correct handler.
|
||||
|
|
|
@ -2,20 +2,23 @@ import datetime
|
|||
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
from django.db.models import Count, Q
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import redirect
|
||||
|
||||
from elasticutils import S
|
||||
import jingo
|
||||
from tower import ugettext as _
|
||||
|
||||
from access import acl
|
||||
import amo
|
||||
from amo import messages
|
||||
from amo.utils import urlparams
|
||||
from addons.decorators import addon_view
|
||||
from addons.models import Version
|
||||
from amo.decorators import permission_required
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import paginate
|
||||
from apps.search.views import name_query
|
||||
from editors.forms import MOTDForm
|
||||
from editors.models import EditorSubscription
|
||||
from editors.views import reviewer_required
|
||||
|
@ -24,10 +27,14 @@ from zadmin.models import get_config, set_config
|
|||
|
||||
from mkt.developers.models import ActivityLog
|
||||
from mkt.webapps.models import Webapp
|
||||
from . import forms, utils
|
||||
|
||||
from . import forms
|
||||
from .models import AppCannedResponse
|
||||
|
||||
|
||||
QUEUE_PER_PAGE = 100
|
||||
|
||||
|
||||
@reviewer_required
|
||||
def home(request):
|
||||
durations = (('new', _('New Apps (Under 5 days)')),
|
||||
|
@ -89,42 +96,6 @@ def _progress():
|
|||
return (progress, percentage)
|
||||
|
||||
|
||||
def _queue(request, TableObj, tab, qs=None):
|
||||
if qs is None:
|
||||
qs = TableObj.Meta.model.objects.all()
|
||||
review_num = request.GET.get('num', None)
|
||||
if review_num:
|
||||
try:
|
||||
review_num = int(review_num)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Force a limit query for efficiency:
|
||||
start = review_num - 1
|
||||
row = qs[start: start + 1][0]
|
||||
return redirect('%s?num=%s' % (
|
||||
TableObj.review_url(row),
|
||||
review_num))
|
||||
except IndexError:
|
||||
pass
|
||||
order_by = request.GET.get('sort', TableObj.default_order_by())
|
||||
order_by = TableObj.translate_sort_cols(order_by)
|
||||
table = TableObj(data=qs, order_by=order_by)
|
||||
default = 100
|
||||
per_page = request.GET.get('per_page', default)
|
||||
try:
|
||||
per_page = int(per_page)
|
||||
except ValueError:
|
||||
per_page = default
|
||||
if per_page <= 0 or per_page > 200:
|
||||
per_page = default
|
||||
page = paginate(request, table.rows, per_page=per_page)
|
||||
table.set_page(page)
|
||||
return jingo.render(request, 'reviewers/queue.html',
|
||||
context(table=table, page=page, tab=tab))
|
||||
|
||||
|
||||
def context(**kw):
|
||||
ctx = dict(motd=get_config('mkt_reviewers_motd'),
|
||||
queue_counts=queue_counts())
|
||||
|
@ -223,8 +194,37 @@ def app_review(request, addon):
|
|||
|
||||
@permission_required('Apps', 'Review')
|
||||
def queue_apps(request):
|
||||
qs = Webapp.objects.pending().annotate(Count('abuse_reports'))
|
||||
return _queue(request, utils.WebappQueueTable, 'pending', qs=qs)
|
||||
sqs = S(Webapp).filter(type=amo.ADDON_WEBAPP, status=amo.STATUS_PENDING,
|
||||
is_disabled=False)
|
||||
|
||||
q = request.GET.get('q', None)
|
||||
if q:
|
||||
sqs = sqs.query(or_=name_query(q))
|
||||
ids = sqs.values()
|
||||
qs = Webapp.objects.filter(id__in=ids).order_by('created')
|
||||
|
||||
review_num = request.GET.get('num', None)
|
||||
if review_num:
|
||||
try:
|
||||
review_num = int(review_num)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
# Force a limit query for efficiency:
|
||||
start = review_num - 1
|
||||
row = qs[start:start + 1][0]
|
||||
return redirect(
|
||||
urlparams(reverse('reviewers.apps.review',
|
||||
args=[row.app_slug]),
|
||||
num=review_num))
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
per_page = request.GET.get('per_page', QUEUE_PER_PAGE)
|
||||
pager = paginate(request, qs, per_page)
|
||||
|
||||
return jingo.render(request, 'reviewers/queue.html', {'pager': pager})
|
||||
|
||||
|
||||
@permission_required('Apps', 'Review')
|
||||
|
|
Загрузка…
Ссылка в новой задаче