Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add lpdm script to generate reports on launchpad bugs

  • Loading branch information...
commit 4ffbc7a340db1c1823f7ad6558928b08cf234e5e 1 parent cf924bc
@markmc authored
Showing with 179 additions and 1 deletion.
  1. +10 −0 database.py
  2. +132 −0 lpdm
  3. +37 −1 reports.py
View
10 database.py
@@ -24,6 +24,7 @@ def __init__ (self, name, id, elist, email):
self.reviews = [ ]
self.tested = [ ]
self.reports = [ ]
+ self.bugsfixed = [ ]
self.testcred = self.repcred = 0
def addemail (self, email, elist):
@@ -62,6 +63,9 @@ def reportcredit (self, patch):
def testcredit (self, patch):
self.testcred += 1
+ def addbugfixed (self, bug):
+ self.bugsfixed.append (bug)
+
HackersByName = { }
HackersByEmail = { }
HackersByID = { }
@@ -124,6 +128,7 @@ def __init__ (self, name):
self.name = name
self.added = self.removed = self.count = self.changed = 0
self.sobs = 0
+ self.bugsfixed = [ ]
self.hackers = [ ]
def AddCSet (self, patch):
@@ -137,6 +142,11 @@ def AddCSet (self, patch):
def AddSOB (self):
self.sobs += 1
+ def AddBug (self, bug):
+ self.bugsfixed.append(bug)
+ if bug.owner not in self.hackers:
+ self.hackers.append (bug.owner)
+
Employers = { }
def GetEmployer (name):
View
132 lpdm
@@ -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))
View
38 reports.py
@@ -91,7 +91,23 @@ def ReportByPCount (hlist, cscount):
if count >= ListCount:
break
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):
return max(h2.added, h2.removed) - max(h1.added, h1.removed)
@@ -143,6 +159,20 @@ def ReportByPCEmpl (elist, cscount):
break
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):
return e2.changed - e1.changed
@@ -341,6 +371,12 @@ def EmplReports (elist, totalchanged, cscount):
ReportByESOBs (elist)
ReportByEHackers (elist)
+def DevBugReports (hlist, totalbugs):
+ ReportByBCount (hlist, totalbugs)
+
+def EmplBugReports (elist, totalbugs):
+ ReportByBCEmpl (elist, totalbugs)
+
def ReportByFileType (hacker_list):
total = {}
total_by_hacker = {}
Please sign in to comment.
Something went wrong with that request. Please try again.