bug 627543, Recent editor activity.

This commit is contained in:
Dave Dash 2011-01-24 16:37:25 -08:00
Родитель a4782421bb
Коммит 22015c47a2
6 изменённых файлов: 89 добавлений и 40 удалений

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

@ -9,7 +9,7 @@ from product_details import firefox_versions, thunderbird_versions
from tower import ugettext_lazy as _
from licenses import license_text
from .log import LOG, LOG_BY_ID, LOG_KEEP, log
from .log import LOG, LOG_BY_ID, LOG_EDITORS, LOG_KEEP, log
from django.utils.translation import trans_real as translation
logger_log = commonware.log.getLogger('z.amo')

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

@ -246,6 +246,24 @@ class CHANGE_ICON:
format = _(u'{addon} icon changed.')
class APPROVE_REVIEW:
id = 40
format = _(u'{review} for {addon} approved.')
editor_format = _(u'{user} approved {review} for {addon}.')
keep = True
editor_event = True
class DELETE_REVIEW:
"""Requires review.id and add-on objects."""
id = 41
format = _(u'{review} for {addon} deleted.')
# TODO(davedash): a {more} link will need to go somewhere
editor_format = _(u'{user} deleted review {0}.')
keep = True
editor_event = True
class CUSTOM_TEXT:
id = 98
format = '{0}'
@ -266,11 +284,12 @@ LOGS = (CREATE_ADDON, EDIT_PROPERTIES, EDIT_DESCRIPTIONS, EDIT_CATEGORIES,
ADD_TO_COLLECTION, REMOVE_FROM_COLLECTION, ADD_REVIEW,
ADD_RECOMMENDED_CATEGORY, REMOVE_RECOMMENDED_CATEGORY, ADD_RECOMMENDED,
REMOVE_RECOMMENDED, ADD_APPVERSION, CUSTOM_TEXT, CUSTOM_HTML,
CHANGE_USER_WITH_ROLE, CHANGE_LICENSE, CHANGE_POLICY, CHANGE_ICON
)
CHANGE_USER_WITH_ROLE, CHANGE_LICENSE, CHANGE_POLICY, CHANGE_ICON,
APPROVE_REVIEW, DELETE_REVIEW,)
LOG_BY_ID = dict((l.id, l) for l in LOGS)
LOG = AttributeDict((l.__name__, l) for l in LOGS)
LOG_KEEP = [l.id for l in LOGS if hasattr(l, 'keep')]
LOG_EDITORS = [l.id for l in LOGS if hasattr(l, 'editor_event')]
def log(action, *args, **kw):
@ -292,6 +311,7 @@ def log(action, *args, **kw):
al = ActivityLog(user=user, action=action.id)
al.arguments = args
al.save()
if 'created' in kw:
al.created = kw['created']
# Double save necessary since django resets the created date on save.

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

@ -17,6 +17,7 @@ from bandwagon.models import Collection
from reviews.models import Review
from tags.models import Tag
from translations.fields import TranslatedField
from users.helpers import user_link
from users.models import UserProfile
from versions.models import Version
@ -114,6 +115,9 @@ class ActivityLogManager(amo.models.ManagerBase):
.values_list('activity_log', flat=True))
return self.filter(pk__in=list(vals))
def editor_events(self):
return self.filter(action__in=amo.LOG_EDITORS)
class SafeFormatter(string.Formatter):
"""A replacement for str.format that escapes interpolated values."""
@ -153,7 +157,7 @@ class ActivityLog(amo.models.ModelBase):
for item in d:
# item has only one element.
model_name, pk = item.items()[0]
if model_name == 'str':
if model_name in ('str', 'int'):
objs.append(pk)
else:
(app_label, model_name) = model_name.split('.')
@ -179,6 +183,8 @@ class ActivityLog(amo.models.ModelBase):
for arg in args:
if isinstance(arg, basestring):
serialize_me.append({'str': arg})
elif isinstance(arg, (int, long)):
serialize_me.append({'int': arg})
elif isinstance(arg, tuple):
# Instead of passing an addon instance you can pass a tuple:
# (Addon, 3) for Addon with pk=3
@ -189,8 +195,12 @@ class ActivityLog(amo.models.ModelBase):
self._arguments = json.dumps(serialize_me)
# TODO(davedash): Support other types.
def to_string(self, type='default'):
def to_string(self, type=None):
log_type = amo.LOG_BY_ID[self.action]
if type and hasattr(log_type, '%s_format' % type):
format = getattr(log_type, '%s_format' % type)
else:
format = log_type.format
# We need to copy arguments so we can remove elements from it
# while we loop over self.arguments.
@ -225,10 +235,13 @@ class ActivityLog(amo.models.ModelBase):
arg.get_url_path(), arg.tag_text)
else:
tag = self.f('{0}', arg.tag_text)
user = user_link(self.user)
try:
kw = dict(addon=addon, review=review, version=version,
collection=collection, tag=tag)
return self.f(log_type.format, *arguments, **kw)
collection=collection, tag=tag, user=user)
return self.f(format, *arguments, **kw)
except (AttributeError, KeyError, IndexError):
log.warning('%d contains garbage data' % (self.id or 0))
return 'Something magical happened.'

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

