Merge lp:~james-w/python-oops-tools/recent-oopses into lp:python-oops-tools

Proposed by James Westby
Status: Merged
Approved by: James Westby
Approved revision: no longer in the source branch.
Merged at revision: 49
Proposed branch: lp:~james-w/python-oops-tools/recent-oopses
Merge into: lp:python-oops-tools
Diff against target: 232 lines (+163/-3)
6 files modified
src/oopstools/oops/static/oops.css (+7/-1)
src/oopstools/oops/templates/report.html (+36/-0)
src/oopstools/oops/test/__init__.py (+0/-2)
src/oopstools/oops/test/test_report.py (+77/-0)
src/oopstools/oops/test/testcase.py (+11/-0)
src/oopstools/oops/views.py (+32/-0)
To merge this branch: bzr merge lp:~james-w/python-oops-tools/recent-oopses
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
j.c.sackett (community) Approve
Review via email: mp+137055@code.launchpad.net

Commit message

Show the (paginated) oopses for each report.

Description of the change

Hi,

Showing the day-old reports is of limited use, because you don't know
if there are issues with the site at this moment. To try and help with that
show the oopses, sorted with the most recent first, on the report page.

It uses pagination to keep the number of oopses displayed small.

Thanks,

James

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Looks good. Thanks, James.

review: Approve
Revision history for this message
Launchpad PQM Bot (launchpad-pqm) wrote :
Download full text (4.0 KiB)

The attempt to merge lp:~james-w/python-oops-tools/recent-oopses into lp:python-oops-tools failed.Below is the output from the failed tests.

find . -type f -name '*.py[co]' -exec rm -f {} \;
rm -f bin/buildout
rm -f src/oopstools/settings.py
bzr clean-tree --unknown --force
bzr up download-cache
python bootstrap.py \
  --setup-source=download-cache/ez_setup.py \
  --download-base=download-cache/dist --eggs=eggs
Download error: [Errno 111] Connection refused -- Some packages may not be found!
Download error: [Errno 111] Connection refused -- Some packages may not be found!
Generated script '/home/pqm/pqm-workdir/oops-tools/bin/buildout'.
bin/buildout configuration:db-port=5433
Develop: '/home/pqm/pqm-workdir/oops-tools/.'
Uninstalling filetemplates.
Updating scripts.
Installing filetemplates.
Updating django.
Updating docs.
Updating tags.
bin/test
Creating test database for alias 'default'...
Destroying test database for alias 'default'...

W: line 4 [buildbot-staging]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 2 [pqm]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 3 [pqm-oops-tools]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 1 [precise]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
I: [pqm-oops-tools chroot] Running command: "cd /home/pqm/pqm-workdir/oops-tools && make check"
Nothing to delete.
+N dist/Twisted-12.2.0.tar.bz2
+N dist/lazr.jobrunner-0.11.tar.gz
+N dist/lazr.restful-0.19.7.tar.gz
+N dist/lazr.restful-0.19.8.tar.gz
+N dist/lazr.restful-0.19.9.tar.gz
+N dist/lazr.restfulclient-0.13.1.tar.gz
-D dist/lazr.restful-0.19.2.tar.gz
-D dist/lazr.restful-0.19.3.tar.gz
-D dist/lazr.restful-0.19.4.tar.gz
-D dist/lazr.restful-0.19.5.tar.gz
All changes applied successfully.
Updated to revision 531 of branch http://bazaar.launchpad.net/~launchpad/lp-source-dependencies/trunk
/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/template/loaders/filesystem.py:58: DeprecationWarning: 'django.template.loaders.filesystem.load_template_source' is deprecated; use 'django.template.loaders.filesystem.Loader' instead.
  DeprecationWarning
/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/template/loaders/app_directories.py:71: DeprecationWarning: 'django.template.loaders.app_directories.load_template_source' is deprecated; use 'django.template.loaders.app_directories.Loader' instead.
  DeprecationWarning
/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/contrib/auth/models.py:393: DeprecationWarning: The user messaging API is deprecated. Please update your code to use the new messages framework.
  category=DeprecationWarning)
