2525# name | chart | description | expected (stdin)
2626# -------------------+-------+---------------------------+-----------------
2727# authors | pie | git authors | -
28- # commits_hour | bar | commits by hour of day | -
28+ # commits_hour_day | bar | commits by hour of day | -
2929# commits_hour_week | dot | commits by hour of week | -
30- # commits_day | bar | commits by day of week | -
30+ # commits_day | bar | commits by day | -
31+ # commits_day_week | bar | commits by day of week | -
3132# commits_month | bar | commits by month of year | -
3233# commits_year | bar | commits by year | -
3334# commits_year_month | bar | commits by year/month | -
4748import sys
4849import traceback
4950
50- VERSION = '0.7 '
51+ VERSION = '0.8 '
5152
5253
5354class GitChart :
5455 """Generate a git stat chart."""
5556
5657 charts = {
5758 'authors' : 'Authors' ,
58- 'commits_hour ' : 'Commits by hour of day' ,
59+ 'commits_hour_day ' : 'Commits by hour of day' ,
5960 'commits_hour_week' : 'Commits by hour of week' ,
60- 'commits_day' : 'Commits by day of week' ,
61+ 'commits_day' : 'Commits by day' ,
62+ 'commits_day_week' : 'Commits by day of week' ,
6163 'commits_month' : 'Commits by month of year' ,
6264 'commits_year' : 'Commits by year' ,
6365 'commits_year_month' : 'Commits by year/month' ,
@@ -80,12 +82,13 @@ class GitChart:
8082 '#ff00cc' , '#899ca1' , '#bf4646' ))
8183
8284 def __init__ (self , chart_name , title = None , repository = '.' , output = None ,
83- max_entries = 20 , in_data = None ):
85+ max_diff = 20 , sort_max = 0 , in_data = None ):
8486 self .chart_name = chart_name
8587 self .title = title if title is not None else self .charts [chart_name ]
8688 self .repository = repository
8789 self .output = output
88- self .max_entries = max_entries
90+ self .max_diff = max_diff
91+ self .sort_max = sort_max
8992 self .in_data = in_data
9093
9194 def _git_command (self , command1 , command2 = None ):
@@ -107,16 +110,35 @@ def _git_command(self, command1, command2=None):
107110 cwd = self .repository )
108111 return p .communicate ()[0 ].decode ('utf-8' ).strip ().split ('\n ' )
109112
110- def _generate_bar_chart (self , data , sorted_keys = None , x_labels = None ,
111- x_label_rotation = 0 ):
113+ def _generate_bar_chart (self , data , sorted_keys = None , max_keys = 0 ,
114+ max_x_labels = 0 , x_label_rotation = 0 ):
112115 """Generate a bar chart."""
113116 bar_chart = pygal .Bar (style = self .style , show_legend = False ,
114117 x_label_rotation = x_label_rotation ,
115118 label_font_size = 12 )
116119 bar_chart .title = self .title
117- if not sorted_keys :
120+ # sort and keep max entries (if asked)
121+ if self .sort_max != 0 :
122+ sorted_keys = sorted (data , key = data .get , reverse = self .sort_max < 0 )
123+ keep = - 1 * self .sort_max
124+ if keep > 0 :
125+ sorted_keys = sorted_keys [:keep ]
126+ else :
127+ sorted_keys = sorted_keys [keep :]
128+ elif not sorted_keys :
118129 sorted_keys = sorted (data )
119- bar_chart .x_labels = x_labels if x_labels else sorted_keys
130+ if max_keys != 0 :
131+ sorted_keys = sorted_keys [- 1 * max_keys :]
132+ bar_chart .x_labels = sorted_keys [:]
133+ if max_x_labels > 0 and len (bar_chart .x_labels ) > max_x_labels :
134+ # reduce number of x labels for readibility: keep only one label
135+ # on N, starting from the end
136+ n = max (2 , (len (bar_chart .x_labels ) // max_x_labels ) * 2 )
137+ count = 0
138+ for i in range (len (bar_chart .x_labels ) - 1 , - 1 , - 1 ):
139+ if count % n != 0 :
140+ bar_chart .x_labels [i ] = ''
141+ count += 1
120142 bar_chart .add ('' , [data [n ] for n in sorted_keys ])
121143 self ._render (bar_chart )
122144
@@ -134,7 +156,7 @@ def _chart_authors(self):
134156 for author in stdout :
135157 (number , name ) = author .strip ().split ('\t ' , 1 )
136158 count += 1
137- if self .max_entries <= 0 or count <= self .max_entries :
159+ if self .max_diff <= 0 or count <= self .max_diff :
138160 pie_chart .add (name + ' ({0})' .format (number ), int (number ))
139161 else :
140162 count_others += 1
@@ -145,7 +167,7 @@ def _chart_authors(self):
145167 self ._render (pie_chart )
146168 return True
147169
148- def _chart_commits_hour (self ):
170+ def _chart_commits_hour_day (self ):
149171 """Generate bar chart with commits by hour of day."""
150172 # format of lines in stdout: 2013-03-15 18:27:55 +0100
151173 stdout = self ._git_command (['git' , 'log' , '--date=iso' ,
@@ -175,6 +197,18 @@ def _chart_commits_hour_week(self):
175197 return True
176198
177199 def _chart_commits_day (self ):
200+ """Generate bar chart with commits by day."""
201+ # format of lines in stdout: 2013-03-15
202+ stdout = self ._git_command (['git' , 'log' , '--date=short' ,
203+ '--pretty=format:%ad' ])
204+ commits = {}
205+ for line in stdout :
206+ commits [line ] = commits .get (line , 0 ) + 1
207+ self ._generate_bar_chart (commits , max_keys = self .max_diff ,
208+ x_label_rotation = 45 )
209+ return True
210+
211+ def _chart_commits_day_week (self ):
178212 """Generate bar chart with commits by day of week."""
179213 # format of lines in stdout: Fri, 15 Mar 2013 18:27:55 +0100
180214 stdout = self ._git_command (['git' , 'log' , '--date=rfc' ,
@@ -238,17 +272,7 @@ def _chart_commits_year_month(self):
238272 date += 89
239273 else :
240274 date += 1
241- x_labels = sorted (commits )
242- # if there are more than 20 commits, keep one x label on 10
243- # (starting from the end)
244- if len (commits ) > 20 :
245- n = 0
246- for i in range (len (x_labels ) - 1 , - 1 , - 1 ):
247- if n % 10 != 0 :
248- x_labels [i ] = ''
249- n += 1
250- self ._generate_bar_chart (commits , x_labels = x_labels ,
251- x_label_rotation = 45 )
275+ self ._generate_bar_chart (commits , max_x_labels = 30 , x_label_rotation = 45 )
252276 return True
253277
254278 def _chart_commits_version (self ):
@@ -290,7 +314,7 @@ def _chart_files_type(self):
290314 sum_others = 0
291315 for ext in sorted (extensions , key = extensions .get , reverse = True ):
292316 count += 1
293- if self .max_entries <= 0 or count <= self .max_entries :
317+ if self .max_diff <= 0 or count <= self .max_diff :
294318 pie_chart .add (ext + ' ({0})' .format (extensions [ext ]),
295319 extensions [ext ])
296320 else :
@@ -334,10 +358,17 @@ def main():
334358 help = 'override the default chart title' )
335359 parser .add_argument ('-r' , '--repo' , default = '.' ,
336360 help = 'directory with git repository' )
337- parser .add_argument ('-m ' , '--max' , type = int , default = 20 ,
361+ parser .add_argument ('-d ' , '--max-diff ' , type = int , default = 20 ,
338362 help = 'max different entries in chart: after this '
339- 'number, an entry is counted in "others" (only for '
340- 'charts "authors" and "files_type"), 0=unlimited' )
363+ 'number, an entry is counted in "others" (for charts '
364+ 'authors and files_type); max number of days (for '
365+ 'chart commits_day); 0=unlimited' )
366+ parser .add_argument ('-s' , '--sort-max' , type = int , default = 0 ,
367+ help = 'keep max entries in chart and sort them by '
368+ 'value; a negative number will reverse the sort '
369+ '(only for charts: commits_hour_day, commits_day, '
370+ 'commits_day_week, commits_month, commits_year, '
371+ 'commits_year_month, commits_version); 0=no sort/max' )
341372 parser .add_argument ('chart' , metavar = 'chart' ,
342373 choices = sorted (GitChart .charts ),
343374 help = 'name of chart, one of: ' +
@@ -362,8 +393,8 @@ def main():
362393 in_data += data .decode ('utf-8' )
363394
364395 # generate chart
365- chart = GitChart (args .chart , args .title , args .repo , args .output , args . max ,
366- in_data )
396+ chart = GitChart (args .chart , args .title , args .repo , args .output ,
397+ args . max_diff , args . sort_max , in_data )
367398 if chart .generate ():
368399 sys .exit (0 )
369400
0 commit comments