2 # A new home for the reporting code.
4 # This code is part of the LWN git data miner.
6 # Copyright 2007-16 Eklektix, Inc.
7 # Copyright 2007-16 Jonathan Corbet <corbet@lwn.net>
9 # This file may be distributed under the terms of the GNU General
10 # Public License, version 2.
24 def SetHTMLOutput(file):
39 <table cellspacing=3 class="OddEven">
40 <tr><th colspan=3>%s</th></tr>
43 def BeginReport(title
):
44 Outfile
.write('\n%s\n' % title
)
46 HTMLfile
.write(THead
% title
)
48 TRow
= ' <tr><td>%s</td><td align="right">%d</td><td align="right">%.1f%%</td></tr>\n'
49 TRowStr
= ' <tr><td>%s</td><td align="right">%d</td><td>%s</td></tr>\n'
51 def ReportLine(text
, count
, pct
):
54 Outfile
.write ('%-25s %4d (%.1f%%)\n' % (text
, count
, pct
))
56 HTMLfile
.write(TRow
% (text
, count
, pct
))
58 def ReportLineStr(text
, count
, extra
):
61 Outfile
.write ('%-25s %4d %s\n' % (text
, count
, extra
))
63 HTMLfile
.write(TRowStr
% (text
, count
, extra
))
67 HTMLfile
.write('</table>\n\n')
70 # Comparison and report generation functions.
72 def ComparePCount(h1
, h2
):
73 return len(h2
.patches
) - len(h1
.patches
)
75 def ReportByPCount(hlist
, cscount
):
76 hlist
.sort(ComparePCount
)
78 BeginReport('Developers with the most changesets')
80 pcount
= len(h
.patches
)
81 changed
= max(h
.added
, h
.removed
)
82 delta
= h
.added
- h
.removed
84 ReportLine(h
.name
, pcount
, (pcount
*100.0)/cscount
)
86 if count
>= ListCount
:
90 def CompareLChanged(h1
, h2
):
91 return h2
.changed
- h1
.changed
93 def ReportByLChanged(hlist
, totalchanged
):
94 hlist
.sort(CompareLChanged
)
96 BeginReport('Developers with the most changed lines')
98 pcount
= len(h
.patches
)
100 ReportLine(h
.name
, h
.changed
, (h
.changed
*100.0)/totalchanged
)
102 if count
>= ListCount
:
106 def CompareLRemoved(h1
, h2
):
107 return (h2
.removed
- h2
.added
) - (h1
.removed
- h1
.added
)
109 def ReportByLRemoved(hlist
, totalremoved
):
110 hlist
.sort(CompareLRemoved
)
112 BeginReport('Developers with the most lines removed')
114 pcount
= len(h
.patches
)
115 changed
= max(h
.added
, h
.removed
)
116 delta
= h
.added
- h
.removed
118 ReportLine(h
.name
, -delta
, (-delta
*100.0)/totalremoved
)
120 if count
>= ListCount
:
124 def CompareEPCount(e1
, e2
):
125 return e2
.count
- e1
.count
127 def ReportByPCEmpl(elist
, cscount
):
128 elist
.sort(CompareEPCount
)
130 BeginReport('Top changeset contributors by employer')
133 ReportLine(e
.name
, e
.count
, (e
.count
*100.0)/cscount
)
135 if count
>= ListCount
:
140 def CompareELChanged(e1
, e2
):
141 return e2
.changed
- e1
.changed
143 def ReportByELChanged(elist
, totalchanged
):
144 elist
.sort(CompareELChanged
)
146 BeginReport('Top lines changed by employer')
149 ReportLine(e
.name
, e
.changed
, (e
.changed
*100.0)/totalchanged
)
151 if count
>= ListCount
:
157 def CompareSOBs(h1
, h2
):
158 return len(h2
.signoffs
) - len(h1
.signoffs
)
160 def ReportBySOBs(hlist
):
161 hlist
.sort(CompareSOBs
)
164 totalsobs
+= len(h
.signoffs
)
166 BeginReport('Developers with the most signoffs (total %d)' % totalsobs
)
168 scount
= len(h
.signoffs
)
170 ReportLine(h
.name
, scount
, (scount
*100.0)/totalsobs
)
172 if count
>= ListCount
:
177 # Reviewer reporting.
179 def CompareRevs(h1
, h2
):
180 return len(h2
.reviews
) - len(h1
.reviews
)
182 def ReportByRevs(hlist
):
183 hlist
.sort(CompareRevs
)
186 totalrevs
+= len(h
.reviews
)
188 BeginReport('Developers with the most reviews (total %d)' % totalrevs
)
190 scount
= len(h
.reviews
)
192 ReportLine(h
.name
, scount
, (scount
*100.0)/totalrevs
)
194 if count
>= ListCount
:
201 def CompareTests(h1
, h2
):
202 return len(h2
.tested
) - len(h1
.tested
)
204 def ReportByTests(hlist
):
205 hlist
.sort(CompareTests
)
208 totaltests
+= len(h
.tested
)
210 BeginReport('Developers with the most test credits (total %d)' % totaltests
)
212 scount
= len(h
.tested
)
214 ReportLine(h
.name
, scount
, (scount
*100.0)/totaltests
)
216 if count
>= ListCount
:
220 def CompareTestCred(h1
, h2
):
221 return h2
.testcred
- h1
.testcred
223 def ReportByTestCreds(hlist
):
224 hlist
.sort(CompareTestCred
)
227 totaltests
+= h
.testcred
229 BeginReport('Developers who gave the most tested-by credits (total %d)' % totaltests
)
232 ReportLine(h
.name
, h
.testcred
, (h
.testcred
*100.0)/totaltests
)
234 if count
>= ListCount
:
241 # Reporter reporting.
243 def CompareReports(h1
, h2
):
244 return len(h2
.reports
) - len(h1
.reports
)
246 def ReportByReports(hlist
):
247 hlist
.sort(CompareReports
)
250 totalreps
+= len(h
.reports
)
252 BeginReport('Developers with the most report credits (total %d)' % totalreps
)
254 scount
= len(h
.reports
)
256 ReportLine(h
.name
, scount
, (scount
*100.0)/totalreps
)
258 if count
>= ListCount
:
262 def CompareRepCred(h1
, h2
):
263 return h2
.repcred
- h1
.repcred
265 def ReportByRepCreds(hlist
):
266 hlist
.sort(CompareRepCred
)
269 totalreps
+= h
.repcred
271 BeginReport('Developers who gave the most report credits (total %d)' % totalreps
)
274 ReportLine(h
.name
, h
.repcred
, (h
.repcred
*100.0)/totalreps
)
276 if count
>= ListCount
:
283 def CompareVersionCounts(h1
, h2
):
284 if h1
.versions
and h2
.versions
:
285 return len(h2
.versions
) - len(h1
.versions
)
292 def MissedVersions(hv
, allv
):
293 missed
= [v
for v
in allv
if v
not in hv
]
295 return ' '.join(missed
)
297 def ReportVersions(hlist
):
298 hlist
.sort(CompareVersionCounts
)
299 BeginReport('Developers represented in the most kernel versions')
301 allversions
= hlist
[0].versions
303 ReportLineStr(h
.name
, len(h
.versions
), MissedVersions(h
.versions
, allversions
))
305 if count
>= ListCount
:
310 def CompareESOBs(e1
, e2
):
311 return e2
.sobs
- e1
.sobs
313 def ReportByESOBs(elist
):
314 elist
.sort(CompareESOBs
)
319 BeginReport('Employers with the most signoffs (total %d)' % totalsobs
)
322 ReportLine(e
.name
, e
.sobs
, (e
.sobs
*100.0)/totalsobs
)
324 if count
>= ListCount
:
328 def CompareHackers(e1
, e2
):
329 return len(e2
.hackers
) - len(e1
.hackers
)
331 def ReportByEHackers(elist
):
332 elist
.sort(CompareHackers
)
335 totalhackers
+= len(e
.hackers
)
337 BeginReport('Employers with the most hackers (total %d)' % totalhackers
)
339 nhackers
= len(e
.hackers
)
341 ReportLine(e
.name
, nhackers
, (nhackers
*100.0)/totalhackers
)
343 if count
>= ListCount
:
348 def DevReports(hlist
, totalchanged
, cscount
, totalremoved
):
349 ReportByPCount(hlist
, cscount
)
350 ReportByLChanged(hlist
, totalchanged
)
351 ReportByLRemoved(hlist
, totalremoved
)
355 ReportByTestCreds(hlist
)
356 ReportByReports(hlist
)
357 ReportByRepCreds(hlist
)
359 def EmplReports(elist
, totalchanged
, cscount
):
360 ReportByPCEmpl(elist
, cscount
)
361 ReportByELChanged(elist
, totalchanged
)
363 ReportByEHackers(elist
)
366 # Who are the unknown hackers?
369 empl
= h
.employer
[0][0][1].name
370 return h
.email
[0] == empl
or empl
== '(Unknown)'
372 def ReportUnknowns(hlist
, cscount
):
374 # Trim the list to just the unknowns; try to work properly whether
375 # mapping to (Unknown) is happening or not.
377 ulist
= [ h
for h
in hlist
if IsUnknown(h
) ]
378 ulist
.sort(ComparePCount
)
380 BeginReport('Developers with unknown affiliation')
382 pcount
= len(h
.patches
)
384 ReportLine(h
.name
, pcount
, (pcount
*100.0)/cscount
)
386 if count
>= ListCount
:
392 def ReportByFileType(hacker_list
):
396 BeginReport('Developer contributions by type')
397 for h
in hacker_list
:
399 for patch
in h
.patches
:
400 # Get a summary by hacker
401 for (filetype
, (added
, removed
)) in patch
.filetypes
.iteritems():
402 if by_hacker
.has_key(filetype
):
403 by_hacker
[filetype
][patch
.ADDED
] += added
404 by_hacker
[filetype
][patch
.REMOVED
] += removed
406 by_hacker
[filetype
] = [added
, removed
]
409 if total
.has_key(filetype
):
410 total
[filetype
][patch
.ADDED
] += added
411 total
[filetype
][patch
.REMOVED
] += removed
413 total
[filetype
] = [added
, removed
, []]
415 # Print a summary by hacker
417 for filetype
, counters
in by_hacker
.iteritems():
418 print '\t', filetype
, counters
419 h_added
= by_hacker
[filetype
][patch
.ADDED
]
420 h_removed
= by_hacker
[filetype
][patch
.REMOVED
]
421 total
[filetype
][2].append([h
.name
, h_added
, h_removed
])
423 # Print the global summary
424 BeginReport('Contributions by type and developers')
425 for filetype
, (added
, removed
, hackers
) in total
.iteritems():
426 print filetype
, added
, removed
427 for h
, h_added
, h_removed
in hackers
:
428 print '\t%s: [%d, %d]' % (h
, h_added
, h_removed
)
430 # Print the very global summary
431 BeginReport('General contributions by type')
432 for filetype
, (added
, removed
, hackers
) in total
.iteritems():
433 print filetype
, added
, removed
436 # The file access report is a special beast.
438 def FileAccessReport(name
, accesses
, total
):
439 outf
= open(name
, 'w')
440 files
= accesses
.keys()
444 outf
.write('%6d %6.1f%% %s\n' % (a
, (100.0*a
)/total
, file))