gitstats: Bugfix for the author filter in author.py
[git-stats.git] / src / git_stats / author.py
blob70f94ff197755920297c63e0be5e06cfa9b7cf79
1 #!/usr/bin/env python
3 import os
4 import sys
6 from optparse import OptionParser
7 from git_python import Git
9 from git_stats import commit
11 class Activity:
12 """Simple storage class containing stats on the activity in one file."""
14 def __init__(self):
15 self.count = 1
16 self.added = 0
17 self.deleted = 0
18 self.id = []
20 def __str__(self):
21 return str(self.count) + ": +" + str(self.added) + " -" + str(self.deleted)
23 def activityInArea(log, filterid):
24 """Parses the specified file containing commit logs.
25 The output is filtered to contain only the results of the specified author.
26 The output is expected to be in the format described below:
28 [<id>\n]+
29 [<lines added>\t<lines deleted>\t<path>]+
33 Params:
34 log: The log formatted as described above.
35 filterid: The id address to filter on.
37 Returns:
38 A dictionary containing activity per path is returned.
39 Each path contains one Activity object with the aggregated results.
40 """
42 # Create a dict to store the activity stats per path
43 activityByPath = {}
45 # Create a place to store the result in
46 activity = Activity()
48 i = 0
50 # Parse all the lines in the file
51 for line in log:
52 i += 1
54 # Split the line at the tab and store the data
55 splitline = line.split('\t')
56 size = len(splitline)
57 length = len(line.lstrip())
59 # There is something on this line, but it contains no separator
60 if size == 1 and length > 0:
61 # Get the id address minus the newline
62 activity.id.append(line[:-1])
63 elif size == 3:
64 try:
65 addpart = splitline[0]
66 deletepart = splitline[1]
68 if addpart == '-':
69 addpart = 0
71 if deletepart == '-':
72 deletepart = 0
74 activity.added = int(addpart)
75 activity.deleted = int(deletepart)
76 except ValueError, e:
77 print("On line '" + str(i) + "', could not convert number: " + str(e))
79 path = splitline[2][:-1]
80 elif length == 0:
81 if not filterid in activity.id:
82 # Ignore other authors
83 pass
84 else:
85 result = Activity()
87 # If we already encountered this path, update the count
88 if activityByPath.has_key(path):
89 known = activityByPath[path]
90 result.added = known.added + activity.added
91 result.deleted = known.deleted + activity.deleted
92 result.count = known.count + activity.count
93 else:
94 result = activity
96 # Store it under it's path
97 activityByPath[path] = result
99 # Create a fresh activity to store the next round in
100 activity = Activity()
101 else:
102 print("Cannot parse line " + str(i) + ".")
104 # Return the result
105 return activityByPath
107 def activity(id, field, startFrom):
108 """Shows the activity for the specified developer in the current repo.
110 Params:
111 id: The id of the developer, currently only e-mail address is accepted.
112 field: The field to filter on.
113 startfrom: The commit to start logging from.
116 git = Git(".")
117 result = git.log("--numstat", "--pretty=format:%" + field, startFrom)
119 log = result.splitlines(True)
120 activity = activityInArea(log, id)
122 result = []
124 for key, value in activity.iteritems():
125 result.append(str(key) + " = " + str(value))
127 return result
129 def aggregateActivity(idFilter, field, startFrom):
130 """Aggregates the activity for all developers
132 Args:
133 idFilter: The id to filter on, if None all developers will be shown.
134 field: The field to filter on.
137 result = []
139 # TODO, implement
141 return result
143 def dispatch(*args):
144 """Dispatches author related commands
147 progname = os.path.basename(sys.argv[0]) + " author"
149 parser = OptionParser(option_class=commit.CommitOption, prog=progname)
151 parser.add_option(
152 "-a", "--aggregate",
153 action="store_true",
154 help="aggregate the results")
156 parser.add_option(
157 "-d", "--developer",
158 help="the id to filter on")
160 parser.add_option(
161 "-i", "--id",
162 help="the one/two letter identifier specifying which field to filter on")
164 parser.add_option(
165 "-s", "--start-from",
166 type="commit",
167 metavar="COMMIT",
168 help="the commit to start logging from")
170 parser.set_default("id", "ae")
171 parser.set_default("start_from", "HEAD")
173 (options, args) = parser.parse_args(list(args))
175 if not options.aggregate and not options.developer:
176 parser.error("When not specifying a developer the -a switch is required")
178 if options.aggregate:
179 result = aggregateActivity(options.developer, options.id, options.start_from)
180 else:
181 result = activity(options.developer, options.id, options.start_from)
183 for line in result:
184 print(line)