Add lpdm script to generate reports on launchpad bugs
This commit is contained in:
10
database.py
10
database.py
@@ -24,6 +24,7 @@ class Hacker:
|
|||||||
self.reviews = [ ]
|
self.reviews = [ ]
|
||||||
self.tested = [ ]
|
self.tested = [ ]
|
||||||
self.reports = [ ]
|
self.reports = [ ]
|
||||||
|
self.bugsfixed = [ ]
|
||||||
self.testcred = self.repcred = 0
|
self.testcred = self.repcred = 0
|
||||||
|
|
||||||
def addemail (self, email, elist):
|
def addemail (self, email, elist):
|
||||||
@@ -62,6 +63,9 @@ class Hacker:
|
|||||||
def testcredit (self, patch):
|
def testcredit (self, patch):
|
||||||
self.testcred += 1
|
self.testcred += 1
|
||||||
|
|
||||||
|
def addbugfixed (self, bug):
|
||||||
|
self.bugsfixed.append (bug)
|
||||||
|
|
||||||
HackersByName = { }
|
HackersByName = { }
|
||||||
HackersByEmail = { }
|
HackersByEmail = { }
|
||||||
HackersByID = { }
|
HackersByID = { }
|
||||||
@@ -124,6 +128,7 @@ class Employer:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.added = self.removed = self.count = self.changed = 0
|
self.added = self.removed = self.count = self.changed = 0
|
||||||
self.sobs = 0
|
self.sobs = 0
|
||||||
|
self.bugsfixed = [ ]
|
||||||
self.hackers = [ ]
|
self.hackers = [ ]
|
||||||
|
|
||||||
def AddCSet (self, patch):
|
def AddCSet (self, patch):
|
||||||
@@ -137,6 +142,11 @@ class Employer:
|
|||||||
def AddSOB (self):
|
def AddSOB (self):
|
||||||
self.sobs += 1
|
self.sobs += 1
|
||||||
|
|
||||||
|
def AddBug (self, bug):
|
||||||
|
self.bugsfixed.append(bug)
|
||||||
|
if bug.owner not in self.hackers:
|
||||||
|
self.hackers.append (bug.owner)
|
||||||
|
|
||||||
Employers = { }
|
Employers = { }
|
||||||
|
|
||||||
def GetEmployer (name):
|
def GetEmployer (name):
|
||||||
|
132
lpdm
Executable file
132
lpdm
Executable file
@@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/pypy
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This code is part of the LWN git data miner.
|
||||||
|
#
|
||||||
|
# Copyright 2007-11 Eklektix, Inc.
|
||||||
|
# Copyright 2007-11 Jonathan Corbet <corbet@lwn.net>
|
||||||
|
# Copyright 2011 Germán Póo-Caamaño <gpoo@gnome.org>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU General
|
||||||
|
# Public License, version 2.
|
||||||
|
|
||||||
|
|
||||||
|
import database, ConfigFile, reports
|
||||||
|
import getopt, datetime
|
||||||
|
import sys
|
||||||
|
|
||||||
|
Today = datetime.date.today()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Control options.
|
||||||
|
#
|
||||||
|
MapUnknown = 0
|
||||||
|
DevReports = 1
|
||||||
|
DumpDB = 0
|
||||||
|
CFName = 'gitdm.config'
|
||||||
|
DirName = ''
|
||||||
|
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# -b dir Specify the base directory to fetch the configuration files
|
||||||
|
# -c cfile Specify a configuration file
|
||||||
|
# -d Output individual developer stats
|
||||||
|
# -h hfile HTML output to hfile
|
||||||
|
# -l count Maximum length for output lists
|
||||||
|
# -o file File for text output
|
||||||
|
# -p prefix Prefix for CSV output
|
||||||
|
# -s Ignore author SOB lines
|
||||||
|
# -u Map unknown employers to '(Unknown)'
|
||||||
|
# -z Dump out the hacker database at completion
|
||||||
|
|
||||||
|
def ParseOpts ():
|
||||||
|
global MapUnknown, DevReports
|
||||||
|
global DumpDB
|
||||||
|
global CFName, DirName, Aggregate
|
||||||
|
|
||||||
|
opts, rest = getopt.getopt (sys.argv[1:], 'b:dc:h:l:o:uz')
|
||||||
|
for opt in opts:
|
||||||
|
if opt[0] == '-b':
|
||||||
|
DirName = opt[1]
|
||||||
|
elif opt[0] == '-c':
|
||||||
|
CFName = opt[1]
|
||||||
|
elif opt[0] == '-d':
|
||||||
|
DevReports = 0
|
||||||
|
elif opt[0] == '-h':
|
||||||
|
reports.SetHTMLOutput (open (opt[1], 'w'))
|
||||||
|
elif opt[0] == '-l':
|
||||||
|
reports.SetMaxList (int (opt[1]))
|
||||||
|
elif opt[0] == '-o':
|
||||||
|
reports.SetOutput (open (opt[1], 'w'))
|
||||||
|
elif opt[0] == '-u':
|
||||||
|
MapUnknown = 1
|
||||||
|
elif opt[0] == '-z':
|
||||||
|
DumpDB = 1
|
||||||
|
|
||||||
|
def LookupStoreHacker (name, email):
|
||||||
|
email = database.RemapEmail (email)
|
||||||
|
h = database.LookupEmail (email)
|
||||||
|
if h: # already there
|
||||||
|
return h
|
||||||
|
elist = database.LookupEmployer (email, MapUnknown)
|
||||||
|
h = database.LookupName (name)
|
||||||
|
if h: # new email
|
||||||
|
h.addemail (email, elist)
|
||||||
|
return h
|
||||||
|
return database.StoreHacker(name, elist, email)
|
||||||
|
|
||||||
|
class Bug:
|
||||||
|
def __init__(self, id, owner, date, emails):
|
||||||
|
self.id = id
|
||||||
|
self.owner = LookupStoreHacker('Unknown hacker', 'unknown@hacker.net')
|
||||||
|
self.date = date
|
||||||
|
for email in emails:
|
||||||
|
self.owner = LookupStoreHacker(owner, email)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, line):
|
||||||
|
split = line.split()
|
||||||
|
return cls(split[0], split[1], split[2], split[3:])
|
||||||
|
|
||||||
|
#
|
||||||
|
# Here starts the real program.
|
||||||
|
#
|
||||||
|
ParseOpts ()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read the config files.
|
||||||
|
#
|
||||||
|
ConfigFile.ConfigFile (CFName, DirName)
|
||||||
|
|
||||||
|
bugs = [Bug.parse(l) for l in sys.stdin]
|
||||||
|
|
||||||
|
for bug in bugs:
|
||||||
|
bug.owner.addbugfixed(bug)
|
||||||
|
empl = bug.owner.emailemployer(bug.owner.email[0], ConfigFile.ParseDate(bug.date))
|
||||||
|
empl.AddBug(bug)
|
||||||
|
|
||||||
|
if DumpDB:
|
||||||
|
database.DumpDB ()
|
||||||
|
database.MixVirtuals ()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Say something
|
||||||
|
#
|
||||||
|
hlist = database.AllHackers ()
|
||||||
|
elist = database.AllEmployers ()
|
||||||
|
ndev = nempl = 0
|
||||||
|
for h in hlist:
|
||||||
|
if len (h.bugsfixed) > 0:
|
||||||
|
ndev += 1
|
||||||
|
for e in elist:
|
||||||
|
if len(e.bugsfixed) > 0:
|
||||||
|
nempl += 1
|
||||||
|
reports.Write ('Processed %d bugs from %d developers\n' % (len(bugs), ndev))
|
||||||
|
reports.Write ('%d employers found\n' % (nempl))
|
||||||
|
|
||||||
|
if DevReports:
|
||||||
|
reports.DevBugReports (hlist, len(bugs))
|
||||||
|
reports.EmplBugReports (elist, len(bugs))
|
38
reports.py
38
reports.py
@@ -91,7 +91,23 @@ def ReportByPCount (hlist, cscount):
|
|||||||
if count >= ListCount:
|
if count >= ListCount:
|
||||||
break
|
break
|
||||||
EndReport ()
|
EndReport ()
|
||||||
|
|
||||||
|
def CompareBCount (h1, h2):
|
||||||
|
return len (h2.bugsfixed) - len (h1.bugsfixed)
|
||||||
|
|
||||||
|
def ReportByBCount (hlist, totalbugs):
|
||||||
|
hlist.sort (CompareBCount)
|
||||||
|
count = 0
|
||||||
|
BeginReport ('Developers with the most bugs fixed')
|
||||||
|
for h in hlist:
|
||||||
|
bcount = len (h.bugsfixed)
|
||||||
|
if bcount > 0:
|
||||||
|
ReportLine (h.name, bcount, (bcount*100.0)/totalbugs)
|
||||||
|
count += 1
|
||||||
|
if count >= ListCount:
|
||||||
|
break
|
||||||
|
EndReport ()
|
||||||
|
|
||||||
def CompareLChanged (h1, h2):
|
def CompareLChanged (h1, h2):
|
||||||
return max(h2.added, h2.removed) - max(h1.added, h1.removed)
|
return max(h2.added, h2.removed) - max(h1.added, h1.removed)
|
||||||
|
|
||||||
@@ -143,6 +159,20 @@ def ReportByPCEmpl (elist, cscount):
|
|||||||
break
|
break
|
||||||
EndReport ()
|
EndReport ()
|
||||||
|
|
||||||
|
def CompareEBCount (e1, e2):
|
||||||
|
return len (e2.bugsfixed) - len (e1.bugsfixed)
|
||||||
|
|
||||||
|
def ReportByBCEmpl (elist, totalbugs):
|
||||||
|
elist.sort (CompareEBCount)
|
||||||
|
count = 0
|
||||||
|
BeginReport ('Top bugs fixed by employer')
|
||||||
|
for e in elist:
|
||||||
|
if len(e.bugsfixed) != 0:
|
||||||
|
ReportLine (e.name, len(e.bugsfixed), (len(e.bugsfixed)*100.0)/totalbugs)
|
||||||
|
count += 1
|
||||||
|
if count >= ListCount:
|
||||||
|
break
|
||||||
|
EndReport ()
|
||||||
|
|
||||||
def CompareELChanged (e1, e2):
|
def CompareELChanged (e1, e2):
|
||||||
return e2.changed - e1.changed
|
return e2.changed - e1.changed
|
||||||
@@ -341,6 +371,12 @@ def EmplReports (elist, totalchanged, cscount):
|
|||||||
ReportByESOBs (elist)
|
ReportByESOBs (elist)
|
||||||
ReportByEHackers (elist)
|
ReportByEHackers (elist)
|
||||||
|
|
||||||
|
def DevBugReports (hlist, totalbugs):
|
||||||
|
ReportByBCount (hlist, totalbugs)
|
||||||
|
|
||||||
|
def EmplBugReports (elist, totalbugs):
|
||||||
|
ReportByBCEmpl (elist, totalbugs)
|
||||||
|
|
||||||
def ReportByFileType (hacker_list):
|
def ReportByFileType (hacker_list):
|
||||||
total = {}
|
total = {}
|
||||||
total_by_hacker = {}
|
total_by_hacker = {}
|
||||||
|
Reference in New Issue
Block a user