@ -48,7 +48,7 @@
<div class="editor-stats-table">
<div>
<table>
{# TODO(dd): ActivityLog stuff #}
{# TODO(gkoberger): MOAR Stats #}
{% for i in range(5) %}
<tr>
<td>John Smith</td>
@ -62,33 +62,17 @@
</div>
</div>
{# TODO(dd): ActivityLog stuff (this is a placeholder) #}
<div class="listing results">
<div class="results-inner">
{% for item in eventlog %}
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
</div>
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
</div>
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
</div>
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
</div>
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
</div>
<div class="row">
<a href="/en-US/firefox/addon/test_addon2/">Kris Maglione</a> deleted review <a href="#">263753</a>.
<time title="Jan 6, 2011 3:35:43 PM" datetime="2011-01-06T23:35:43Z">1 week, 6 days ago</time>
{{ item.to_string('editor') }}
{% trans ago=item.created|timesince, iso=item.created|isotime,
pretty=item.created|babel_datetime %}
<time datetime="{{ iso }}" title="{{ pretty }}">{{ ago }}</time>
{% endtrans %}
</div>
{% endfor %}
</div>
</div>
</div>

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

@ -1,23 +1,52 @@
# -*- coding: utf8 -*-
import re
from nose.tools import eq_
from pyquery import PyQuery as pq
import test_utils
import amo
from amo.urlresolvers import reverse
from addons.models import Addon
from reviews.models import Review
from users.models import UserProfile
class EditorTest(test_utils.TestCase):
fixtures = ('base/users', 'editors/pending-queue')
def login_as_editor(self):
assert self.client.login(username='editor@mozilla.com',
password='password')
class TestPendingQueue(EditorTest):
fixtures = ['base/users', 'editors/pending-queue']
class TestHome(EditorTest):
"""Test the page at /editors."""
def setUp(self):
self.login_as_editor()
amo.set_user(UserProfile.objects.get(username='editor'))
def make_review(self):
u = UserProfile.objects.create(username='a')
a = Addon.objects.create(name='yermom', type=amo.ADDON_EXTENSION)
return Review.objects.create(user=u, addon=a)
def test_approved_review(self):
review = self.make_review()
amo.log(amo.LOG.APPROVE_REVIEW, review, review.addon)
r = self.client.get(reverse('editors.home'))
doc = pq(r.content)
eq_(doc('.row').eq(0).text().strip().split('.')[0],
'editor approved Review for yermom ')
def test_deleted_review(self):
review = self.make_review()
amo.log(amo.LOG.DELETE_REVIEW, review.id, review.addon)
r = self.client.get(reverse('editors.home'))
doc = pq(r.content)
eq_(doc('.row').eq(0).text().strip().split('.')[0],
'editor deleted review %d' % review.id)
class TestPendingQueue(EditorTest):
def setUp(self):
super(TestPendingQueue, self).setUp()
self.login_as_editor()
@ -31,7 +60,7 @@ class TestPendingQueue(EditorTest):
def test_invalid_page(self):
r = self.client.get(reverse('editors.queue_pending'),
data={'page':999})
data={'page': 999})
eq_(r.status_code, 200)
eq_(r.context['page'].number, 1)

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

@ -2,11 +2,11 @@ import functools
from django import http
from django.shortcuts import redirect
from django.core.paginator import Paginator
import jingo
from access import acl
from amo.decorators import login_required
from devhub.models import ActivityLog
from editors.models import ViewEditorQueue
from editors.helpers import ViewEditorQueueTable
from amo.utils import paginate
@ -14,6 +14,7 @@ from amo.urlresolvers import reverse
from files.models import Approval
from zadmin.models import get_config
def editor_required(func):
"""Requires the user to be logged in as an editor or admin."""
@functools.wraps(func)
@ -28,9 +29,11 @@ def editor_required(func):
@editor_required
def home(request):
data = {'reviews_total': Approval.total_reviews(),
'reviews_monthly': Approval.monthly_reviews(),
'motd': get_config('editors_review_motd')}
data = dict(reviews_total=Approval.total_reviews(),
reviews_monthly=Approval.monthly_reviews(),
motd=get_config('editors_review_motd'),
eventlog=ActivityLog.objects.editor_events()[:6],
)
return jingo.render(request, 'editors/home.html', data)