Added a responsibilities parameter that shows which author has done what.
The responsibilities module shows a maximum of 10 files, per author, that each author is responsible for. This fucntionality can be enabled by supplying -r or --responsibilities to gitinspector. The parameter is also enabled if --tda367 is specified.
This commit is contained in:
parent
0fa883eb55
commit
949b62736c
41
blame.py
41
blame.py
|
@ -38,13 +38,14 @@ __thread_lock__ = threading.BoundedSemaphore(NUM_THREADS)
|
|||
__blame_lock__ = threading.Lock()
|
||||
|
||||
class BlameThread(threading.Thread):
|
||||
def __init__(self, blame_string, extension, blames):
|
||||
def __init__(self, blame_string, extension, blames, filename):
|
||||
__thread_lock__.acquire() # Lock controlling the number of threads running
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.blame_string = blame_string
|
||||
self.extension = extension
|
||||
self.blames = blames
|
||||
self.filename = filename
|
||||
|
||||
def run(self):
|
||||
git_blame_r = os.popen(self.blame_string)
|
||||
|
@ -56,20 +57,20 @@ class BlameThread(threading.Thread):
|
|||
content = Blame.get_content(j)
|
||||
__blame_lock__.acquire() # Global lock used to protect calls from here...
|
||||
|
||||
if self.blames.get(author, None) == None:
|
||||
self.blames[author] = BlameEntry()
|
||||
if self.blames.get((author, self.filename), None) == None:
|
||||
self.blames[(author, self.filename)] = BlameEntry()
|
||||
|
||||
if comment.is_comment(self.extension, content):
|
||||
self.blames[author].comments += 1
|
||||
self.blames[(author, self.filename)].comments += 1
|
||||
if is_inside_comment:
|
||||
if comment.has_comment_end(self.extension, content):
|
||||
is_inside_comment = False
|
||||
else:
|
||||
self.blames[author].comments += 1
|
||||
self.blames[(author, self.filename)].comments += 1
|
||||
elif comment.has_comment_begining(self.extension, content):
|
||||
is_inside_comment = True
|
||||
|
||||
self.blames[author].rows += 1
|
||||
self.blames[(author, self.filename)].rows += 1
|
||||
__blame_lock__.release() # ...to here.
|
||||
|
||||
__thread_lock__.release() # Lock controlling the number of threads running
|
||||
|
@ -84,7 +85,7 @@ class Blame:
|
|||
if FileDiff.is_valid_extension(row) and not filtering.set_filtered(FileDiff.get_filename(row)):
|
||||
if not missing.add(row.strip()):
|
||||
blame_string = "git blame -w {0} \"".format("-C -C -M" if hard else "") + row.strip() + "\""
|
||||
thread = BlameThread(blame_string, FileDiff.get_extension(row), self.blames)
|
||||
thread = BlameThread(blame_string, FileDiff.get_extension(row), self.blames, row.strip())
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
|
@ -116,17 +117,37 @@ class Blame:
|
|||
content = re.search(" \d+\)(.*)", string)
|
||||
return content.group(1).lstrip()
|
||||
|
||||
def get_summed_blames(self):
|
||||
summed_blames = {}
|
||||
for i in self.blames.items():
|
||||
if summed_blames.get(i[0][0], None) == None:
|
||||
summed_blames[i[0][0]] = BlameEntry()
|
||||
|
||||
summed_blames[i[0][0]].rows += i[1].rows
|
||||
summed_blames[i[0][0]].comments += i[1].comments
|
||||
|
||||
return summed_blames
|
||||
|
||||
__blame__ = None
|
||||
|
||||
def get(hard):
|
||||
global __blame__
|
||||
if __blame__ == None:
|
||||
__blame__ = Blame(hard)
|
||||
|
||||
return __blame__
|
||||
|
||||
def output(hard):
|
||||
print ""
|
||||
blame = Blame(hard)
|
||||
get(hard)
|
||||
|
||||
if hard and sys.stdout.isatty():
|
||||
terminal.clear_row()
|
||||
|
||||
print "\bBelow is the number of rows from each author that have survived and"
|
||||
print "{0}Below is the number of rows from each author that have survived and".format("\b" if sys.stdout.isatty() else "")
|
||||
print "are still intact in the current revision:\n"
|
||||
terminal.printb("Author".ljust(21) + "Rows".rjust(10) + "% in comments".rjust(16))
|
||||
for i in sorted(blame.blames.items()):
|
||||
for i in sorted(__blame__.get_summed_blames().items()):
|
||||
print i[0].ljust(20)[0:20],
|
||||
print str(i[1].rows).rjust(10),
|
||||
print "{0:.2f}".format(100.0 * i[1].comments / i[1].rows).rjust(15)
|
||||
|
|
|
@ -146,7 +146,6 @@ def get(hard):
|
|||
return __changes__
|
||||
|
||||
def output(hard):
|
||||
get(hard)
|
||||
authorinfo_list = get(hard).get_authorinfo_list()
|
||||
total_changes = 0.0
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import help
|
|||
import metrics
|
||||
import missing
|
||||
import os
|
||||
import responsibilities
|
||||
import sys
|
||||
import terminal
|
||||
import timeline
|
||||
|
@ -38,6 +39,7 @@ class Runner:
|
|||
self.include_metrics = False
|
||||
self.list_file_types = False
|
||||
self.repo = "."
|
||||
self.responsibilities = False
|
||||
self.tda367 = False
|
||||
self.timeline = False
|
||||
self.useweeks = False
|
||||
|
@ -57,6 +59,9 @@ class Runner:
|
|||
if self.include_metrics:
|
||||
metrics.output()
|
||||
|
||||
if self.responsibilities:
|
||||
responsibilities.output(self.hard)
|
||||
|
||||
missing.output()
|
||||
filtering.output()
|
||||
|
||||
|
@ -69,9 +74,9 @@ if __name__ == "__main__":
|
|||
__run__ = Runner()
|
||||
|
||||
try:
|
||||
__opts__, __args__ = getopt.gnu_getopt(sys.argv[1:], "cf:hHlmTwx:", ["checkout-missing", "exclude=", "file-types=",
|
||||
"hard", "help", "list-file-types", "metrics","tda367", "timeline",
|
||||
"version"])
|
||||
__opts__, __args__ = getopt.gnu_getopt(sys.argv[1:], "cf:hHlmrTwx:", ["checkout-missing", "exclude=",
|
||||
"file-types=", "hard", "help", "list-file-types", "metrics",
|
||||
"responsibilities", "tda367", "timeline", "version"])
|
||||
except getopt.error, msg:
|
||||
print sys.argv[0], "\b:", msg
|
||||
print "Try `", sys.argv[0], "--help' for more information."
|
||||
|
@ -90,12 +95,15 @@ if __name__ == "__main__":
|
|||
__run__.list_file_types = True
|
||||
elif o in("-m", "--metrics"):
|
||||
__run__.include_metrics = True
|
||||
elif o in("-r", "--responsibilities"):
|
||||
__run__.responsibilities = True
|
||||
elif o in("--version"):
|
||||
version.output()
|
||||
sys.exit(0)
|
||||
elif o in("--tda367"):
|
||||
__run__.include_metrics = True
|
||||
__run__.list_file_types = True
|
||||
__run__.responsibilities = True
|
||||
__run__.tda367 = True
|
||||
__run__.timeline = True
|
||||
__run__.useweeks = True
|
||||
|
|
4
help.py
4
help.py
|
@ -34,10 +34,12 @@ Mandatory arguments to long options are mandatory for short options too.
|
|||
current branch of the repository
|
||||
-m --metrics include checks for certain metrics during the
|
||||
analysis of commits
|
||||
-r --responsibilities show which files the different authors seem
|
||||
most responsible for
|
||||
-T, --timeline show commit timeline, including author names
|
||||
--tda367 show statistics and information in a way that
|
||||
is formatted for the course TDA367/DIT211;
|
||||
this is the same as supplying -lmTw
|
||||
this is the same as supplying -lmrTw
|
||||
-w show all statistical information in weeks
|
||||
instead of in months
|
||||
-x, --exclude=PATTERN an exclusion pattern describing file names that
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
# Copyright © 2012 Ejwa Software. All rights reserved.
|
||||
#
|
||||
# This file is part of gitinspector.
|
||||
#
|
||||
# gitinspector is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gitinspector is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gitinspector. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import blame
|
||||
import terminal
|
||||
|
||||
class ResponsibiltyEntry:
|
||||
blames = {}
|
||||
|
||||
class Responsibilities:
|
||||
@staticmethod
|
||||
def get(hard, author_name):
|
||||
author_blames = {}
|
||||
|
||||
for i in blame.get(hard).blames.items():
|
||||
if (author_name == i[0][0]):
|
||||
total_rows = i[1].rows - i[1].comments
|
||||
if total_rows > 0:
|
||||
author_blames[i[0][1]] = total_rows
|
||||
|
||||
return sorted(author_blames.items())
|
||||
|
||||
def output(hard):
|
||||
print "\nThe following repsonsibilties, by author, were found in the current"
|
||||
print "revision of the repository (comments are exluded from the line count,"
|
||||
print "if possible):"
|
||||
|
||||
for i in sorted(set(i[0] for i in blame.get(hard).blames)):
|
||||
print "\n" + i, "is mostly responsible for:"
|
||||
responsibilities = sorted(((i[1], i[0]) for i in Responsibilities.get(hard, i)), reverse=True)
|
||||
|
||||
for j, entry in enumerate(responsibilities):
|
||||
(width, _) = terminal.get_size()
|
||||
width -= 7
|
||||
|
||||
print str(entry[0]).rjust(6),
|
||||
print "...%s" % entry[1][-width+3:] if len(entry[1]) > width else entry[1]
|
||||
|
||||
if j >= 9:
|
||||
break
|
Loading…
Reference in New Issue