........................................E
======================================================================
ERROR: test_recent_oopses (test_report.ReportTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/pqm/...

Read more...

Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you for addressing the upcall issue.

review: Approve (code)
Revision history for this message
Launchpad PQM Bot (launchpad-pqm) wrote :
Download full text (4.4 KiB)

The attempt to merge lp:~james-w/python-oops-tools/recent-oopses into lp:python-oops-tools failed.Below is the output from the failed tests.

find . -type f -name '*.py[co]' -exec rm -f {} \;
rm -f bin/buildout
rm -f src/oopstools/settings.py
bzr clean-tree --unknown --force
bzr up download-cache
python bootstrap.py \
  --setup-source=download-cache/ez_setup.py \
  --download-base=download-cache/dist --eggs=eggs
Download error: [Errno 111] Connection refused -- Some packages may not be found!
Download error: [Errno 111] Connection refused -- Some packages may not be found!
Generated script '/home/pqm/pqm-workdir/oops-tools/bin/buildout'.
bin/buildout configuration:db-port=5433
Develop: '/home/pqm/pqm-workdir/oops-tools/.'
Uninstalling filetemplates.
Updating scripts.
Installing filetemplates.
Updating django.
Updating docs.
Updating tags.
bin/test
Creating test database for alias 'default'...

W: line 4 [buildbot-staging]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 2 [pqm]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 3 [pqm-oops-tools]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
W: line 1 [precise]: Deprecated key 'location' used
I: This option will be removed in the future; please update your configuration
I: [pqm-oops-tools chroot] Running command: "cd /home/pqm/pqm-workdir/oops-tools && make check"
Nothing to delete.
Tree is up to date at revision 531 of branch http://bazaar.launchpad.net/~launchpad/lp-source-dependencies/trunk
Traceback (most recent call last):
  File "bin/test", line 58, in <module>
    djangorecipe.test.main('oopstools.settings', 'oopstools')
  File "/home/pqm/pqm-workdir/oops-tools/eggs/djangorecipe-1.3-py2.6.egg/djangorecipe/test.py", line 19, in main
    management.execute_manager(settings, argv=argv)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/core/management/base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/core/management/base.py", line 220, in execute
    output = self.handle(*args, **options)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/South-0.7.3_r929-py2.6.egg/south/management/commands/test.py", line 8, in handle
    super(Command, self).handle(*args, **kwargs)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/core/management/commands/test.py", line 37, in handle
    failures = test_runner.run_tests(test_labels)
  File "/home/pqm/pqm-workdir/oops-tools/eggs/Django-1.3.3-py2.6.egg/django/test/simple.py", line 359, in run_tests
    old_config = self.setup_databases()
  File "/home/pqm/pqm-workdir/oo...

Read more...

49. By James Westby

Show the (paginated) oopses for each report.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/oopstools/oops/static/oops.css'
2--- src/oopstools/oops/static/oops.css 2011-10-13 20:18:51 +0000
3+++ src/oopstools/oops/static/oops.css 2012-12-04 16:05:33 +0000
4@@ -7,7 +7,13 @@
5 padding:0px;
6 }
7 table {
8- border: 1px #000;
9+ border: 1px solid #000;
10+}
11+td {
12+ padding: 4px;
13+}
14+tr:nth-child(2n) {
15+ background-color: #eee;
16 }
17 p,h1,pre {
18 margin:0px 10px 10px 10px;
19
20=== modified file 'src/oopstools/oops/templates/report.html'
21--- src/oopstools/oops/templates/report.html 2011-10-13 20:18:51 +0000
22+++ src/oopstools/oops/templates/report.html 2012-12-04 16:05:33 +0000
23@@ -13,5 +13,41 @@
24 <li><a href="{{SUMMARY_URI}}/{{report.name}}-{{date|date:"Y-m-d"}}.html">{{report.title}} {{date|date:"Y-m-d"}}</a></li>
25 {% endfor %}
26 <ul>
27+ <h2>Recent Oopses</h2>
28+ <table>
29+ <th><tr>
30+ <td>Date/Time</td>
31+ <td>HTTP Method</td>
32+ <td>URL</td>
33+ <td>Duration (ms)</td>
34+ <td>Exception Type</td>
35+ <td>Oops</td>
36+ </tr></th>
37+ {% for oops in recent.object_list %}
38+ <tr>
39+ <td>{{oops.date|date:"Y-m-d H:i:s"}}</td>
40+ <td>{{oops.http_method}}</td>
41+ <td>{% if oops.url %}<a href="{{oops.url}}">{{oops.url}}</a>{% endif %}</td>
42+ <td>{{oops.total_time}}</td>
43+ <td>{{oops.exception_type}}</td>
44+ <td><a href="{% url oopstools.oops.views.index %}?oopsid={{oops.oopsid}}">{{oops.oopsid}}</a></td>
45+ </tr>
46+ {% endfor %}
47+ </table>
48+ <div class="pagination">
49+ <span class="step-links">
50+ {% if recent.has_previous %}
51+ <a href="?page={{ recent.previous_page_number }}">newer</a>
52+ {% endif %}
53+
54+ <span class="current">
55+ Page {{ recent.number }} of {{ recent.paginator.num_pages }}.
56+ </span>
57+
58+ {% if recent.has_next %}
59+ <a href="?page={{ recent.next_page_number }}">older</a>
60+ {% endif %}
61+ </span>
62+ </div>
63 </body>
64 </html>
65
66=== modified file 'src/oopstools/oops/test/__init__.py'
67--- src/oopstools/oops/test/__init__.py 2011-10-13 20:18:51 +0000
68+++ src/oopstools/oops/test/__init__.py 2012-12-04 16:05:33 +0000
69@@ -12,5 +12,3 @@
70 #
71 # You should have received a copy of the GNU Affero General Public License
72 # along with this program. If not, see <http://www.gnu.org/licenses/>.
73-
74-
75
76=== added file 'src/oopstools/oops/test/test_report.py'
77--- src/oopstools/oops/test/test_report.py 1970-01-01 00:00:00 +0000
78+++ src/oopstools/oops/test/test_report.py 2012-12-04 16:05:33 +0000
79@@ -0,0 +1,77 @@
80+# Copyright 2005-2012 Canonical Ltd. All rights reserved.
81+#
82+# This program is free software: you can redistribute it and/or modify
83+# it under the terms of the GNU Affero General Public License as published by
84+# the Free Software Foundation, either version 3 of the License, or
85+# (at your option) any later version.
86+#
87+# This program is distributed in the hope that it will be useful,
88+# but WITHOUT ANY WARRANTY; without even the implied warranty of
89+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
90+# GNU Affero General Public License for more details.
91+#
92+# You should have received a copy of the GNU Affero General Public License
93+# along with this program. If not, see <http://www.gnu.org/licenses/>.
94+
95+from datetime import datetime
96+from operator import attrgetter
97+
98+from oopstools.oops.models import (
99+ AppInstance,
100+ Classification,
101+ Infestation,
102+ Oops,
103+ Prefix,
104+ Report,
105+)
106+from oopstools.oops.test.testcase import TestCase
107+
108+
109+class ReportTests(TestCase):
110+
111+ def test_no_report(self):
112+ resp = self.client.get('/reports/some-report/')
113+ self.assertEqual(404, resp.status_code)
114+
115+ def test_inactive_report(self):
116+ report_name = 'areport'
117+ Report.objects.create(name=report_name, active=False)
118+ resp = self.client.get('/reports/%s/' % (report_name,))
119+ self.assertEqual(404, resp.status_code)
120+
121+ def test_no_recent_oopses(self):
122+ report_name = 'areport'
123+ Report.objects.create(name=report_name, active=True)
124+ resp = self.client.get('/reports/%s/' % (report_name,))
125+ self.assertEqual(200, resp.status_code)
126+ self.assertQuerysetEqual(resp.context['recent'].object_list, [])
127+
128+ def make_oops(self, prefix):
129+ classification = Classification.objects.create(
130+ title=self.getUniqueString(prefix="classification"))
131+ infestation = Infestation.objects.create(
132+ exception_type=self.getUniqueString(prefix="exc_type"),
133+ exception_value=self.getUniqueString(prefix="exc_value"))
134+ return Oops.objects.create(
135+ prefix=prefix, classification=classification,
136+ oopsinfestation=infestation, statements_count=100,
137+ appinstance=prefix.appinstance, total_time=3,
138+ date=datetime.now())
139+
140+ def make_prefix(self):
141+ appinstance = AppInstance.objects.create(
142+ title=self.getUniqueString(prefix="appinstance"))
143+ return Prefix.objects.create(
144+ value=self.getUniqueString(prefix="prefix"),
145+ appinstance=appinstance)
146+
147+ def test_recent_oopses(self):
148+ report_name = 'areport'
149+ report = Report.objects.create(name=report_name, active=True)
150+ prefix = self.make_prefix()
151+ report.prefixes.add(prefix)
152+ oops = self.make_oops(prefix)
153+ resp = self.client.get('/reports/%s/' % (report_name,))
154+ self.assertEqual(200, resp.status_code)
155+ self.assertQuerysetEqual(
156+ resp.context['recent'].object_list, [oops.pk], transform=attrgetter('pk'))
157
158=== added file 'src/oopstools/oops/test/testcase.py'
159--- src/oopstools/oops/test/testcase.py 1970-01-01 00:00:00 +0000
160+++ src/oopstools/oops/test/testcase.py 2012-12-04 16:05:33 +0000
161@@ -0,0 +1,11 @@
162+from django.test import TestCase as DjangoTestCase
163+from testtools import TestCase as TesttoolsTestCase
164+
165+
166+class TestCase(DjangoTestCase, TesttoolsTestCase):
167+
168+ def __init__(self, *args, **kwargs):
169+ # python2.6 doesn't up-call, so TesttoolsTestCase.__init__
170+ # never gets called unless we do this.
171+ DjangoTestCase.__init__(self, *args, **kwargs)
172+ TesttoolsTestCase.__init__(self, *args, **kwargs)
173
174=== modified file 'src/oopstools/oops/views.py'
175--- src/oopstools/oops/views.py 2011-11-03 00:08:18 +0000
176+++ src/oopstools/oops/views.py 2012-12-04 16:05:33 +0000
177@@ -18,6 +18,7 @@
178 from datetime import datetime, timedelta
179
180 from django.conf import settings
181+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
182 from django.http import Http404, HttpResponseRedirect
183 from django.shortcuts import render_to_response
184
185@@ -96,6 +97,32 @@
186 "prefixes": prefixes})
187
188
189+def get_page_from_query_args(paginator, query_args):
190+ """Get the page from ``paginator`` specified in the query params.
191+
192+ If the request specified ``page`` in the query params, then that
193+ page of the paginator will be retrieved.
194+
195+ This function aims to return a page regardless of the page requested,
196+ e.g. requesting a page beyond the last page will just return the
197+ last page.
198+
199+ :param paginator: a ``Paginator`` object.
200+ :param query_args: a ``dict`` of query params.
201+ :return: a ``Page`` from the ``Paginator``.
202+ """
203+ page_num = query_args.get('page', 1)
204+ try:
205+ page = paginator.page(page_num)
206+ except PageNotAnInteger:
207+ # If page is not an integer, deliver first page.
208+ page = paginator.page(1)
209+ except EmptyPage:
210+ # If page is out of range (e.g. 9999), deliver last page of results.
211+ page = paginator.page(paginator.num_pages)
212+ return page
213+
214+
215 def report(request, report_name):
216 try:
217 r = Report.objects.get(name=report_name, active=True)
218@@ -108,10 +135,15 @@
219 dates = []
220 for day in range(1, 11):
221 dates.append(now - timedelta(day))
222+ oopses = Oops.objects.filter(
223+ prefix__report=r).order_by('-date')
224+ paginator = Paginator(oopses, 50)
225+ recent_oopses = get_page_from_query_args(paginator, request.GET)
226 data = {
227 'report': r,
228 'dates': dates,
229 'SUMMARY_URI': settings.SUMMARY_URI,
230+ 'recent': recent_oopses,
231 }
232 return render_to_response("report.html", dictionary=data)
233

Subscribers

People subscribed via source and target branches

to all changes: