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.
75 def ReportByPCount(hlist
, cscount
):
76 hlist
.sort(key
= ComparePCount
, reverse
= True)
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(h
):
93 def ReportByLChanged(hlist
, totalchanged
):
94 hlist
.sort(key
= CompareLChanged
, reverse
= True)
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(h
):
107 return (h
.removed
- h
.added
)
109 def ReportByLRemoved(hlist
, totalremoved
):
110 hlist
.sort(key
= CompareLRemoved
, reverse
= True)
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(e
):
127 def ReportByPCEmpl(elist
, cscount
):
128 elist
.sort(key
= CompareEPCount
, reverse
= True)
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(e
):
143 def ReportByELChanged(elist
, totalchanged
):
144 elist
.sort(key
= CompareELChanged
, reverse
= True)
146 BeginReport('Top lines changed by employer')
149 ReportLine(e
.name
, e
.changed
, (e
.changed
*100.0)/totalchanged
)
151 if count
>= ListCount
:
158 return len(h
.signoffs
)
160 def ReportBySOBs(hlist
):
161 hlist
.sort(key
= CompareSOBs
, reverse
= True)
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.
180 return len(h
.reviews
)
182 def ReportByRevs(hlist
):
183 hlist
.sort(key
= CompareRevs
, reverse
= True)
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
:
204 def ReportByTests(hlist
):
205 hlist
.sort(key
= CompareTests
, reverse
= True)
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(h
):
223 def ReportByTestCreds(hlist
):
224 hlist
.sort(key
= CompareTestCred
, reverse
= True)
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(h
):
244 return len(h
.reports
)
246 def ReportByReports(hlist
):
247 hlist
.sort(key
= CompareReports
, reverse
= True)
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(h
):
265 def ReportByRepCreds(hlist
):
266 hlist
.sort(key
= CompareRepCred
, reverse
= True)
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(h
):
285 return len(h
.versions
)
288 def MissedVersions(hv
, allv
):
289 missed
= [v
for v
in allv
if v
not in hv
]
291 return ' '.join(missed
)
293 def ReportVersions(hlist
):
294 hlist
.sort(key
= CompareVersionCounts
, reverse
= True)
295 BeginReport('Developers represented in the most kernel versions')
297 allversions
= hlist
[0].versions
299 ReportLineStr(h
.name
, len(h
.versions
), MissedVersions(h
.versions
, allversions
))
301 if count
>= ListCount
:
309 def ReportByESOBs(elist
):
310 elist
.sort(key
= CompareESOBs
, reverse
= True)
315 BeginReport('Employers with the most signoffs (total %d)' % totalsobs
)
318 ReportLine(e
.name
, e
.sobs
, (e
.sobs
*100.0)/totalsobs
)
320 if count
>= ListCount
:
324 def CompareHackers(e
):
325 return len(e
.hackers
)
327 def ReportByEHackers(elist
):
328 elist
.sort(key
= CompareHackers
, reverse
= True)
331 totalhackers
+= len(e
.hackers
)
333 BeginReport('Employers with the most hackers (total %d)' % totalhackers
)
335 nhackers
= len(e
.hackers
)
337 ReportLine(e
.name
, nhackers
, (nhackers
*100.0)/totalhackers
)
339 if count
>= ListCount
:
344 def DevReports(hlist
, totalchanged
, cscount
, totalremoved
):
345 ReportByPCount(hlist
, cscount
)
346 ReportByLChanged(hlist
, totalchanged
)
347 ReportByLRemoved(hlist
, totalremoved
)
351 ReportByTestCreds(hlist
)
352 ReportByReports(hlist
)
353 ReportByRepCreds(hlist
)
355 def EmplReports(elist
, totalchanged
, cscount
):
356 ReportByPCEmpl(elist
, cscount
)
357 ReportByELChanged(elist
, totalchanged
)
359 ReportByEHackers(elist
)
362 # Who are the unknown hackers?
365 empl
= h
.employer
[0][0][1].name
366 return h
.email
[0] == empl
or empl
== '(Unknown)'
368 def ReportUnknowns(hlist
, cscount
):
370 # Trim the list to just the unknowns; try to work properly whether
371 # mapping to (Unknown) is happening or not.
373 ulist
= [ h
for h
in hlist
if IsUnknown(h
) ]
374 ulist
.sort(key
= ComparePCount
, reverse
= True)
376 BeginReport('Developers with unknown affiliation')
378 pcount
= len(h
.patches
)
380 ReportLine(h
.name
, pcount
, (pcount
*100.0)/cscount
)
382 if count
>= ListCount
:
388 def ReportByFileType(hacker_list
):
392 BeginReport('Developer contributions by type')
393 for h
in hacker_list
:
395 for patch
in h
.patches
:
396 # Get a summary by hacker
397 for (filetype
, (added
, removed
)) in patch
.filetypes
.iteritems():
398 if by_hacker
.has_key(filetype
):
399 by_hacker
[filetype
][patch
.ADDED
] += added
400 by_hacker
[filetype
][patch
.REMOVED
] += removed
402 by_hacker
[filetype
] = [added
, removed
]
405 if total
.has_key(filetype
):
406 total
[filetype
][patch
.ADDED
] += added
407 total
[filetype
][patch
.REMOVED
] += removed
409 total
[filetype
] = [added
, removed
, []]
411 # Print a summary by hacker
412 # FIXME why isn't this using Outfile?
414 for filetype
, counters
in by_hacker
.iteritems():
415 print('\t', filetype
, counters
)
416 h_added
= by_hacker
[filetype
][patch
.ADDED
]
417 h_removed
= by_hacker
[filetype
][patch
.REMOVED
]
418 total
[filetype
][2].append([h
.name
, h_added
, h_removed
])
420 # Print the global summary
421 BeginReport('Contributions by type and developers')
422 for filetype
, (added
, removed
, hackers
) in total
.iteritems():
423 print(filetype
, added
, removed
)
424 for h
, h_added
, h_removed
in hackers
:
425 print('\t%s: [%d, %d]' % (h
, h_added
, h_removed
))
427 # Print the very global summary
428 BeginReport('General contributions by type')
429 for filetype
, (added
, removed
, hackers
) in total
.iteritems():
430 print(filetype
, added
, removed
)
433 # The file access report is a special beast.
435 def FileAccessReport(name
, accesses
, total
):
436 outf
= open(name
, 'w')
437 files
= sorted(accesses
)
440 outf
.write('%6d %6.1f%% %s\n' % (a
, (100.0*a
)/total
, file))