diff --git a/apps/devhub/templates/devhub/versions/list.html b/apps/devhub/templates/devhub/versions/list.html
index 5985e9688d2..cba5143973d 100644
--- a/apps/devhub/templates/devhub/versions/list.html
+++ b/apps/devhub/templates/devhub/versions/list.html
@@ -50,6 +50,14 @@
{{ _('Current Status') }}
{% endif %}
+ {% if position %}
+
+ {% trans pos=position['pos'], total=position['total'] %}
+ Queue Position: {{ pos }} of {{ total }}
+ {% endtrans %}
+ ·
+
+ {% endif %}
{% if check_addon_ownership(request, addon, dev=True) %}
{% set req = {amo.STATUS_PUBLIC: _('Request Full Review'),
amo.STATUS_LITE: _('Request Preliminary Review')} %}
diff --git a/apps/devhub/tests/test_views.py b/apps/devhub/tests/test_views.py
index a5babd51796..e4a734f5b30 100644
--- a/apps/devhub/tests/test_views.py
+++ b/apps/devhub/tests/test_views.py
@@ -2702,6 +2702,50 @@ def setUp(self):
assert self.client.login(username='del@icio.us', password='password')
+class TestQueuePosition(UploadTest):
+ fixtures = ['base/apps', 'base/users',
+ 'base/addon_3615', 'base/platforms']
+
+ def setUp(self):
+ super(TestQueuePosition, self).setUp()
+
+ self.url = reverse('devhub.versions.add_file',
+ args=[self.addon.slug, self.version.id])
+ self.edit_url = reverse('devhub.versions.edit',
+ args=[self.addon.slug, self.version.id])
+ files = self.version.files.all()[0]
+ files.platform_id = amo.PLATFORM_LINUX.id
+ files.save()
+
+ def test_not_in_queue(self):
+ r = self.client.get(reverse('devhub.versions', args=[self.addon.slug]))
+
+ eq_(self.addon.status, amo.STATUS_PUBLIC)
+ eq_(pq(r.content)('.version-status-actions .dark').length, 0)
+
+ def test_in_queue(self):
+ statuses = [(amo.STATUS_NOMINATED, amo.STATUS_NOMINATED),
+ (amo.STATUS_PUBLIC, amo.STATUS_UNREVIEWED),
+ (amo.STATUS_LITE, amo.STATUS_UNREVIEWED)]
+
+ for addon_status in statuses:
+ self.addon.status = addon_status[0]
+ self.addon.save()
+
+ file = self.addon.latest_version.files.all()[0]
+ file.status = addon_status[1]
+ file.save()
+
+ r = self.client.get(reverse('devhub.versions',
+ args=[self.addon.slug]))
+ doc = pq(r.content)
+
+ span = doc('.version-status-actions .dark')
+
+ eq_(span.length, 1)
+ assert "Queue Position: 1 of 1" in span.text()
+
+
class TestVersionAddFile(UploadTest):
fixtures = ['base/apps', 'base/users',
'base/addon_3615', 'base/platforms']
diff --git a/apps/devhub/views.py b/apps/devhub/views.py
index 79a37331dcd..d548a0920a9 100644
--- a/apps/devhub/views.py
+++ b/apps/devhub/views.py
@@ -40,6 +40,7 @@
from addons.models import Addon, AddonUser
from addons.views import BaseFilter
from devhub.models import ActivityLog, BlogPost, RssKey, SubmitStep
+from editors.helpers import get_position
from files.models import File, FileUpload
from files.utils import parse_addon
from translations.models import delete_translation
@@ -913,9 +914,11 @@ def version_list(request, addon_id, addon):
qs = addon.versions.order_by('-created').transform(Version.transformer)
versions = amo.utils.paginate(request, qs)
new_file_form = forms.NewVersionForm(None, addon=addon)
+
data = {'addon': addon,
'versions': versions,
'new_file_form': new_file_form,
+ 'position': get_position(addon),
'timestamp': int(time.time())}
return jingo.render(request, 'devhub/versions/list.html', data)
diff --git a/apps/editors/helpers.py b/apps/editors/helpers.py
index 2c06db23927..d2c73661df8 100644
--- a/apps/editors/helpers.py
+++ b/apps/editors/helpers.py
@@ -219,6 +219,26 @@ def send_mail(template, subject, emails, context):
use_blacklist=False)
+def get_position(addon):
+ version = addon.latest_version
+
+ if not version:
+ return False
+
+ q = version.current_queue
+ if not q:
+ return False
+
+ mins_query = q.objects.filter(id=addon.id)
+ if mins_query.count() > 0:
+ mins = mins_query[0].waiting_time_min
+ pos = q.objects.having('waiting_time_min >=', mins).count()
+ total = q.objects.count()
+ return dict(mins=mins, pos=pos, total=total)
+
+ return False
+
+
class ReviewHelper:
"""
A class that builds enough to render the form back to the user and
diff --git a/apps/editors/tests/test_views.py b/apps/editors/tests/test_views.py
index 0b7755cf822..6554307c4cd 100644
--- a/apps/editors/tests/test_views.py
+++ b/apps/editors/tests/test_views.py
@@ -20,6 +20,7 @@
from applications.models import Application
from devhub.models import ActivityLog
from editors.models import EditorSubscription, EventLog
+from editors.helpers import get_position
from files.models import Platform, File
import reviews
from reviews.models import Review, ReviewFlag
@@ -430,6 +431,11 @@ def addon_file(self, *args, **kw):
a = create_addon_file(*args, **kw)
self.versions[unicode(a['addon'].name)] = a['version']
+ def get_queue(self, version_name):
+ addon_id = self.versions[version_name].addon.id
+ version = Addon.objects.get(pk=addon_id).latest_version
+ eq_(version.current_queue.objects.filter(id=addon_id).count(), 1)
+
class TestQueueBasics(QueueTest):
@@ -688,6 +694,10 @@ def test_breadcrumbs(self):
eq_(list_items.eq(0).find('a').text(), "Editor Tools")
eq_(list_items.eq(1).text(), "Pending Updates")
+ def test_get_queue(self):
+ self.get_queue(u'Pending One')
+ self.get_queue(u'Pending Two')
+
class TestNominatedQueue(QueueTest):
@@ -743,6 +753,10 @@ def test_queue_count(self):
eq_(doc('.tabnav li a:eq(0)').attr('href'),
reverse('editors.queue_nominated'))
+ def test_get_queue(self):
+ self.get_queue(u'Nominated One')
+ self.get_queue(u'Nominated Two')
+
class TestPreliminaryQueue(QueueTest):
@@ -778,6 +792,10 @@ def test_breadcrumbs(self):
eq_(list_items.eq(0).find('a').text(), "Editor Tools")
eq_(list_items.eq(1).text(), "Preliminary Reviews")
+ def test_get_queue(self):
+ self.get_queue(u'Prelim One')
+ self.get_queue(u'Prelim Two')
+
class TestModeratedQueue(QueueTest):
fixtures = ['base/users', 'base/apps', 'reviews/dev-reply.json']
diff --git a/apps/versions/models.py b/apps/versions/models.py
index 8bc0bef070b..cf73a8e5708 100644
--- a/apps/versions/models.py
+++ b/apps/versions/models.py
@@ -151,6 +151,22 @@ def delete(self):
amo.log(amo.LOG.DELETE_VERSION, self.addon, str(self.version))
super(Version, self).delete()
+ @property
+ def current_queue(self):
+ """Return the current queue, or None if not in a queue."""
+ from editors.models import (ViewPendingQueue, ViewFullReviewQueue,
+ ViewPreliminaryQueue)
+
+ if self.addon.status in [amo.STATUS_NOMINATED,
+ amo.STATUS_LITE_AND_NOMINATED]:
+ return ViewFullReviewQueue
+ elif self.addon.status == amo.STATUS_PUBLIC:
+ return ViewPendingQueue
+ elif self.addon.status in [amo.STATUS_LITE, amo.STATUS_UNREVIEWED]:
+ return ViewPreliminaryQueue
+
+ return None
+
@amo.cached_property(writable=True)
def all_activity(self):
from devhub.models import VersionLog # yucky
diff --git a/media/css/zamboni/developers.css b/media/css/zamboni/developers.css
index 33f9b20f309..45e9ce5d380 100644
--- a/media/css/zamboni/developers.css
+++ b/media/css/zamboni/developers.css
@@ -705,6 +705,9 @@ form .char-count b {
.item-actions a {
color: #aaa;
}
+.item-actions .dark {
+ color: #444;
+}
#dashboard .item:hover .item-actions {
color: #555;
}