Merge lp:~kfogel/launchpad/cc-script-new-world into lp:launchpad
- cc-script-new-world
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Jonathan Lange |
Approved revision: | not available |
Merge reported by: | Karl Fogel |
Merged at revision: | not available |
Proposed branch: | lp:~kfogel/launchpad/cc-script-new-world |
Merge into: | lp:launchpad |
Diff against target: |
513 lines (+274/-131) 1 file modified
utilities/community-contributions.py (+274/-131) |
To merge this branch: | bzr merge lp:~kfogel/launchpad/cc-script-new-world |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Lange (community) | Approve | ||
Karl Fogel (community) | Needs Resubmitting | ||
Review via email: mp+19060@code.launchpad.net |
Commit message
[r=jml][ui=none][bug=432742] Improve the community-
Description of the change
Karl Fogel (kfogel) wrote : | # |
Jonathan Lange (jml) wrote : | # |
On Wed, Feb 10, 2010 at 10:49 PM, Karl Fogel <email address hidden> wrote:
> Karl Fogel has proposed merging lp:~kfogel/launchpad/cc-script-new-world into lp:launchpad/devel.
>
> Requested reviews:
> Jonathan Lange (jml)
>
Hey Karl,
The change looks great. Most of my comments on the code are related to
Python idioms.
If this were application code, I might push a bit harder on whether
the ExCon data structure is the best way of thinking about the
problem. As it is, it's not application code and I've run out of
neurons for the morning :)
>
> Various improvements to utilities/
>
> 1. Show Canonical developers too (those not on the Launchpad team)
> 2. Don't show huge commit blocks; just elide and link to the top rev.
> 3. Finally clean up various utf-8 encoding issues (bug #432742)
> 4. Add a '--draft-run' option for testing.
>
> You can see the results here:
>
> https:/
>
> (Compare with https:/
>
The new output is much better.
> === modified file 'utilities/
> --- utilities/
> +++ utilities/
...
> @@ -56,85 +57,133 @@
> sys.exit(1)
>
>
> -# While anyone with "@canonical.com" in their email address will be
> -# counted as a Canonical contributor, sometimes Canonical people
> -# submit from personal addresses, so we still need a list.
> +# The output contains two classes of contributors: people who don't
> +# work for Canonical at all, and people who do work for Canonical but
> +# not on the Launchpad team.
As wgrant hinted on IRC, it's worth being explicit about what we do
with Launchpad team alumnists (e.g. Carlos & Daniel Silverstone).
> #
> # XXX: Karl Fogel 2009-09-10 bug=513608: We should use launchpadlib
> -# to consult Launchpad itself to find out who's a Canonical developer.
> -known_
> - u'Aaron Bentley',
> - u'Abel Deuring',
> - u'Adam Conrad',
> - u'Andrew Bennetts',
> - u'Anthony Lenton',
> - u'Barry Warsaw',
> - u'Brad Crittenden',
> - u'Carlos Perello Marin',
> - u'Carlos Perelló Marín',
> - u'Celso Providelo',
> - u'Christian Robottom Reis',
> - u'Cody Somerville',
> - u'Curtis Hovey',
> - u'Dafydd Harries',
> - u'Daniel Silverstone',
> - u'Danilo Šegan',
> - u'Данило Шеган',
> - u'данило шеган',
> - u'David Allouche',
> - u'Deryck Hodge',
> - u'Diogo Matsubara',
> - u'Elliot Murphy',
> - u'Francis J. Lacoste',
> - u'Gabriel Neuman <email address hidden>',
> - u'Gary Poster',
> - u'Guilherme Salgado',
> - u'Gustavo Niemeyer',
> - u'Henning Eggers',
> - u'Herb McNew',
> - u'James Henstridge',
> - u'Jelmer Vernooij',
> - u'Jeroen Vermeulen',
> - u'Jonathan Knowles',
> - u'Jonathan Lange',
> - u'Julian Edwards',
> - u'Karl Fogel',
> - u'Kees Cook',
> - u'Launch Pad',
> - u'Launchpad Developers',
> - u'LaMont Jones',
> - u'Leonard Richardson',
> - u'Malcolm Cleaton',
> - u'Maris Fogels',
> - u'Martin...
Jonathan Lange (jml) : | # |
Karl Fogel (kfogel) wrote : | # |
Thanks for the thorough review, Jono. All comments addressed; please see the latest.
Jonathan Lange (jml) wrote : | # |
Thanks Karl. Looks good to me.
Preview Diff
1 | === modified file 'utilities/community-contributions.py' | |||
2 | --- utilities/community-contributions.py 2010-02-08 14:38:31 +0000 | |||
3 | +++ utilities/community-contributions.py 2010-02-22 15:15:33 +0000 | |||
4 | @@ -18,9 +18,10 @@ | |||
5 | 18 | have it, the error message will tell you where to get it). | 18 | have it, the error message will tell you where to get it). |
6 | 19 | 19 | ||
7 | 20 | Options: | 20 | Options: |
11 | 21 | -q Print no non-essential messages. | 21 | -q Print no non-essential messages. |
12 | 22 | -h, --help Print this help. | 22 | -h, --help Print this help. |
13 | 23 | --dry-run Don't update the wiki, just print the new wiki page to stdout. | 23 | --dry-run Don't update the wiki, just print the new wiki page to stdout. |
14 | 24 | --draft-run Update the wiki "/Draft" page instead of the real page. | ||
15 | 24 | """ | 25 | """ |
16 | 25 | 26 | ||
17 | 26 | # General notes: | 27 | # General notes: |
18 | @@ -56,85 +57,160 @@ | |||
19 | 56 | sys.exit(1) | 57 | sys.exit(1) |
20 | 57 | 58 | ||
21 | 58 | 59 | ||
25 | 59 | # While anyone with "@canonical.com" in their email address will be | 60 | def wiki_encode(x): |
26 | 60 | # counted as a Canonical contributor, sometimes Canonical people | 61 | """Encode a Unicode string for display on the wiki.""" |
27 | 61 | # submit from personal addresses, so we still need a list. | 62 | return x.encode('utf-8', 'xmlcharrefreplace') |
28 | 63 | |||
29 | 64 | |||
30 | 65 | |||
31 | 66 | # The output contains two classes of contributors: people who don't | ||
32 | 67 | # work for Canonical at all, and people who do work for Canonical but | ||
33 | 68 | # not on the Launchpad team. | ||
34 | 69 | # | ||
35 | 70 | # People who used to work for Canonical on the Launchpad team are not | ||
36 | 71 | # shown in the output, since they don't help us from a "contributions | ||
37 | 72 | # from outside the team" perspective, so they are listed as known | ||
38 | 73 | # Canonical Launchpad developers even though they aren't actually on | ||
39 | 74 | # the team anymore. There may be a few former Canonicalites who | ||
40 | 75 | # didn't work on the Launchpad team but who still contributed to | ||
41 | 76 | # Launchpad; most of them would have done so before Launchpad was open | ||
42 | 77 | # sourced in July 2009, though, and since this script is really about | ||
43 | 78 | # showing things that have happened since Launchpad was open sourced, | ||
44 | 79 | # they may be listed as Launchpad team members anyway just to ensure | ||
45 | 80 | # they don't appear in the output. | ||
46 | 81 | # | ||
47 | 82 | # (As time goes on, that assumption will be less and less correct, of | ||
48 | 83 | # course, and eventually we may wish to do something about it. Also, | ||
49 | 84 | # there are some people, e.g. Jelmer Vernooij, who made contributions | ||
50 | 85 | # to Launchpad before working at Canonical, but who now work on the | ||
51 | 86 | # Launchpad team at Canonical. Ideally, each potentially listable | ||
52 | 87 | # contributor could have a set of roles, and a date range associated | ||
53 | 88 | # with each role... but that would be overkill for this script. That | ||
54 | 89 | # last 2% of correctness would cost way too much to achieve.) | ||
55 | 62 | # | 90 | # |
56 | 63 | # XXX: Karl Fogel 2009-09-10 bug=513608: We should use launchpadlib | 91 | # XXX: Karl Fogel 2009-09-10 bug=513608: We should use launchpadlib |
122 | 64 | # to consult Launchpad itself to find out who's a Canonical developer. | 92 | # to consult Launchpad itself to find out who's a Canonical developer, |
123 | 65 | known_canonical_devs = ( | 93 | # and within that who's a Launchpad developer. |
124 | 66 | u'Aaron Bentley', | 94 | |
125 | 67 | u'Abel Deuring', | 95 | |
126 | 68 | u'Adam Conrad', | 96 | # If a contributor's address contains this, then they are or were a |
127 | 69 | u'Andrew Bennetts', | 97 | # Canonical developer -- maybe on the Launchpad team, maybe not. |
128 | 70 | u'Anthony Lenton', | 98 | CANONICAL_ADDR = wiki_encode(u" {_AT_} canonical.com") |
129 | 71 | u'Barry Warsaw', | 99 | |
130 | 72 | u'Brad Crittenden', | 100 | # People on the Canonical Launchpad team. |
131 | 73 | u'Carlos Perello Marin', | 101 | known_canonical_lp_devs = \ |
132 | 74 | u'Carlos Perelló Marín', | 102 | [wiki_encode(x) for x in (u'Aaron Bentley', |
133 | 75 | u'Celso Providelo', | 103 | u'Abel Deuring', |
134 | 76 | u'Christian Robottom Reis', | 104 | u'Andrew Bennetts', |
135 | 77 | u'Cody Somerville', | 105 | u'Barry Warsaw', |
136 | 78 | u'Curtis Hovey', | 106 | u'Bjorn Tillenius', |
137 | 79 | u'Dafydd Harries', | 107 | u'Björn Tillenius', |
138 | 80 | u'Daniel Silverstone', | 108 | u'Brad Crittenden', |
139 | 81 | u'Danilo Šegan', | 109 | u'Brian Fromme', |
140 | 82 | u'Данило Шеган', | 110 | u'Carlos Perello Marin', |
141 | 83 | u'данило шеган', | 111 | u'Carlos Perelló Marín', |
142 | 84 | u'David Allouche', | 112 | u'Celso Providelo', |
143 | 85 | u'Deryck Hodge', | 113 | u'Christian Reis', |
144 | 86 | u'Diogo Matsubara', | 114 | u'kiko {_AT_} beetle', |
145 | 87 | u'Elliot Murphy', | 115 | u'Curtis Hovey', |
146 | 88 | u'Francis J. Lacoste', | 116 | u'Dafydd Harries', |
147 | 89 | u'Gabriel Neuman gneuman@async.com', | 117 | u'Danilo Šegan', |
148 | 90 | u'Gary Poster', | 118 | u'Данило Шеган', |
149 | 91 | u'Guilherme Salgado', | 119 | u'данило шеган', |
150 | 92 | u'Gustavo Niemeyer', | 120 | u'Daniel Silverstone', |
151 | 93 | u'Henning Eggers', | 121 | u'David Allouche', |
152 | 94 | u'Herb McNew', | 122 | u'Deryck Hodge', |
153 | 95 | u'James Henstridge', | 123 | u'Diogo Matsubara', |
154 | 96 | u'Jelmer Vernooij', | 124 | u'Edwin Grubbs', |
155 | 97 | u'Jeroen Vermeulen', | 125 | u'Francis Lacoste', |
156 | 98 | u'Jonathan Knowles', | 126 | u'Francis J. Lacoste', |
157 | 99 | u'Jonathan Lange', | 127 | u'Gary Poster', |
158 | 100 | u'Julian Edwards', | 128 | u'Gavin Panella', |
159 | 101 | u'Karl Fogel', | 129 | u'Graham Binns', |
160 | 102 | u'Kees Cook', | 130 | u'Guilherme Salgado', |
161 | 103 | u'Launch Pad', | 131 | u'Henning Eggers', |
162 | 104 | u'Launchpad Developers', | 132 | u'James Henstridge', |
163 | 105 | u'LaMont Jones', | 133 | u'Jelmer Vernooij', |
164 | 106 | u'Leonard Richardson', | 134 | u'Jeroen Vermeulen', |
165 | 107 | u'Malcolm Cleaton', | 135 | u'Jeroen T. Vermeulen', |
166 | 108 | u'Maris Fogels', | 136 | u'Joey Stanford', |
167 | 109 | u'Martin Albisetti', | 137 | u'Jonathan Lange', |
168 | 110 | u'Martin Pool', | 138 | u'jml {_AT_} canonical.com', |
169 | 111 | u'Matt Zimmerman', | 139 | u'jml {_AT_} mumak.net', |
170 | 112 | u'Matthew Revell', | 140 | u'Jonathan Knowles', |
171 | 113 | u'Michael Hudson', | 141 | u'Julian Edwards', |
172 | 114 | u'Michael Nelson', | 142 | u'Karl Fogel', |
173 | 115 | u'Muharem Hrnjadovic', | 143 | u'Launchpad APA', |
174 | 116 | u'Patch Queue Manager', | 144 | u'Launchpad Patch Queue Manager', |
175 | 117 | u'Paul Hummer', | 145 | u'Launchpad PQM Bot', |
176 | 118 | u'Robert Collins', | 146 | u'Leonard Richardson', |
177 | 119 | u'Sidnei', | 147 | u'Malcolm Cleaton', |
178 | 120 | u'Sidnei da Silva', | 148 | u'Maris Fogels', |
179 | 121 | u'Steve Kowalik', | 149 | u'Mark Shuttleworth', |
180 | 122 | u'Steve McInerney', | 150 | u'Martin Albisetti', |
181 | 123 | u'Stuart Bishop', | 151 | u'Martin Pool', |
182 | 124 | u'Tom Berger', | 152 | u'Matt Zimmerman', |
183 | 125 | u'david', | 153 | u'Matthew Paul Thomas', |
184 | 126 | u'jml@mumak.net', | 154 | u'Matthew Revell', |
185 | 127 | u'kiko@beetle', | 155 | u'matthew.revell {_AT_} canonical.com', |
186 | 128 | ) | 156 | u'Michael Hudson', |
187 | 157 | u'Michael Nelson', | ||
188 | 158 | u'Muharem Hrnjadovic', | ||
189 | 159 | u'muharem {_AT_} canonical.com', | ||
190 | 160 | u'Paul Hummer', | ||
191 | 161 | u'Robert Collins', | ||
192 | 162 | u'Stuart Bishop', | ||
193 | 163 | u'Steve McInerney', | ||
194 | 164 | u'<steve {_AT_} stedee.id.au>', | ||
195 | 165 | u'Tom Haddon', | ||
196 | 166 | u'Tim Penhey', | ||
197 | 167 | u'Tom Berger', | ||
198 | 168 | u'Ursula Junque', | ||
199 | 169 | )] | ||
200 | 170 | |||
201 | 171 | # People known to work for Canonical but not on the Launchpad team. | ||
202 | 172 | # Anyone with "@canonical.com" in their email address is considered to | ||
203 | 173 | # work for Canonical, but some people occasionally submit changes from | ||
204 | 174 | # their personal email addresses; this list contains people known to | ||
205 | 175 | # do that, so we can treat them appropriately in the output. | ||
206 | 176 | known_canonical_non_lp_devs = \ | ||
207 | 177 | [wiki_encode(x) for x in (u'Adam Conrad', | ||
208 | 178 | u'Andrew Bennetts', | ||
209 | 179 | u'Anthony Lenton', | ||
210 | 180 | u'Cody Somerville', | ||
211 | 181 | u'Cody A.W. Somerville', | ||
212 | 182 | u'David Murphy', | ||
213 | 183 | u'Elliot Murphy', | ||
214 | 184 | u'Gabriel Neuman gneuman {_AT_} async.com', | ||
215 | 185 | u'Gustavo Niemeyer', | ||
216 | 186 | u'James Henstridge', | ||
217 | 187 | u'John Lenton', | ||
218 | 188 | u'Kees Cook', | ||
219 | 189 | u'LaMont Jones', | ||
220 | 190 | u'Martin Pool', | ||
221 | 191 | u'Matt Zimmerman', | ||
222 | 192 | u'Michael Casadevall', | ||
223 | 193 | u'Michael Vogt', | ||
224 | 194 | u'Sidnei da Silva', | ||
225 | 195 | u'Steve Kowalik', | ||
226 | 196 | )] | ||
227 | 129 | 197 | ||
228 | 130 | # Some people have made commits using various names and/or email | 198 | # Some people have made commits using various names and/or email |
229 | 131 | # addresses, so this map will be used to merge them accordingly. | 199 | # addresses, so this map will be used to merge them accordingly. |
236 | 132 | merge_names_map = { | 200 | # The map is initialized from this list of pairs, where each pair is |
237 | 133 | u'Jamal Fanaian <jfanaian@gmail.com>': | 201 | # of the form (CONTRIBUTOR_AS_SEEN, UNIFYING_IDENTITY_FOR_CONTRIBUTOR). |
238 | 134 | u'Jamal Fanaian <jamal.fanaian@gmail.com>', | 202 | merge_names_pairs = ( |
239 | 135 | u'Jamal Fanaian <jamal@jfvm1>': | 203 | (u'Jamal Fanaian <jfanaian {_AT_} gmail.com>', |
240 | 136 | u'Jamal Fanaian <jamal.fanaian@gmail.com>', | 204 | u'Jamal Fanaian <jamal.fanaian {_AT_} gmail.com>'), |
241 | 137 | } | 205 | (u'Jamal Fanaian <jamal {_AT_} jfvm1>', |
242 | 206 | u'Jamal Fanaian <jamal.fanaian {_AT_} gmail.com>'), | ||
243 | 207 | (u'LaMont Jones <lamont {_AT_} rover3>', | ||
244 | 208 | u'LaMont Jones <lamont {_AT_} debian.org>'), | ||
245 | 209 | ) | ||
246 | 210 | # Then put it in dictionary form with the correct encodings. | ||
247 | 211 | merge_names_map = dict((wiki_encode(a), wiki_encode(b)) | ||
248 | 212 | for a, b in merge_names_pairs) | ||
249 | 213 | |||
250 | 138 | 214 | ||
251 | 139 | class ContainerRevision(): | 215 | class ContainerRevision(): |
252 | 140 | """A wrapper for a top-level LogRevision containing child LogRevisions.""" | 216 | """A wrapper for a top-level LogRevision containing child LogRevisions.""" |
253 | @@ -187,14 +263,22 @@ | |||
254 | 187 | # will give you some information about it before you click | 263 | # will give you some information about it before you click |
255 | 188 | # (because a rev id often identifies the committer). | 264 | # (because a rev id often identifies the committer). |
256 | 189 | rev_id_url = rev_url_base + rev_id | 265 | rev_id_url = rev_url_base + rev_id |
257 | 266 | |||
258 | 267 | if len(self.contained_revs) <= 10: | ||
259 | 268 | commits_block = "\n ".join( | ||
260 | 269 | ["[[%s|%s]]" % (rev_url_base + lr.rev.revision_id, lr.revno) | ||
261 | 270 | for lr in self.contained_revs]) | ||
262 | 271 | else: | ||
263 | 272 | commits_block = ("''see the [[%s|full revision]] for details " | ||
264 | 273 | "(it contains %d commits)''" | ||
265 | 274 | % (rev_id_url, len(self.contained_revs))) | ||
266 | 275 | |||
267 | 190 | text = [ | 276 | text = [ |
268 | 191 | " * [[%s|r%s]] -- %s\n" % (rev_id_url, self.top_rev.revno, | 277 | " * [[%s|r%s]] -- %s\n" % (rev_id_url, self.top_rev.revno, |
269 | 192 | date_str), | 278 | date_str), |
270 | 193 | " {{{\n%s\n}}}\n" % message, | 279 | " {{{\n%s\n}}}\n" % message, |
271 | 194 | " '''Commits:'''\n ", | 280 | " '''Commits:'''\n ", |
275 | 195 | "\n ".join(["[[%s|%s]]" % (rev_url_base + lr.rev.revision_id, | 281 | commits_block, |
273 | 196 | lr.revno) | ||
274 | 197 | for lr in self.contained_revs]), | ||
276 | 198 | "\n", | 282 | "\n", |
277 | 199 | ] | 283 | ] |
278 | 200 | return ''.join(text) | 284 | return ''.join(text) |
279 | @@ -202,14 +286,17 @@ | |||
280 | 202 | 286 | ||
281 | 203 | # "ExternalContributor" is too much to type, so I guess we'll just use this. | 287 | # "ExternalContributor" is too much to type, so I guess we'll just use this. |
282 | 204 | class ExCon(): | 288 | class ExCon(): |
291 | 205 | """A contributor to Launchpad from outside Canonical.""" | 289 | """A contributor to Launchpad from outside Canonical's Launchpad team.""" |
292 | 206 | 290 | ||
293 | 207 | def __init__(self, name): | 291 | def __init__(self, name, is_canonical=False): |
294 | 208 | """Create a new external contributor named NAME. NAME is usually | 292 | """Create a new external contributor named 'name'. |
295 | 209 | e.g. "Veronica Random <veronica@example.com>", but any "@"-sign | 293 | |
296 | 210 | will be disguised in the new object.""" | 294 | If 'is_canonical' is True, then this is a contributor from |
297 | 211 | 295 | within Canonical, but not on the Launchpad team at Canonical. | |
298 | 212 | self.name = name.replace("@", " {_AT_} ") | 296 | 'name' is something like "Veronica Random <vr {_AT_} example.com>". |
299 | 297 | """ | ||
300 | 298 | self.name = name | ||
301 | 299 | self.is_canonical = is_canonical | ||
302 | 213 | # If name is "Veronica Random <veronica {_AT_} example.com>", | 300 | # If name is "Veronica Random <veronica {_AT_} example.com>", |
303 | 214 | # then name_as_anchor will be "veronica_random". | 301 | # then name_as_anchor will be "veronica_random". |
304 | 215 | self.name_as_anchor = \ | 302 | self.name_as_anchor = \ |
305 | @@ -231,10 +318,13 @@ | |||
306 | 231 | def show_contributions(self): | 318 | def show_contributions(self): |
307 | 232 | "Return a wikified string showing this contributor's contributions." | 319 | "Return a wikified string showing this contributor's contributions." |
308 | 233 | plural = "s" | 320 | plural = "s" |
309 | 321 | name = self.name | ||
310 | 322 | if self.is_canonical: | ||
311 | 323 | name = name + " (Canonical developer)" | ||
312 | 234 | if self.num_landings() == 1: | 324 | if self.num_landings() == 1: |
313 | 235 | plural = "" | 325 | plural = "" |
314 | 236 | text = [ | 326 | text = [ |
316 | 237 | "== %s ==\n\n" % self.name, | 327 | "=== %s ===\n\n" % name, |
317 | 238 | "''%d top-level landing%s:''\n\n" % (self.num_landings(), plural), | 328 | "''%d top-level landing%s:''\n\n" % (self.num_landings(), plural), |
318 | 239 | ''.join(map(str, sorted(self._landings, | 329 | ''.join(map(str, sorted(self._landings, |
319 | 240 | key=lambda x: x.top_rev.revno, | 330 | key=lambda x: x.top_rev.revno, |
320 | @@ -253,29 +343,47 @@ | |||
321 | 253 | the bzr logs, i.e., with email address undisguised) to ExCon objects. | 343 | the bzr logs, i.e., with email address undisguised) to ExCon objects. |
322 | 254 | """ | 344 | """ |
323 | 255 | ex_cons_this_rev = [] | 345 | ex_cons_this_rev = [] |
329 | 256 | for a in authors: | 346 | for author in authors: |
330 | 257 | known = False | 347 | known_canonical_lp_dev = False |
331 | 258 | for name_fragment in known_canonical_devs: | 348 | known_canonical_non_lp_dev = False |
332 | 259 | if u"@canonical.com" in a or name_fragment in a: | 349 | # The authors we list in the source code have their addresses |
333 | 260 | known = True | 350 | # disguised (since this source code is public). We must |
334 | 351 | # disguise the ones coming from the Bazaar logs in the same way, | ||
335 | 352 | # so string matches will work. | ||
336 | 353 | author = wiki_encode(author) | ||
337 | 354 | author = author.replace("@", " {_AT_} ") | ||
338 | 355 | |||
339 | 356 | # If someone works/worked for Canonical on the Launchpad team, | ||
340 | 357 | # then skip them -- we don't want to show them in the output. | ||
341 | 358 | for name_fragment in known_canonical_lp_devs: | ||
342 | 359 | if name_fragment in author: | ||
343 | 360 | known_canonical_lp_dev = True | ||
344 | 261 | break | 361 | break |
362 | 262 | if not known: | 362 | if known_canonical_lp_dev: |
363 | 263 | # Use the merge names map to merge contributions from the same | 363 | continue |
364 | 264 | # person using alternate names and/or emails. | 364 | |
365 | 265 | if a in merge_names_map: | 365 | # Use the merge names map to merge contributions from the same |
366 | 266 | a = merge_names_map[a] | 366 | # person using alternate names and/or emails. |
367 | 267 | 367 | author = merge_names_map.get(author, author) | |
368 | 268 | ### There's a variant of the Singleton pattern that could be | 368 | |
369 | 269 | ### used for this, whereby instantiating an ExCon object would | 369 | if CANONICAL_ADDR in author: |
370 | 270 | ### just get back an existing object if such has already been | 370 | known_canonical_non_lp_dev = True |
371 | 271 | ### instantiated for this name. But that would make this code | 371 | else: |
372 | 272 | ### non-reentrant, and that's just not cool. | 372 | for name_fragment in known_canonical_non_lp_devs: |
373 | 273 | if a in all_ex_cons: | 373 | if name_fragment in author: |
374 | 274 | ec = all_ex_cons[a] | 374 | known_canonical_non_lp_dev = True |
375 | 275 | else: | 375 | break |
376 | 276 | ec = ExCon(a) | 376 | |
377 | 277 | all_ex_cons[a] = ec | 377 | # There's a variant of the Singleton pattern that could be |
378 | 278 | ex_cons_this_rev.append(ec) | 378 | # used for this, whereby instantiating an ExCon object would |
379 | 379 | # just get back an existing object if such has already been | ||
380 | 380 | # instantiated for this name. But that would make this code | ||
381 | 381 | # non-reentrant, and that's just not cool. | ||
382 | 382 | ec = all_ex_cons.get(author, None) | ||
383 | 383 | if ec is None: | ||
384 | 384 | ec = ExCon(author, is_canonical=known_canonical_non_lp_dev) | ||
385 | 385 | all_ex_cons[author] = ec | ||
386 | 386 | ex_cons_this_rev.append(ec) | ||
387 | 279 | return ex_cons_this_rev | 387 | return ex_cons_this_rev |
388 | 280 | 388 | ||
389 | 281 | 389 | ||
390 | @@ -296,26 +404,55 @@ | |||
391 | 296 | # top-level rev. | 404 | # top-level rev. |
392 | 297 | current_top_level_rev = None | 405 | current_top_level_rev = None |
393 | 298 | 406 | ||
394 | 407 | def _toc(self, contributors): | ||
395 | 408 | toc_text = [] | ||
396 | 409 | for val in contributors: | ||
397 | 410 | plural = "s" | ||
398 | 411 | if val.num_landings() == 1: | ||
399 | 412 | plural = "" | ||
400 | 413 | toc_text.extend(" 1. [[#%s|%s]] ''(%d top-level landing%s)''\n" | ||
401 | 414 | % (val.name_as_anchor, val.name, | ||
402 | 415 | val.num_landings(), plural)) | ||
403 | 416 | return toc_text | ||
404 | 417 | |||
405 | 299 | def result(self): | 418 | def result(self): |
406 | 300 | "Return a moin-wiki-syntax string with TOC followed by contributions." | 419 | "Return a moin-wiki-syntax string with TOC followed by contributions." |
407 | 420 | |||
408 | 421 | # Divide contributors into non-Canonical and Canonical. | ||
409 | 422 | non_canonical_contributors = [x for x in self.all_ex_cons.values() | ||
410 | 423 | if not x.is_canonical] | ||
411 | 424 | canonical_contributors = [x for x in self.all_ex_cons.values() | ||
412 | 425 | if x.is_canonical] | ||
413 | 426 | # Sort them. | ||
414 | 427 | non_canonical_contributors = sorted(non_canonical_contributors, | ||
415 | 428 | key=lambda x: x.num_landings(), | ||
416 | 429 | reverse=True) | ||
417 | 430 | canonical_contributors = sorted(canonical_contributors, | ||
418 | 431 | key=lambda x: x.num_landings(), | ||
419 | 432 | reverse=True) | ||
420 | 433 | |||
421 | 301 | text = [ | 434 | text = [ |
422 | 302 | "-----\n\n", | 435 | "-----\n\n", |
424 | 303 | "= Who =\n\n", | 436 | "= Who =\n\n" |
425 | 437 | "== Contributors (from outside Canonical) ==\n\n", | ||
426 | 304 | ] | 438 | ] |
437 | 305 | sorted_contributors = sorted(self.all_ex_cons.values(), | 439 | text.extend(self._toc(non_canonical_contributors)) |
438 | 306 | key=lambda x: x.num_landings(), | 440 | text.extend([ |
439 | 307 | reverse=True) | 441 | "== Contributors (from Canonical, but outside " |
440 | 308 | for val in sorted_contributors: | 442 | "the Launchpad team) ==\n\n", |
441 | 309 | plural = "s" | 443 | ]) |
442 | 310 | if val.num_landings() == 1: | 444 | text.extend(self._toc(canonical_contributors)) |
433 | 311 | plural = "" | ||
434 | 312 | text.extend(" 1. [[#%s|%s]] ''(%d top-level landing%s)''\n" | ||
435 | 313 | % (val.name_as_anchor, val.name, | ||
436 | 314 | val.num_landings(), plural)) | ||
443 | 315 | text.extend(["\n-----\n\n", | 445 | text.extend(["\n-----\n\n", |
444 | 316 | "= What =\n\n", | 446 | "= What =\n\n", |
447 | 317 | ]) | 447 | "== Contributions (from outside Canonical) ==\n\n", |
448 | 318 | for val in sorted_contributors: | 448 | ]) |
449 | 449 | for val in non_canonical_contributors: | ||
450 | 450 | text.extend("<<Anchor(%s)>>\n" % val.name_as_anchor) | ||
451 | 451 | text.extend(val.show_contributions()) | ||
452 | 452 | text.extend(["== Contributions (from Canonical, but outside " | ||
453 | 453 | "the Launchpad team) ==\n\n", | ||
454 | 454 | ]) | ||
455 | 455 | for val in canonical_contributors: | ||
456 | 319 | text.extend("<<Anchor(%s)>>\n" % val.name_as_anchor) | 456 | text.extend("<<Anchor(%s)>>\n" % val.name_as_anchor) |
457 | 320 | text.extend(val.show_contributions()) | 457 | text.extend(val.show_contributions()) |
458 | 321 | return ''.join(text) | 458 | return ''.join(text) |
459 | @@ -343,11 +480,14 @@ | |||
460 | 343 | print __doc__ | 480 | print __doc__ |
461 | 344 | 481 | ||
462 | 345 | 482 | ||
463 | 483 | # Use backslashes to suppress newlines because this is wiki syntax, | ||
464 | 484 | # not HTML, so newlines would be rendered as line breaks. | ||
465 | 346 | page_intro = """This page shows contributions to Launchpad from \ | 485 | page_intro = """This page shows contributions to Launchpad from \ |
470 | 347 | outside Canonical. It only lists changes that have landed in the \ | 486 | developers not on the Launchpad team at Canonical. |
471 | 348 | Launchpad ''devel'' tree, so changes that land in ''db-devel'' first \ | 487 | |
472 | 349 | may take a while to show up (see the [[Trunk|trunk explanation]] for \ | 488 | It only lists changes that have landed in the Launchpad ''devel'' \ |
473 | 350 | more). | 489 | tree, so changes that land in ''db-devel'' first may take a while to \ |
474 | 490 | show up (see the [[Trunk|trunk explanation]] for more). | ||
475 | 351 | 491 | ||
476 | 352 | ~-''Note for maintainers: this page is updated every 10 minutes by a \ | 492 | ~-''Note for maintainers: this page is updated every 10 minutes by a \ |
477 | 353 | cron job running as kfogel on devpad (though if there are no new \ | 493 | cron job running as kfogel on devpad (though if there are no new \ |
478 | @@ -363,13 +503,15 @@ | |||
479 | 363 | target = None | 503 | target = None |
480 | 364 | dry_run = False | 504 | dry_run = False |
481 | 365 | 505 | ||
482 | 506 | wiki_dest = "https://dev.launchpad.net/Contributions" | ||
483 | 507 | |||
484 | 366 | if len(sys.argv) < 2: | 508 | if len(sys.argv) < 2: |
485 | 367 | usage() | 509 | usage() |
486 | 368 | sys.exit(1) | 510 | sys.exit(1) |
487 | 369 | 511 | ||
488 | 370 | try: | 512 | try: |
489 | 371 | opts, args = getopt.getopt(sys.argv[1:], '?hq', | 513 | opts, args = getopt.getopt(sys.argv[1:], '?hq', |
491 | 372 | ['help', 'usage', 'dry-run']) | 514 | ['help', 'usage', 'dry-run', 'draft-run']) |
492 | 373 | except getopt.GetoptError, e: | 515 | except getopt.GetoptError, e: |
493 | 374 | sys.stderr.write("ERROR: " + str(e) + '\n\n') | 516 | sys.stderr.write("ERROR: " + str(e) + '\n\n') |
494 | 375 | usage() | 517 | usage() |
495 | @@ -383,6 +525,8 @@ | |||
496 | 383 | quiet = True | 525 | quiet = True |
497 | 384 | elif opt == '--dry-run': | 526 | elif opt == '--dry-run': |
498 | 385 | dry_run = True | 527 | dry_run = True |
499 | 528 | elif opt == '--draft-run': | ||
500 | 529 | wiki_dest += "/Draft" | ||
501 | 386 | 530 | ||
502 | 387 | # Ensure we have the arguments we need. | 531 | # Ensure we have the arguments we need. |
503 | 388 | if len(args) < 1: | 532 | if len(args) < 1: |
504 | @@ -419,8 +563,7 @@ | |||
505 | 419 | if not quiet: | 563 | if not quiet: |
506 | 420 | print "Updating wiki..." | 564 | print "Updating wiki..." |
507 | 421 | # Not sure how to get editmoin to obey our quiet flag. | 565 | # Not sure how to get editmoin to obey our quiet flag. |
510 | 422 | editshortcut("https://dev.launchpad.net/Contributions", | 566 | editshortcut(wiki_dest, editfile_func=update_if_modified) |
509 | 423 | editfile_func=update_if_modified) | ||
511 | 424 | if not quiet: | 567 | if not quiet: |
512 | 425 | print "Done updating wiki." | 568 | print "Done updating wiki." |
513 | 426 | else: | 569 | else: |
Various improvements to utilities/ community- contributions. py. Namely:
1. Show Canonical developers too (those not on the Launchpad team)
2. Don't show huge commit blocks; just elide and link to the top rev.
3. Finally clean up various utf-8 encoding issues (bug #432742)
4. Add a '--draft-run' option for testing.
You can see the results here:
https:/ /dev.launchpad. net/Contributio ns/Draft
(Compare with https:/ /dev.launchpad. net/Contributio ns for old output.)