bug 622202, event log and log detail page

This commit is contained in:
Dave Dash 2011-01-25 14:52:53 -08:00
Родитель adbe9b0ac8
Коммит 234fe3ca96
11 изменённых файлов: 248 добавлений и 6 удалений

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

@ -310,8 +310,11 @@ def log(action, *args, **kw):
al = ActivityLog(user=user, action=action.id)
al.arguments = args
if 'details' in kw:
al.details = kw['details']
al.save()
# TODO(davedash): post-remora this may not be necessary.
if 'created' in kw:
al.created = kw['created']
# Double save necessary since django resets the created date on save.
@ -331,3 +334,4 @@ def log(action, *args, **kw):
# Index by every user
UserLog(activity_log=al, user=user).save()
return al

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

@ -0,0 +1,37 @@
"""Tests for the activitylog."""
from datetime import datetime
import test_utils
from nose.tools import eq_
import amo
from addons.models import Addon
from users.models import UserProfile
class LogTest(test_utils.TestCase):
def setUp(self):
u = UserProfile.objects.create(username='foo')
amo.set_user(u)
def test_details(self):
"""
If we get details, verify they are stored as JSON, and we get out what
we put in.
"""
a = Addon.objects.create(name='kumar is awesome',
type=amo.ADDON_EXTENSION)
magic = dict(title='no', body='way!')
al = amo.log(amo.LOG.DELETE_REVIEW, 1, a, details=magic)
eq_(al.details, magic)
eq_(al._details, '{"body": "way!", "title": "no"}')
def test_created(self):
"""
Verify that we preserve the create date.
"""
al = amo.log(amo.LOG.CUSTOM_TEXT, 'hi', created=datetime(2009, 1, 1))
eq_(al.created, datetime(2009, 1, 1))

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

@ -133,6 +133,7 @@ class ActivityLog(amo.models.ModelBase):
user = models.ForeignKey('users.UserProfile', null=True)
action = models.SmallIntegerField(choices=TYPES, db_index=True)
_arguments = models.TextField(blank=True, db_column='arguments')
_details = models.TextField(blank=True, db_column='details')
objects = ActivityLogManager()
formatter = SafeFormatter()
@ -194,6 +195,15 @@ class ActivityLog(amo.models.ModelBase):
self._arguments = json.dumps(serialize_me)
@property
def details(self):
if self._details:
return json.loads(self._details)
@details.setter
def details(self, data):
self._details = json.dumps(data)
# TODO(davedash): Support other types.
def to_string(self, type=None):
log_type = amo.LOG_BY_ID[self.action]

33
apps/editors/forms.py Normal file
Просмотреть файл

@ -0,0 +1,33 @@
from datetime import timedelta
from django import forms
import happyforms
from tower import ugettext_lazy as _lazy
import amo
ACTION_FILTERS = (('', ''), ('approved', _lazy('Approved reviews')),
('deleted', _lazy('Deleted reviews')))
ACTION_DICT = dict(approved=amo.LOG.APPROVE_REVIEW,
deleted=amo.LOG.DELETE_REVIEW)
class EventLogForm(happyforms.Form):
start = forms.DateField(required=False,
label=_lazy(u'View entries between'))
end = forms.DateField(required=False,
label=_lazy(u'and'))
filter = forms.ChoiceField(required=False, choices=ACTION_FILTERS,
label=_lazy(u'Filter by type/action'))
def clean(self):
data = self.cleaned_data
# We want this to be inclusive of the end date.
if data['end']:
data['end'] += timedelta(days=1)
if data['filter']:
data['filter'] = ACTION_DICT[data['filter']]
return data

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

@ -0,0 +1,49 @@
{% extends "editors/base.html" %}
{% block content %}
<h2>{{ _('Event Log') }}</h2>
<div class="listing results">
<div class="results-inner controls">
<form action="{{ url('editors.eventlog') }}">
<p class="date_range">
{{ form.start.label_tag()|safe }}
{{ form.start|safe }}
{{ form.end.label_tag()|safe }}
{{ form.end|safe }}
</p><p>
{{ form.filter.label_tag()|safe }}
{{ form.filter|safe }}
<button type="submit">{{ _('Filter') }}</button>
</p>
</form>
<table>
<thead><tr>
<th>{{ _('Date') }}</th>
<th>{{ _('Event') }}</th>
</tr></thead>
<tbody>
{% for item in pager.object_list %}
<tr>
<td>
{{ item.created|babel_datetime }}
</td>
<td>
{{ item.to_string('editor') }}
{% if item.details %}
<a href="{{ url('editors.eventlog.detail', item.id) }}">
{{ _('More details.') }}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="listing-footer">
{{ pager|paginator }}
</div>
</div>
{% endblock %}

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

@ -0,0 +1,19 @@
{% extends "editors/base.html" %}
{% block content %}
<div class="featured">
<div class="featured-inner">
<h2>{{ _('Log details') }}</h2>
<p>{{ log.to_string('editor') }}</p>
{% if log.details %}
<dl>
{% for k, v in log.details.iteritems() %}
<dt>{{ k }}</dt>
<dd>{{ v }}</dd>
{% endfor %}
</dl>
{% endif %}
</div>
</div>
{% endblock %}

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

