Merge lp:~cjwatson/launchpad/bmp-show-diffstat into lp:launchpad

Proposed by Colin Watson on 2015-06-03
Status: Merged
Merged at revision: 17541
Proposed branch: lp:~cjwatson/launchpad/bmp-show-diffstat
Merge into: lp:launchpad
Diff against target: 220 lines (+71/-24)
4 files modified
lib/lp/code/browser/diff.py (+16/-10)
lib/lp/code/browser/tests/test_tales.py (+42/-7)
lib/lp/code/stories/branches/xx-branchmergeproposals.txt (+11/-5)
lib/lp/code/tests/helpers.py (+2/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/bmp-show-diffstat
Reviewer Review Type Date Requested Status
William Grant code 2015-06-03 Approve on 2015-06-03
Review via email: mp+260921@code.launchpad.net

Commit Message

When linking to a diff, include a per-file diffstat under an expander.

Description of the Change

One of the items on the Launchpad stakeholder list at the moment is "List files that are being modified at top of an MP for convenience". This seems like a good idea as long as it's behind an expander so that it doesn't make the page get too long, and Launchpad already has all the information it needs to render this.

To post a comment you must log in.
William Grant (wgrant) :
review: Needs Fixing (code)
Colin Watson (cjwatson) wrote :

Good point. I've pushed an attempted conversion; how's this?

William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/diff.py'
2--- lib/lp/code/browser/diff.py 2012-01-01 02:58:52 +0000
3+++ lib/lp/code/browser/diff.py 2015-06-03 11:21:01 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Display classes relating to diff objects of one sort or another."""
10@@ -15,6 +15,7 @@
11 from lp.services.browser_helpers import get_plural_text
12 from lp.services.librarian.browser import FileNavigationMixin
13 from lp.services.webapp import Navigation
14+from lp.services.webapp.escaping import structured
15 from lp.services.webapp.publisher import canonical_url
16
17
18@@ -64,13 +65,18 @@
19 diffstat = diff.diffstat
20 if diffstat is not None:
21 file_count = len(diffstat)
22- file_text = get_plural_text(
23- file_count, _(' %d file modified'), _(' %d files modified'))
24- file_text = file_text % file_count
25+ basic_file_text = get_plural_text(
26+ file_count, _('%d file modified'), _('%d files modified'))
27+ basic_file_text = basic_file_text % file_count
28+ diffstat_text = '<br/>'.join(
29+ structured('%s (+%s/-%s)', path, added, removed).escapedtext
30+ for path, (added, removed) in sorted(diffstat.items()))
31+ file_text = (
32+ '<div class="collapsible"><span>%s</span><div>%s</div></div>' %
33+ (basic_file_text, diffstat_text))
34
35 args = {
36 'line_count': _('%s lines') % diff.diff_lines_count,
37- 'file_text': file_text,
38 'conflict_text': conflict_text,
39 'count_text': count_text,
40 'url': self.url(view_name),
41@@ -78,14 +84,14 @@
42 # Under normal circumstances, there will be an associated file,
43 # however if the diff is empty, then there is no alias to link to.
44 if args['url'] is None:
45- return (
46+ return structured(
47 '<span class="empty-diff">'
48- '%(line_count)s</span>' % args)
49+ '%(line_count)s</span>', **args).escapedtext
50 else:
51- return (
52+ return structured(
53 '<a href="%(url)s" class="diff-link">'
54- '%(line_count)s%(count_text)s%(file_text)s%(conflict_text)s'
55- '</a>' % args)
56+ '%(line_count)s%(count_text)s%(conflict_text)s'
57+ '</a>', **args).escapedtext + file_text
58
59
60 class PreviewDiffFormatterAPI(DiffFormatterAPI):
61
62=== modified file 'lib/lp/code/browser/tests/test_tales.py'
63--- lib/lp/code/browser/tests/test_tales.py 2014-07-09 02:42:47 +0000
64+++ lib/lp/code/browser/tests/test_tales.py 2015-06-03 11:21:01 +0000
65@@ -1,4 +1,4 @@
66-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
67+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
68 # GNU Affero General Public License version 3 (see the file LICENSE).
69
70 """Tests for the tales formatters."""
71@@ -110,7 +110,11 @@
72 'file2': (3, 0)})
73 self.assertEqual(
74 '<a href="%s/+files/preview.diff" class="diff-link">'
75- '10 lines (+4/-0) 2 files modified</a>'
76+ '10 lines (+4/-0)</a>'
77+ '<div class="collapsible">'
78+ '<span>2 files modified</span>'
79+ '<div>file1 (+1/-0)<br/>file2 (+3/-0)</div>'
80+ '</div>'
81 % canonical_url(preview),
82 test_tales('preview/fmt:link', preview=preview))
83
84@@ -121,7 +125,26 @@
85 'file': (3, 0)})
86 self.assertEqual(
87 '<a href="%s/+files/preview.diff" class="diff-link">'
88- '10 lines (+4/-0) 1 file modified</a>'
89+ '10 lines (+4/-0)</a>'
90+ '<div class="collapsible">'
91+ '<span>1 file modified</span>'
92+ '<div>file (+3/-0)</div>'
93+ '</div>'
94+ % canonical_url(preview),
95+ test_tales('preview/fmt:link', preview=preview))
96+
97+ def test_fmt_lines_escapes_file_name(self):
98+ # File names that include HTML metacharacters are escaped.
99+ preview = self._createPreviewDiff(
100+ 10, added=4, removed=0, diffstat={
101+ 'file<name>': (3, 0)})
102+ self.assertEqual(
103+ '<a href="%s/+files/preview.diff" class="diff-link">'
104+ '10 lines (+4/-0)</a>'
105+ '<div class="collapsible">'
106+ '<span>1 file modified</span>'
107+ '<div>file&lt;name&gt; (+3/-0)</div>'
108+ '</div>'
109 % canonical_url(preview),
110 test_tales('preview/fmt:link', preview=preview))
111
112@@ -147,10 +170,16 @@
113 (self.factory.getUniqueString(), (2, 3)) for x in range(23))
114 preview = self._createStalePreviewDiff(
115 500, 89, 340, diffstat=diffstat)
116+ expected_diffstat = '<br/>'.join(
117+ '%s (+2/-3)' % path for path in sorted(diffstat))
118 self.assertEqual(
119 '<a href="%s/+files/preview.diff" class="diff-link">'
120- '500 lines (+89/-340) 23 files modified</a>'
121- % canonical_url(preview),
122+ '500 lines (+89/-340)</a>'
123+ '<div class="collapsible">'
124+ '<span>23 files modified</span>'
125+ '<div>%s</div>'
126+ '</div>'
127+ % (canonical_url(preview), expected_diffstat),
128 test_tales('preview/fmt:link', preview=preview))
129
130 def test_fmt_stale_non_empty_diff_with_conflicts(self):
131@@ -159,10 +188,16 @@
132 (self.factory.getUniqueString(), (2, 3)) for x in range(23))
133 preview = self._createStalePreviewDiff(
134 500, 89, 340, u'conflicts', diffstat=diffstat)
135+ expected_diffstat = '<br/>'.join(
136+ '%s (+2/-3)' % path for path in sorted(diffstat))
137 self.assertEqual(
138 '<a href="%s/+files/preview.diff" class="diff-link">'
139- '500 lines (+89/-340) 23 files modified (has conflicts)</a>'
140- % canonical_url(preview),
141+ '500 lines (+89/-340) (has conflicts)</a>'
142+ '<div class="collapsible">'
143+ '<span>23 files modified</span>'
144+ '<div>%s</div>'
145+ '</div>'
146+ % (canonical_url(preview), expected_diffstat),
147 test_tales('preview/fmt:link', preview=preview))
148
149
150
151=== modified file 'lib/lp/code/stories/branches/xx-branchmergeproposals.txt'
152--- lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2015-05-28 08:35:20 +0000
153+++ lib/lp/code/stories/branches/xx-branchmergeproposals.txt 2015-06-03 11:21:01 +0000
154@@ -13,7 +13,6 @@
155 ... BranchMergeProposalStatus,
156 ... BranchSubscriptionNotificationLevel,
157 ... CodeReviewNotificationLevel)
158- ... BranchSubscriptionNotificationLevel, CodeReviewNotificationLevel)
159 >>> from lp.code.interfaces.branchlookup import IBranchLookup
160 >>> from lp.code.tests.helpers import (
161 ... make_merge_proposal_without_reviewers)
162@@ -638,7 +637,7 @@
163 >>> from lazr.uri import URI
164 >>> print http(r"""
165 ... GET %s HTTP/1.1
166- ... Authorization: Basic no-priv@canonical.com:test
167+ ... Authorization: Basic no-priv@canonical.com:test
168 ... """ % URI(link['href']).path)
169 HTTP/1.1 303 See Other
170 ...
171@@ -667,7 +666,8 @@
172 Preview diff generation status
173 ------------------------------
174
175- >>> update = find_tag_by_id(nopriv_browser.contents, 'diff-pending-update')
176+ >>> update = find_tag_by_id(
177+ ... nopriv_browser.contents, 'diff-pending-update')
178 >>> print extract_text(update)
179 Updating diff...
180 An updated diff will be available in a few minutes. Reload to see the
181@@ -713,7 +713,10 @@
182 >>> print_tag_with_id(nopriv_browser.contents, 'landing-targets')
183 Ready for review for merging into lp://dev/fooix
184 Eric the Viking: Pending (code) requested ... ago
185- Diff: 47 lines (+7/-0) 2 files modified api
186+ Diff: 47 lines (+7/-13) 2 files modified
187+ file1 (+3/-8)
188+ file2 (+4/-5)
189+ api
190
191
192 Merge proposal details shown on the bug page
193@@ -734,4 +737,7 @@
194 lp://dev/~fred/fooix/proposed
195 Ready for review for merging into lp://dev/fooix
196 Eric the Viking: Pending (code) requested ... ago
197- Diff: 47 lines (+7/-0) 2 files modified api
198+ Diff: 47 lines (+7/-13) 2 files modified
199+ file1 (+3/-8)
200+ file2 (+4/-5)
201+ api
202
203=== modified file 'lib/lp/code/tests/helpers.py'
204--- lib/lp/code/tests/helpers.py 2015-04-21 16:53:45 +0000
205+++ lib/lp/code/tests/helpers.py 2015-06-03 11:21:01 +0000
206@@ -1,4 +1,4 @@
207-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
208+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
209 # GNU Affero General Public License version 3 (see the file LICENSE).
210
211 """Helper functions for code testing live here."""
212@@ -117,7 +117,7 @@
213 naked_bmp.target_branch.last_scanned_id = preview.target_revision_id
214 preview.diff_lines_count = 47
215 preview.added_lines_count = 7
216- preview.remvoed_lines_count = 13
217+ preview.removed_lines_count = 13
218 preview.diffstat = {'file1': (3, 8), 'file2': (4, 5)}
219 return {
220 'eric': eric, 'fooix': fooix, 'trunk': trunk, 'feature': feature,