@ -1,4 +1,6 @@
# -*- coding: utf8 -*-
from datetime import datetime
from nose.tools import eq_
from pyquery import PyQuery as pq
import test_utils
@ -6,6 +8,7 @@ import test_utils
import amo
from amo.urlresolvers import reverse
from addons.models import Addon
from devhub.models import ActivityLog
from reviews.models import Review
from users.models import UserProfile
@ -17,6 +20,62 @@ class EditorTest(test_utils.TestCase):
assert self.client.login(username='editor@mozilla.com',
password='password')
def make_review(self, username='a'):
u = UserProfile.objects.create(username=username)
a = Addon.objects.create(name='yermom', type=amo.ADDON_EXTENSION)
return Review.objects.create(user=u, addon=a)
class TestEventLog(EditorTest):
def setUp(self):
self.login_as_editor()
amo.set_user(UserProfile.objects.get(username='editor'))
review = self.make_review()
for i in xrange(30):
amo.log(amo.LOG.APPROVE_REVIEW, review, review.addon)
amo.log(amo.LOG.DELETE_REVIEW, review.id, review.addon)
def test_log(self):
r = self.client.get(reverse('editors.eventlog'))
eq_(r.status_code, 200)
def test_start_filter(self):
r = self.client.get(reverse('editors.eventlog') + '?start=3011-01-01')
eq_(r.status_code, 200)
doc = pq(r.content)
assert doc('tbody')
eq_(len(doc('tbody tr')), 0)
def test_enddate_filter(self):
"""
Make sure that if our end date is 1/1/2011, that we include items from
1/1/2011. To not do as such would be dishonorable.
"""
review = self.make_review(username='b')
amo.log(amo.LOG.APPROVE_REVIEW, review, review.addon,
created=datetime(2011, 1, 1))
r = self.client.get(reverse('editors.eventlog') + '?end=2011-01-01')
eq_(r.status_code, 200)
doc = pq(r.content)
eq_(doc('tbody td').eq(0).text(), 'Jan 1, 2011 12:00:00 AM')
def test_action_filter(self):
"""
Based on setup we should only see 30 items if we filter for deleted
reviews.
"""
r = self.client.get(reverse('editors.eventlog') + '?filter=deleted')
doc = pq(r.content)
eq_(len(doc('tbody tr')), 30)
class TestEventLogDetail(TestEventLog):
def test_me(self):
id = ActivityLog.objects.editor_events()[0].id
r = self.client.get(reverse('editors.eventlog.detail', args=[id]))
eq_(r.status_code, 200)
class TestHome(EditorTest):
"""Test the page at /editors."""
@ -24,11 +83,6 @@ class TestHome(EditorTest):
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)

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

@ -7,6 +7,9 @@ urlpatterns = patterns('',
url(r'^queue$', views.queue, name='editors.queue'),
url(r'^queue/pending$', views.queue_pending,
name='editors.queue_pending'),
url(r'^logs$', views.eventlog, name='editors.eventlog'),
url(r'^log/(\d+)$', views.eventlog_detail, name='editors.eventlog.detail'),
url(r'^review/(?P<version_id>\d+)$', views.review, name='editors.review'),
url(r'^$', views.home, name='editors.home'),
)

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

@ -1,12 +1,14 @@
import functools
from django import http
from django.shortcuts import redirect
from django.shortcuts import redirect, get_object_or_404
import jingo
import amo
from access import acl
from amo.decorators import login_required
from devhub.models import ActivityLog
from editors import forms
from editors.models import ViewEditorQueue
from editors.helpers import ViewEditorQueueTable
from amo.utils import paginate
@ -27,6 +29,32 @@ def editor_required(func):
return wrapper
@editor_required
def eventlog(request):
form = forms.EventLogForm(request.GET)
eventlog = ActivityLog.objects.editor_events()
if form.is_valid():
if form.cleaned_data['start']:
eventlog = eventlog.filter(created__gte=form.cleaned_data['start'])
if form.cleaned_data['end']:
eventlog = eventlog.filter(created__lt=form.cleaned_data['end'])
if form.cleaned_data['filter']:
eventlog = eventlog.filter(action=form.cleaned_data['filter'].id)
pager = amo.utils.paginate(request, eventlog, 50)
data = dict(form=form, pager=pager)
return jingo.render(request, 'editors/eventlog.html', data)
@editor_required
def eventlog_detail(request, id):
log = get_object_or_404(ActivityLog.objects.editor_events(), pk=id)
data = dict(log=log)
return jingo.render(request, 'editors/eventlog_detail.html', data)
@editor_required
def home(request):
data = dict(reviews_total=Approval.total_reviews(),

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

@ -240,3 +240,7 @@ ul.tabnav a:hover {
font-weight: bold;
margin-top: 0;
}
.controls {
padding: 15px;
}

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

@ -0,0 +1 @@
ALTER TABLE log_activity ADD COLUMN details longtext AFTER arguments;