|
|
@@ -151,6 +151,8 @@ class DataCollector:
|
|
151
|
151
|
def __init__(self):
|
|
152
|
152
|
self.stamp_created = time.time()
|
|
153
|
153
|
self.cache = {}
|
|
|
154
|
+ self.total_branches = 0
|
|
|
155
|
+ self.total_tags = 0
|
|
154
|
156
|
self.total_authors = 0
|
|
155
|
157
|
self.activity_by_hour_of_day = {} # hour -> commits
|
|
156
|
158
|
self.activity_by_day_of_week = {} # day -> commits
|
|
|
@@ -318,6 +320,8 @@ class GitDataCollector(DataCollector):
|
|
318
|
320
|
DataCollector.collect(self, dir)
|
|
319
|
321
|
|
|
320
|
322
|
self.total_authors += int(getpipeoutput(['git shortlog -s %s' % getlogrange(), 'wc -l']))
|
|
|
323
|
+ self.total_branches += int(getpipeoutput(['git branch -r', 'wc -l']))
|
|
|
324
|
+ self.total_tags += int(getpipeoutput(['git tag', 'wc -l']))
|
|
321
|
325
|
#self.total_lines = int(getoutput('git-ls-files -z |xargs -0 cat |wc -l'))
|
|
322
|
326
|
|
|
323
|
327
|
# tags
|
|
|
@@ -862,40 +866,44 @@ class HTMLReportCreator(ReportCreator):
|
|
862
|
866
|
format = '%Y-%m-%d %H:%M:%S'
|
|
863
|
867
|
self.printHeader(f)
|
|
864
|
868
|
|
|
865
|
|
- f.write('<h1>GitStats - %s</h1>' % data.projectname)
|
|
866
|
|
-
|
|
867
|
869
|
self.printNav(f)
|
|
868
|
870
|
|
|
869
|
|
- f.write('<div><div class="card-body"><dl>')
|
|
870
|
|
- f.write('<dt>Project name</dt><dd>%s</dd>' % (data.projectname))
|
|
871
|
|
- f.write('<dt>Generated</dt><dd>%s (in %d seconds)</dd>' % (datetime.datetime.now().strftime(format), time.time() - data.getStampCreated()))
|
|
872
|
|
- f.write('<dt>Generator</dt><dd><a href="http://gitstats.sourceforge.net/">GitStats</a> (version %s), %s, %s</dd>' % (getversion(), getgitversion(), getgnuplotversion()))
|
|
873
|
|
- f.write('<dt>Report Period</dt><dd>%s to %s</dd>' % (data.getFirstCommitDate().strftime(format), data.getLastCommitDate().strftime(format)))
|
|
874
|
|
- f.write('<dt>Age</dt><dd>%d days, %d active days (%3.2f%%)</dd>' % (data.getCommitDeltaDays(), len(data.getActiveDays()), (100.0 * len(data.getActiveDays()) / data.getCommitDeltaDays())))
|
|
875
|
|
- f.write('<dt>Total Files</dt><dd>%s</dd>' % data.getTotalFiles())
|
|
876
|
|
- f.write('<dt>Total Lines of Code</dt><dd>%s (%d added, %d removed)</dd>' % (data.getTotalLOC(), data.total_lines_added, data.total_lines_removed))
|
|
877
|
|
- f.write('<dt>Total Commits</dt><dd>%s (average %.1f commits per active day, %.1f per all days)</dd>' % (data.getTotalCommits(), float(data.getTotalCommits()) / len(data.getActiveDays()), float(data.getTotalCommits()) / data.getCommitDeltaDays()))
|
|
878
|
|
- f.write('<dt>Authors</dt><dd>%s (average %.1f commits per author)</dd>' % (data.getTotalAuthors(), (1.0 * data.getTotalCommits()) / data.getTotalAuthors()))
|
|
879
|
|
- f.write('</dl></div></div>')
|
|
880
|
|
-
|
|
881
|
|
- f.write('</body>\n</html>')
|
|
|
871
|
+ general_content = ['<h1 class="text-3xl font-bold underline">GitStats - %s</h1>' % data.projectname]
|
|
|
872
|
+
|
|
|
873
|
+ general_content.append('<div><div class="card-body"><dl>')
|
|
|
874
|
+ general_content.append('<dt>Project name</dt><dd>%s (%s branches, %s tags)</dd>' % (data.projectname, data.total_branches, data.total_tags))
|
|
|
875
|
+ general_content.append('<dt>Generated</dt><dd>%s (in %d seconds)</dd>' % (datetime.datetime.now().strftime(format), time.time() - data.getStampCreated()))
|
|
|
876
|
+ general_content.append('<dt>Generator</dt><dd><a href="http://gitstats.sourceforge.net/">GitStats</a> (version %s), %s, %s</dd>' % (getversion(), getgitversion(), getgnuplotversion()))
|
|
|
877
|
+ general_content.append('<dt>Report Period</dt><dd>%s to %s</dd>' % (data.getFirstCommitDate().strftime(format), data.getLastCommitDate().strftime(format)))
|
|
|
878
|
+ general_content.append('<dt>Age</dt><dd>%d days, %d active days (%3.2f%%)</dd>' % (data.getCommitDeltaDays(), len(data.getActiveDays()), (100.0 * len(data.getActiveDays()) / data.getCommitDeltaDays())))
|
|
|
879
|
+ general_content.append('<dt>Total Files</dt><dd>%s</dd>' % data.getTotalFiles())
|
|
|
880
|
+ general_content.append('<dt>Total Lines of Code</dt><dd>%s (%d added, %d removed)</dd>' % (data.getTotalLOC(), data.total_lines_added, data.total_lines_removed))
|
|
|
881
|
+ general_content.append('<dt>Total Commits</dt><dd>%s (average %.1f commits per active day, %.1f per all days)</dd>' % (data.getTotalCommits(), float(data.getTotalCommits()) / len(data.getActiveDays()), float(data.getTotalCommits()) / data.getCommitDeltaDays()))
|
|
|
882
|
+ general_content.append('<dt>Authors</dt><dd>%s (average %.1f commits per author)</dd>' % (data.getTotalAuthors(), (1.0 * data.getTotalCommits()) / data.getTotalAuthors()))
|
|
|
883
|
+ general_content.append('</dl></div></div>')
|
|
|
884
|
+
|
|
|
885
|
+ self.printContent(f, general_content)
|
|
|
886
|
+
|
|
|
887
|
+ f.write('</div></body>\n</html>')
|
|
882
|
888
|
f.close()
|
|
883
|
889
|
|
|
884
|
890
|
###
|
|
885
|
891
|
# Activity
|
|
886
|
892
|
f = open(path + '/activity.html', 'w')
|
|
887
|
893
|
self.printHeader(f)
|
|
888
|
|
- f.write('<h1>Activity</h1>')
|
|
|
894
|
+
|
|
889
|
895
|
self.printNav(f)
|
|
890
|
896
|
|
|
891
|
|
- #f.write('<h2>Last 30 days</h2>')
|
|
|
897
|
+ activity_content = []
|
|
|
898
|
+
|
|
|
899
|
+ #activity_content.append('<h2>Last 30 days</h2>')
|
|
892
|
900
|
|
|
893
|
|
- #f.write('<h2>Last 12 months</h2>')
|
|
|
901
|
+ #activity_content.append('<h2>Last 12 months</h2>')
|
|
894
|
902
|
|
|
895
|
903
|
# Weekly activity
|
|
896
|
904
|
WEEKS = 32
|
|
897
|
|
- f.write(html_header(2, 'Weekly activity'))
|
|
898
|
|
- f.write('<p>Last %d weeks</p>' % WEEKS)
|
|
|
905
|
+ activity_content.append(html_header(2, 'Weekly activity'))
|
|
|
906
|
+ activity_content.append('<p>Last %d weeks</p>' % WEEKS)
|
|
899
|
907
|
|
|
900
|
908
|
# generate weeks to show (previous N weeks from now)
|
|
901
|
909
|
now = datetime.datetime.now()
|
|
|
@@ -907,7 +915,7 @@ class HTMLReportCreator(ReportCreator):
|
|
907
|
915
|
stampcur -= deltaweek
|
|
908
|
916
|
|
|
909
|
917
|
# top row: commits & bar
|
|
910
|
|
- f.write('<div><div class="card-body"><table class="noborders"><tr>')
|
|
|
918
|
+ activity_content.append('<div><div class="card-body"><table class="noborders"><tr>')
|
|
911
|
919
|
for i in range(0, WEEKS):
|
|
912
|
920
|
commits = 0
|
|
913
|
921
|
if weeks[i] in data.activity_by_year_week:
|
|
|
@@ -917,41 +925,41 @@ class HTMLReportCreator(ReportCreator):
|
|
917
|
925
|
if weeks[i] in data.activity_by_year_week:
|
|
918
|
926
|
percentage = float(data.activity_by_year_week[weeks[i]]) / data.activity_by_year_week_peak
|
|
919
|
927
|
height = max(1, int(200 * percentage))
|
|
920
|
|
- f.write('<td style="text-align: center; vertical-align: bottom">%d<div style="display: block; background-color: red; width: 20px; height: %dpx"></div></td>' % (commits, height))
|
|
|
928
|
+ activity_content.append('<td style="text-align: center; vertical-align: bottom">%d<div style="display: block; background-color: red; width: 20px; height: %dpx"></div></td>' % (commits, height))
|
|
921
|
929
|
|
|
922
|
930
|
# bottom row: year/week
|
|
923
|
|
- f.write('</tr><tr>')
|
|
|
931
|
+ activity_content.append('</tr><tr>')
|
|
924
|
932
|
for i in range(0, WEEKS):
|
|
925
|
|
- f.write('<td>%s</td>' % (WEEKS - i))
|
|
926
|
|
- f.write('</tr></table></div></div>')
|
|
|
933
|
+ activity_content.append('<td>%s</td>' % (WEEKS - i))
|
|
|
934
|
+ activity_content.append('</tr></table></div></div>')
|
|
927
|
935
|
|
|
928
|
936
|
# Hour of Day
|
|
929
|
|
- f.write(html_header(2, 'Hour of Day'))
|
|
|
937
|
+ activity_content.append(html_header(2, 'Hour of Day'))
|
|
930
|
938
|
hour_of_day = data.getActivityByHourOfDay()
|
|
931
|
|
- f.write('<div><div class="card-body"><table><tr><th>Hour</th>')
|
|
|
939
|
+ activity_content.append('<div><div class="card-body"><table><tr><th>Hour</th>')
|
|
932
|
940
|
for i in range(0, 24):
|
|
933
|
|
- f.write('<th>%d</th>' % i)
|
|
934
|
|
- f.write('</tr>\n<tr><th>Commits</th>')
|
|
|
941
|
+ activity_content.append('<th>%d</th>' % i)
|
|
|
942
|
+ activity_content.append('</tr>\n<tr><th>Commits</th>')
|
|
935
|
943
|
fp = open(path + '/hour_of_day.dat', 'w')
|
|
936
|
944
|
for i in range(0, 24):
|
|
937
|
945
|
if i in hour_of_day:
|
|
938
|
946
|
r = 127 + int((float(hour_of_day[i]) / data.activity_by_hour_of_day_busiest) * 128)
|
|
939
|
|
- f.write('<td style="background-color: rgb(%d, 0, 0)">%d</td>' % (r, hour_of_day[i]))
|
|
|
947
|
+ activity_content.append('<td style="background-color: rgb(%d, 0, 0)">%d</td>' % (r, hour_of_day[i]))
|
|
940
|
948
|
fp.write('%d %d\n' % (i, hour_of_day[i]))
|
|
941
|
949
|
else:
|
|
942
|
|
- f.write('<td>0</td>')
|
|
|
950
|
+ activity_content.append('<td>0</td>')
|
|
943
|
951
|
fp.write('%d 0\n' % i)
|
|
944
|
952
|
fp.close()
|
|
945
|
|
- f.write('</tr>\n<tr><th>%</th>')
|
|
|
953
|
+ activity_content.append('</tr>\n<tr><th>%</th>')
|
|
946
|
954
|
totalcommits = data.getTotalCommits()
|
|
947
|
955
|
for i in range(0, 24):
|
|
948
|
956
|
if i in hour_of_day:
|
|
949
|
957
|
r = 127 + int((float(hour_of_day[i]) / data.activity_by_hour_of_day_busiest) * 128)
|
|
950
|
|
- f.write('<td style="background-color: rgb(%d, 0, 0)">%.2f</td>' % (r, (100.0 * hour_of_day[i]) / totalcommits))
|
|
|
958
|
+ activity_content.append('<td style="background-color: rgb(%d, 0, 0)">%.2f</td>' % (r, (100.0 * hour_of_day[i]) / totalcommits))
|
|
951
|
959
|
else:
|
|
952
|
|
- f.write('<td>0.00</td>')
|
|
953
|
|
- f.write('</tr></table>')
|
|
954
|
|
- f.write('<img src="hour_of_day.png" alt="Hour of Day">')
|
|
|
960
|
+ activity_content.append('<td>0.00</td>')
|
|
|
961
|
+ activity_content.append('</tr></table>')
|
|
|
962
|
+ activity_content.append('<img src="hour_of_day.png" alt="Hour of Day">')
|
|
955
|
963
|
fg = open(path + '/hour_of_day.dat', 'w')
|
|
956
|
964
|
for i in range(0, 24):
|
|
957
|
965
|
if i in hour_of_day:
|
|
|
@@ -961,106 +969,108 @@ class HTMLReportCreator(ReportCreator):
|
|
961
|
969
|
fg.close()
|
|
962
|
970
|
|
|
963
|
971
|
# Day of Week
|
|
964
|
|
- f.write(html_header(2, 'Day of Week'))
|
|
|
972
|
+ activity_content.append(html_header(2, 'Day of Week'))
|
|
965
|
973
|
day_of_week = data.getActivityByDayOfWeek()
|
|
966
|
|
- f.write('<div class="vtable"><table>')
|
|
967
|
|
- f.write('<tr><th>Day</th><th>Total (%)</th></tr>')
|
|
|
974
|
+ activity_content.append('<div class="vtable"><table>')
|
|
|
975
|
+ activity_content.append('<tr><th>Day</th><th>Total (%)</th></tr>')
|
|
968
|
976
|
fp = open(path + '/day_of_week.dat', 'w')
|
|
969
|
977
|
for d in range(0, 7):
|
|
970
|
978
|
commits = 0
|
|
971
|
979
|
if d in day_of_week:
|
|
972
|
980
|
commits = day_of_week[d]
|
|
973
|
981
|
fp.write('%d %s %d\n' % (d + 1, WEEKDAYS[d], commits))
|
|
974
|
|
- f.write('<tr>')
|
|
975
|
|
- f.write('<th>%s</th>' % (WEEKDAYS[d]))
|
|
|
982
|
+ activity_content.append('<tr>')
|
|
|
983
|
+ activity_content.append('<th>%s</th>' % (WEEKDAYS[d]))
|
|
976
|
984
|
if d in day_of_week:
|
|
977
|
|
- f.write('<td>%d (%.2f%%)</td>' % (day_of_week[d], (100.0 * day_of_week[d]) / totalcommits))
|
|
|
985
|
+ activity_content.append('<td>%d (%.2f%%)</td>' % (day_of_week[d], (100.0 * day_of_week[d]) / totalcommits))
|
|
978
|
986
|
else:
|
|
979
|
|
- f.write('<td>0</td>')
|
|
980
|
|
- f.write('</tr>')
|
|
981
|
|
- f.write('</table></div>')
|
|
982
|
|
- f.write('<img src="day_of_week.png" alt="Day of Week">')
|
|
|
987
|
+ activity_content.append('<td>0</td>')
|
|
|
988
|
+ activity_content.append('</tr>')
|
|
|
989
|
+ activity_content.append('</table></div>')
|
|
|
990
|
+ activity_content.append('<img src="day_of_week.png" alt="Day of Week">')
|
|
983
|
991
|
fp.close()
|
|
984
|
992
|
|
|
985
|
993
|
# Hour of Week
|
|
986
|
|
- f.write(html_header(2, 'Hour of Week'))
|
|
987
|
|
- f.write('<table>')
|
|
|
994
|
+ activity_content.append(html_header(2, 'Hour of Week'))
|
|
|
995
|
+ activity_content.append('<table>')
|
|
988
|
996
|
|
|
989
|
|
- f.write('<tr><th>Weekday</th>')
|
|
|
997
|
+ activity_content.append('<tr><th>Weekday</th>')
|
|
990
|
998
|
for hour in range(0, 24):
|
|
991
|
|
- f.write('<th>%d</th>' % (hour))
|
|
992
|
|
- f.write('</tr>')
|
|
|
999
|
+ activity_content.append('<th>%d</th>' % (hour))
|
|
|
1000
|
+ activity_content.append('</tr>')
|
|
993
|
1001
|
|
|
994
|
1002
|
for weekday in range(0, 7):
|
|
995
|
|
- f.write('<tr><th>%s</th>' % (WEEKDAYS[weekday]))
|
|
|
1003
|
+ activity_content.append('<tr><th>%s</th>' % (WEEKDAYS[weekday]))
|
|
996
|
1004
|
for hour in range(0, 24):
|
|
997
|
1005
|
try:
|
|
998
|
1006
|
commits = data.activity_by_hour_of_week[weekday][hour]
|
|
999
|
1007
|
except KeyError:
|
|
1000
|
1008
|
commits = 0
|
|
1001
|
1009
|
if commits != 0:
|
|
1002
|
|
- f.write('<td')
|
|
|
1010
|
+ activity_content.append('<td')
|
|
1003
|
1011
|
r = 127 + int((float(commits) / data.activity_by_hour_of_week_busiest) * 128)
|
|
1004
|
|
- f.write(' style="background-color: rgb(%d, 0, 0)"' % r)
|
|
1005
|
|
- f.write('>%d</td>' % commits)
|
|
|
1012
|
+ activity_content.append(' style="background-color: rgb(%d, 0, 0)"' % r)
|
|
|
1013
|
+ activity_content.append('>%d</td>' % commits)
|
|
1006
|
1014
|
else:
|
|
1007
|
|
- f.write('<td></td>')
|
|
1008
|
|
- f.write('</tr>')
|
|
|
1015
|
+ activity_content.append('<td></td>')
|
|
|
1016
|
+ activity_content.append('</tr>')
|
|
1009
|
1017
|
|
|
1010
|
|
- f.write('</table></div></div>')
|
|
|
1018
|
+ activity_content.append('</table></div></div>')
|
|
1011
|
1019
|
|
|
1012
|
1020
|
# Month of Year
|
|
1013
|
|
- f.write(html_header(2, 'Month of Year'))
|
|
1014
|
|
- f.write('<div class="vtable"><table>')
|
|
1015
|
|
- f.write('<tr><th>Month</th><th>Commits (%)</th></tr>')
|
|
|
1021
|
+ activity_content.append(html_header(2, 'Month of Year'))
|
|
|
1022
|
+ activity_content.append('<div class="vtable"><table>')
|
|
|
1023
|
+ activity_content.append('<tr><th>Month</th><th>Commits (%)</th></tr>')
|
|
1016
|
1024
|
fp = open (path + '/month_of_year.dat', 'w')
|
|
1017
|
1025
|
for mm in range(1, 13):
|
|
1018
|
1026
|
commits = 0
|
|
1019
|
1027
|
if mm in data.activity_by_month_of_year:
|
|
1020
|
1028
|
commits = data.activity_by_month_of_year[mm]
|
|
1021
|
|
- f.write('<tr><td>%d</td><td>%d (%.2f %%)</td></tr>' % (mm, commits, (100.0 * commits) / data.getTotalCommits()))
|
|
|
1029
|
+ activity_content.append('<tr><td>%d</td><td>%d (%.2f %%)</td></tr>' % (mm, commits, (100.0 * commits) / data.getTotalCommits()))
|
|
1022
|
1030
|
fp.write('%d %d\n' % (mm, commits))
|
|
1023
|
1031
|
fp.close()
|
|
1024
|
|
- f.write('</table></div>')
|
|
1025
|
|
- f.write('<img src="month_of_year.png" alt="Month of Year">')
|
|
|
1032
|
+ activity_content.append('</table></div>')
|
|
|
1033
|
+ activity_content.append('<img src="month_of_year.png" alt="Month of Year">')
|
|
1026
|
1034
|
|
|
1027
|
1035
|
# Commits by year/month
|
|
1028
|
|
- f.write(html_header(2, 'Commits by year/month'))
|
|
1029
|
|
- f.write('<div class="vtable"><table><tr><th>Month</th><th>Commits</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
|
1036
|
+ activity_content.append(html_header(2, 'Commits by year/month'))
|
|
|
1037
|
+ activity_content.append('<div class="vtable"><table><tr><th>Month</th><th>Commits</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
1030
|
1038
|
for yymm in reversed(sorted(data.commits_by_month.keys())):
|
|
1031
|
|
- f.write('<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td></tr>' % (yymm, data.commits_by_month.get(yymm,0), data.lines_added_by_month.get(yymm,0), data.lines_removed_by_month.get(yymm,0)))
|
|
1032
|
|
- f.write('</table></div>')
|
|
1033
|
|
- f.write('<img src="commits_by_year_month.png" alt="Commits by year/month">')
|
|
|
1039
|
+ activity_content.append('<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td></tr>' % (yymm, data.commits_by_month.get(yymm,0), data.lines_added_by_month.get(yymm,0), data.lines_removed_by_month.get(yymm,0)))
|
|
|
1040
|
+ activity_content.append('</table></div>')
|
|
|
1041
|
+ activity_content.append('<img src="commits_by_year_month.png" alt="Commits by year/month">')
|
|
1034
|
1042
|
fg = open(path + '/commits_by_year_month.dat', 'w')
|
|
1035
|
1043
|
for yymm in sorted(data.commits_by_month.keys()):
|
|
1036
|
1044
|
fg.write('%s %s\n' % (yymm, data.commits_by_month[yymm]))
|
|
1037
|
1045
|
fg.close()
|
|
1038
|
1046
|
|
|
1039
|
1047
|
# Commits by year
|
|
1040
|
|
- f.write(html_header(2, 'Commits by Year'))
|
|
1041
|
|
- f.write('<div class="vtable"><table><tr><th>Year</th><th>Commits (% of all)</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
|
1048
|
+ activity_content.append(html_header(2, 'Commits by Year'))
|
|
|
1049
|
+ activity_content.append('<div class="vtable"><table><tr><th>Year</th><th>Commits (% of all)</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
1042
|
1050
|
for yy in reversed(sorted(data.commits_by_year.keys())):
|
|
1043
|
|
- f.write('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td></tr>' % (yy, data.commits_by_year.get(yy,0), (100.0 * data.commits_by_year.get(yy,0)) / data.getTotalCommits(), data.lines_added_by_year.get(yy,0), data.lines_removed_by_year.get(yy,0)))
|
|
1044
|
|
- f.write('</table></div>')
|
|
1045
|
|
- f.write('<img src="commits_by_year.png" alt="Commits by Year">')
|
|
|
1051
|
+ activity_content.append('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td></tr>' % (yy, data.commits_by_year.get(yy,0), (100.0 * data.commits_by_year.get(yy,0)) / data.getTotalCommits(), data.lines_added_by_year.get(yy,0), data.lines_removed_by_year.get(yy,0)))
|
|
|
1052
|
+ activity_content.append('</table></div>')
|
|
|
1053
|
+ activity_content.append('<img src="commits_by_year.png" alt="Commits by Year">')
|
|
1046
|
1054
|
fg = open(path + '/commits_by_year.dat', 'w')
|
|
1047
|
1055
|
for yy in sorted(data.commits_by_year.keys()):
|
|
1048
|
1056
|
fg.write('%d %d\n' % (yy, data.commits_by_year[yy]))
|
|
1049
|
1057
|
fg.close()
|
|
1050
|
1058
|
|
|
1051
|
1059
|
# Commits by timezone
|
|
1052
|
|
- f.write(html_header(2, 'Commits by Timezone'))
|
|
1053
|
|
- f.write('<div><div class="card-body"><table><tr>')
|
|
1054
|
|
- f.write('<th>Timezone</th><th>Commits</th>')
|
|
1055
|
|
- f.write('</tr>')
|
|
|
1060
|
+ activity_content.append(html_header(2, 'Commits by Timezone'))
|
|
|
1061
|
+ activity_content.append('<div><div class="card-body"><table><tr>')
|
|
|
1062
|
+ activity_content.append('<th>Timezone</th><th>Commits</th>')
|
|
|
1063
|
+ activity_content.append('</tr>')
|
|
1056
|
1064
|
max_commits_on_tz = max(data.commits_by_timezone.values())
|
|
1057
|
1065
|
for i in sorted(data.commits_by_timezone.keys(), key = lambda n : int(n)):
|
|
1058
|
1066
|
commits = data.commits_by_timezone[i]
|
|
1059
|
1067
|
r = 127 + int((float(commits) / max_commits_on_tz) * 128)
|
|
1060
|
|
- f.write('<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>' % (i, r, commits))
|
|
1061
|
|
- f.write('</table></div>')
|
|
|
1068
|
+ activity_content.append('<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>' % (i, r, commits))
|
|
|
1069
|
+ activity_content.append('</table></div>')
|
|
1062
|
1070
|
|
|
1063
|
|
- f.write('</body></html>')
|
|
|
1071
|
+ self.printContent(f, activity_content, 'Activity')
|
|
|
1072
|
+
|
|
|
1073
|
+ f.write('</div></body></html>')
|
|
1064
|
1074
|
f.close()
|
|
1065
|
1075
|
|
|
1066
|
1076
|
###
|
|
|
@@ -1068,33 +1078,34 @@ class HTMLReportCreator(ReportCreator):
|
|
1068
|
1078
|
f = open(path + '/authors.html', 'w')
|
|
1069
|
1079
|
self.printHeader(f)
|
|
1070
|
1080
|
|
|
1071
|
|
- f.write('<h1>Authors</h1>')
|
|
1072
|
1081
|
self.printNav(f)
|
|
1073
|
1082
|
|
|
|
1083
|
+ authors_content = []
|
|
|
1084
|
+
|
|
1074
|
1085
|
# Authors :: List of authors
|
|
1075
|
|
- f.write(html_header(2, 'List of Authors'))
|
|
|
1086
|
+ authors_content.append(html_header(2, 'List of Authors'))
|
|
1076
|
1087
|
|
|
1077
|
|
- f.write('<div><div class="card-body"><table class="authors sortable" id="authors">')
|
|
1078
|
|
- f.write('<tr><th>Author</th><th>Commits (%)</th><th>+ lines</th><th>- lines</th><th>First commit</th><th>Last commit</th><th class="unsortable">Age</th><th>Active days</th><th># by commits</th></tr>')
|
|
|
1088
|
+ authors_content.append('<div><div class="card-body"><table class="authors sortable" id="authors">')
|
|
|
1089
|
+ authors_content.append('<tr><th>Author</th><th>Commits (%)</th><th>+ lines</th><th>- lines</th><th>First commit</th><th>Last commit</th><th class="unsortable">Age</th><th>Active days</th><th># by commits</th></tr>')
|
|
1079
|
1090
|
for author in data.getAuthors(conf['max_authors']):
|
|
1080
|
1091
|
info = data.getAuthorInfo(author)
|
|
1081
|
|
- f.write('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%d</td></tr>' % (author, info['commits'], info['commits_frac'], info['lines_added'], info['lines_removed'], info['date_first'], info['date_last'], info['timedelta'], len(info['active_days']), info['place_by_commits']))
|
|
1082
|
|
- f.write('</table></div></div>')
|
|
|
1092
|
+ authors_content.append('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%d</td></tr>' % (author, info['commits'], info['commits_frac'], info['lines_added'], info['lines_removed'], info['date_first'], info['date_last'], info['timedelta'], len(info['active_days']), info['place_by_commits']))
|
|
|
1093
|
+ authors_content.append('</table></div></div>')
|
|
1083
|
1094
|
|
|
1084
|
1095
|
allauthors = data.getAuthors()
|
|
1085
|
1096
|
if len(allauthors) > conf['max_authors']:
|
|
1086
|
1097
|
rest = allauthors[conf['max_authors']:]
|
|
1087
|
|
- f.write('<p class="moreauthors">These didn\'t make it to the top: %s</p>' % ', '.join(rest))
|
|
|
1098
|
+ authors_content.append('<p class="moreauthors">These didn\'t make it to the top: %s</p>' % ', '.join(rest))
|
|
1088
|
1099
|
|
|
1089
|
|
- f.write(html_header(2, 'Cumulated Added Lines of Code per Author'))
|
|
1090
|
|
- f.write('<img src="lines_of_code_by_author.png" alt="Lines of code per Author">')
|
|
|
1100
|
+ authors_content.append(html_header(2, 'Cumulated Added Lines of Code per Author'))
|
|
|
1101
|
+ authors_content.append('<img src="lines_of_code_by_author.png" alt="Lines of code per Author">')
|
|
1091
|
1102
|
if len(allauthors) > conf['max_authors']:
|
|
1092
|
|
- f.write('<p class="moreauthors">Only top %d authors shown</p>' % conf['max_authors'])
|
|
|
1103
|
+ authors_content.append('<p class="moreauthors">Only top %d authors shown</p>' % conf['max_authors'])
|
|
1093
|
1104
|
|
|
1094
|
|
- f.write(html_header(2, 'Commits per Author'))
|
|
1095
|
|
- f.write('<img src="commits_by_author.png" alt="Commits per Author">')
|
|
|
1105
|
+ authors_content.append(html_header(2, 'Commits per Author'))
|
|
|
1106
|
+ authors_content.append('<img src="commits_by_author.png" alt="Commits per Author">')
|
|
1096
|
1107
|
if len(allauthors) > conf['max_authors']:
|
|
1097
|
|
- f.write('<p class="moreauthors">Only top %d authors shown</p>' % conf['max_authors'])
|
|
|
1108
|
+ authors_content.append('<p class="moreauthors">Only top %d authors shown</p>' % conf['max_authors'])
|
|
1098
|
1109
|
|
|
1099
|
1110
|
fgl = open(path + '/lines_of_code_by_author.dat', 'w')
|
|
1100
|
1111
|
fgc = open(path + '/commits_by_author.dat', 'w')
|
|
|
@@ -1129,36 +1140,36 @@ class HTMLReportCreator(ReportCreator):
|
|
1129
|
1140
|
fgc.close()
|
|
1130
|
1141
|
|
|
1131
|
1142
|
# Authors :: Author of Month
|
|
1132
|
|
- f.write(html_header(2, 'Author of Month'))
|
|
1133
|
|
- f.write('<table class="sortable" id="aom">')
|
|
1134
|
|
- f.write('<tr><th>Month</th><th>Author</th><th>Commits (%%)</th><th class="unsortable">Next top %d</th><th>Number of authors</th></tr>' % conf['authors_top'])
|
|
|
1143
|
+ authors_content.append(html_header(2, 'Author of Month'))
|
|
|
1144
|
+ authors_content.append('<table class="sortable" id="aom">')
|
|
|
1145
|
+ authors_content.append('<tr><th>Month</th><th>Author</th><th>Commits (%%)</th><th class="unsortable">Next top %d</th><th>Number of authors</th></tr>' % conf['authors_top'])
|
|
1135
|
1146
|
for yymm in reversed(sorted(data.author_of_month.keys())):
|
|
1136
|
1147
|
authordict = data.author_of_month[yymm]
|
|
1137
|
1148
|
authors = getkeyssortedbyvalues(authordict)
|
|
1138
|
1149
|
authors.reverse()
|
|
1139
|
1150
|
commits = data.author_of_month[yymm][authors[0]]
|
|
1140
|
1151
|
next = ', '.join(authors[1:conf['authors_top']+1])
|
|
1141
|
|
- f.write('<tr><td>%s</td><td>%s</td><td>%d (%.2f%% of %d)</td><td>%s</td><td>%d</td></tr>' % (yymm, authors[0], commits, (100.0 * commits) / data.commits_by_month[yymm], data.commits_by_month[yymm], next, len(authors)))
|
|
|
1152
|
+ authors_content.append('<tr><td>%s</td><td>%s</td><td>%d (%.2f%% of %d)</td><td>%s</td><td>%d</td></tr>' % (yymm, authors[0], commits, (100.0 * commits) / data.commits_by_month[yymm], data.commits_by_month[yymm], next, len(authors)))
|
|
1142
|
1153
|
|
|
1143
|
|
- f.write('</table>')
|
|
|
1154
|
+ authors_content.append('</table>')
|
|
1144
|
1155
|
|
|
1145
|
|
- f.write(html_header(2, 'Author of Year'))
|
|
1146
|
|
- f.write('<table class="sortable" id="aoy"><tr><th>Year</th><th>Author</th><th>Commits (%%)</th><th class="unsortable">Next top %d</th><th>Number of authors</th></tr>' % conf['authors_top'])
|
|
|
1156
|
+ authors_content.append(html_header(2, 'Author of Year'))
|
|
|
1157
|
+ authors_content.append('<table class="sortable" id="aoy"><tr><th>Year</th><th>Author</th><th>Commits (%%)</th><th class="unsortable">Next top %d</th><th>Number of authors</th></tr>' % conf['authors_top'])
|
|
1147
|
1158
|
for yy in reversed(sorted(data.author_of_year.keys())):
|
|
1148
|
1159
|
authordict = data.author_of_year[yy]
|
|
1149
|
1160
|
authors = getkeyssortedbyvalues(authordict)
|
|
1150
|
1161
|
authors.reverse()
|
|
1151
|
1162
|
commits = data.author_of_year[yy][authors[0]]
|
|
1152
|
1163
|
next = ', '.join(authors[1:conf['authors_top']+1])
|
|
1153
|
|
- f.write('<tr><td>%s</td><td>%s</td><td>%d (%.2f%% of %d)</td><td>%s</td><td>%d</td></tr>' % (yy, authors[0], commits, (100.0 * commits) / data.commits_by_year[yy], data.commits_by_year[yy], next, len(authors)))
|
|
1154
|
|
- f.write('</table>')
|
|
|
1164
|
+ authors_content.append('<tr><td>%s</td><td>%s</td><td>%d (%.2f%% of %d)</td><td>%s</td><td>%d</td></tr>' % (yy, authors[0], commits, (100.0 * commits) / data.commits_by_year[yy], data.commits_by_year[yy], next, len(authors)))
|
|
|
1165
|
+ authors_content.append('</table>')
|
|
1155
|
1166
|
|
|
1156
|
1167
|
# Domains
|
|
1157
|
|
- f.write(html_header(2, 'Commits by Domains'))
|
|
|
1168
|
+ authors_content.append(html_header(2, 'Commits by Domains'))
|
|
1158
|
1169
|
domains_by_commits = getkeyssortedbyvaluekey(data.domains, 'commits')
|
|
1159
|
1170
|
domains_by_commits.reverse() # most first
|
|
1160
|
|
- f.write('<div class="vtable"><table>')
|
|
1161
|
|
- f.write('<tr><th>Domains</th><th>Total (%)</th></tr>')
|
|
|
1171
|
+ authors_content.append('<div class="vtable"><table>')
|
|
|
1172
|
+ authors_content.append('<tr><th>Domains</th><th>Total (%)</th></tr>')
|
|
1162
|
1173
|
fp = open(path + '/domains.dat', 'w')
|
|
1163
|
1174
|
n = 0
|
|
1164
|
1175
|
for domain in domains_by_commits:
|
|
|
@@ -1168,32 +1179,36 @@ class HTMLReportCreator(ReportCreator):
|
|
1168
|
1179
|
n += 1
|
|
1169
|
1180
|
info = data.getDomainInfo(domain)
|
|
1170
|
1181
|
fp.write('%s %d %d\n' % (domain, n , info['commits']))
|
|
1171
|
|
- f.write('<tr><th>%s</th><td>%d (%.2f%%)</td></tr>' % (domain, info['commits'], (100.0 * info['commits'] / totalcommits)))
|
|
1172
|
|
- f.write('</table></div>')
|
|
1173
|
|
- f.write('<img src="domains.png" alt="Commits by Domains">')
|
|
|
1182
|
+ authors_content.append('<tr><th>%s</th><td>%d (%.2f%%)</td></tr>' % (domain, info['commits'], (100.0 * info['commits'] / totalcommits)))
|
|
|
1183
|
+ authors_content.append('</table></div>')
|
|
|
1184
|
+ authors_content.append('<img src="domains.png" alt="Commits by Domains">')
|
|
1174
|
1185
|
fp.close()
|
|
1175
|
1186
|
|
|
1176
|
|
- f.write('</body></html>')
|
|
|
1187
|
+ self.printContent(f, authors_content, 'Authors')
|
|
|
1188
|
+ f.write('</div></body></html>')
|
|
1177
|
1189
|
f.close()
|
|
1178
|
1190
|
|
|
1179
|
1191
|
###
|
|
1180
|
1192
|
# Files
|
|
1181
|
1193
|
f = open(path + '/files.html', 'w')
|
|
1182
|
1194
|
self.printHeader(f)
|
|
1183
|
|
- f.write('<h1>Files</h1>')
|
|
|
1195
|
+
|
|
1184
|
1196
|
self.printNav(f)
|
|
1185
|
1197
|
|
|
1186
|
|
- f.write('<dl>\n')
|
|
1187
|
|
- f.write('<dt>Total files</dt><dd>%d</dd>' % data.getTotalFiles())
|
|
1188
|
|
- f.write('<dt>Total lines</dt><dd>%d</dd>' % data.getTotalLOC())
|
|
|
1198
|
+
|
|
|
1199
|
+ files_content = []
|
|
|
1200
|
+
|
|
|
1201
|
+ files_content.append('<dl>\n')
|
|
|
1202
|
+ files_content.append('<dt>Total files</dt><dd>%d</dd>' % data.getTotalFiles())
|
|
|
1203
|
+ files_content.append('<dt>Total lines</dt><dd>%d</dd>' % data.getTotalLOC())
|
|
1189
|
1204
|
try:
|
|
1190
|
|
- f.write('<dt>Average file size</dt><dd>%.2f bytes</dd>' % (float(data.getTotalSize()) / data.getTotalFiles()))
|
|
|
1205
|
+ files_content.append('<dt>Average file size</dt><dd>%.2f bytes</dd>' % (float(data.getTotalSize()) / data.getTotalFiles()))
|
|
1191
|
1206
|
except ZeroDivisionError:
|
|
1192
|
1207
|
pass
|
|
1193
|
|
- f.write('</dl>\n')
|
|
|
1208
|
+ files_content.append('</dl>\n')
|
|
1194
|
1209
|
|
|
1195
|
1210
|
# Files :: File count by date
|
|
1196
|
|
- f.write(html_header(2, 'File count by date'))
|
|
|
1211
|
+ files_content.append(html_header(2, 'File count by date'))
|
|
1197
|
1212
|
|
|
1198
|
1213
|
# use set to get rid of duplicate/unnecessary entries
|
|
1199
|
1214
|
files_by_date = set()
|
|
|
@@ -1207,13 +1222,13 @@ class HTMLReportCreator(ReportCreator):
|
|
1207
|
1222
|
# fg.write('%s %d\n' % (datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d'), data.files_by_stamp[stamp]))
|
|
1208
|
1223
|
fg.close()
|
|
1209
|
1224
|
|
|
1210
|
|
- f.write('<img src="files_by_date.png" alt="Files by Date">')
|
|
|
1225
|
+ files_content.append('<img src="files_by_date.png" alt="Files by Date">')
|
|
1211
|
1226
|
|
|
1212
|
|
- #f.write('<h2>Average file size by date</h2>')
|
|
|
1227
|
+ #files_content.append('<h2>Average file size by date</h2>')
|
|
1213
|
1228
|
|
|
1214
|
1229
|
# Files :: Extensions
|
|
1215
|
|
- f.write(html_header(2, 'Extensions'))
|
|
1216
|
|
- f.write('<table class="sortable" id="ext"><tr><th>Extension</th><th>Files (%)</th><th>Lines (%)</th><th>Lines/file</th></tr>')
|
|
|
1230
|
+ files_content.append(html_header(2, 'Extensions'))
|
|
|
1231
|
+ files_content.append('<table class="sortable" id="ext"><tr><th>Extension</th><th>Files (%)</th><th>Lines (%)</th><th>Lines/file</th></tr>')
|
|
1217
|
1232
|
for ext in sorted(data.extensions.keys()):
|
|
1218
|
1233
|
files = data.extensions[ext]['files']
|
|
1219
|
1234
|
lines = data.extensions[ext]['lines']
|
|
|
@@ -1221,25 +1236,28 @@ class HTMLReportCreator(ReportCreator):
|
|
1221
|
1236
|
loc_percentage = (100.0 * lines) / data.getTotalLOC()
|
|
1222
|
1237
|
except ZeroDivisionError:
|
|
1223
|
1238
|
loc_percentage = 0
|
|
1224
|
|
- f.write('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d (%.2f%%)</td><td>%d</td></tr>' % (ext, files, (100.0 * files) / data.getTotalFiles(), lines, loc_percentage, lines / files))
|
|
1225
|
|
- f.write('</table>')
|
|
|
1239
|
+ files_content.append('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d (%.2f%%)</td><td>%d</td></tr>' % (ext, files, (100.0 * files) / data.getTotalFiles(), lines, loc_percentage, lines / files))
|
|
|
1240
|
+ files_content.append('</table>')
|
|
1226
|
1241
|
|
|
1227
|
|
- f.write('</body></html>')
|
|
|
1242
|
+ self.printContent(f, files_content, 'Files')
|
|
|
1243
|
+
|
|
|
1244
|
+ f.write('</div></body></html>')
|
|
1228
|
1245
|
f.close()
|
|
1229
|
1246
|
|
|
1230
|
1247
|
###
|
|
1231
|
1248
|
# Lines
|
|
1232
|
1249
|
f = open(path + '/lines.html', 'w')
|
|
1233
|
1250
|
self.printHeader(f)
|
|
1234
|
|
- f.write('<h1>Lines</h1>')
|
|
|
1251
|
+
|
|
1235
|
1252
|
self.printNav(f)
|
|
|
1253
|
+ lines_content=[]
|
|
1236
|
1254
|
|
|
1237
|
|
- f.write('<dl>\n')
|
|
1238
|
|
- f.write('<dt>Total lines</dt><dd>%d</dd>' % data.getTotalLOC())
|
|
1239
|
|
- f.write('</dl>\n')
|
|
|
1255
|
+ lines_content.append('<dl>\n')
|
|
|
1256
|
+ lines_content.append('<dt>Total lines</dt><dd>%d</dd>' % data.getTotalLOC())
|
|
|
1257
|
+ lines_content.append('</dl>\n')
|
|
1240
|
1258
|
|
|
1241
|
|
- f.write(html_header(2, 'Lines of Code'))
|
|
1242
|
|
- f.write('<img src="lines_of_code.png" alt="Lines of Code">')
|
|
|
1259
|
+ lines_content.append(html_header(2, 'Lines of Code'))
|
|
|
1260
|
+ lines_content.append('<img src="lines_of_code.png" alt="Lines of Code">')
|
|
1243
|
1261
|
|
|
1244
|
1262
|
fg = open(path + '/lines_of_code.dat', 'w')
|
|
1245
|
1263
|
for stamp in sorted(data.changes_by_date.keys()):
|
|
|
@@ -1248,8 +1266,8 @@ class HTMLReportCreator(ReportCreator):
|
|
1248
|
1266
|
|
|
1249
|
1267
|
# Weekly activity
|
|
1250
|
1268
|
WEEKS = 32
|
|
1251
|
|
- f.write(html_header(2, 'Weekly activity'))
|
|
1252
|
|
- f.write('<p>Last %d weeks</p>' % WEEKS)
|
|
|
1269
|
+ lines_content.append(html_header(2, 'Weekly activity'))
|
|
|
1270
|
+ lines_content.append('<p>Last %d weeks</p>' % WEEKS)
|
|
1253
|
1271
|
|
|
1254
|
1272
|
# generate weeks to show (previous N weeks from now)
|
|
1255
|
1273
|
now = datetime.datetime.now()
|
|
|
@@ -1261,7 +1279,7 @@ class HTMLReportCreator(ReportCreator):
|
|
1261
|
1279
|
stampcur -= deltaweek
|
|
1262
|
1280
|
|
|
1263
|
1281
|
# top row: commits & bar
|
|
1264
|
|
- f.write('<table class="noborders"><tr>')
|
|
|
1282
|
+ lines_content.append('<table class="noborders"><tr>')
|
|
1265
|
1283
|
for i in range(0, WEEKS):
|
|
1266
|
1284
|
commits = 0
|
|
1267
|
1285
|
if weeks[i] in data.lineactivity_by_year_week:
|
|
|
@@ -1271,41 +1289,41 @@ class HTMLReportCreator(ReportCreator):
|
|
1271
|
1289
|
if weeks[i] in data.lineactivity_by_year_week:
|
|
1272
|
1290
|
percentage = float(data.lineactivity_by_year_week[weeks[i]]) / data.lineactivity_by_year_week_peak
|
|
1273
|
1291
|
height = max(1, int(200 * percentage))
|
|
1274
|
|
- f.write('<td style="text-align: center; vertical-align: bottom">%d<div style="display: block; background-color: red; width: 20px; height: %dpx"></div></td>' % (commits, height))
|
|
|
1292
|
+ lines_content.append('<td style="text-align: center; vertical-align: bottom">%d<div style="display: block; background-color: red; width: 20px; height: %dpx"></div></td>' % (commits, height))
|
|
1275
|
1293
|
|
|
1276
|
1294
|
# bottom row: year/week
|
|
1277
|
|
- f.write('</tr><tr>')
|
|
|
1295
|
+ lines_content.append('</tr><tr>')
|
|
1278
|
1296
|
for i in range(0, WEEKS):
|
|
1279
|
|
- f.write('<td>%s</td>' % (WEEKS - i))
|
|
1280
|
|
- f.write('</tr></table>')
|
|
|
1297
|
+ lines_content.append('<td>%s</td>' % (WEEKS - i))
|
|
|
1298
|
+ lines_content.append('</tr></table>')
|
|
1281
|
1299
|
|
|
1282
|
1300
|
# Hour of Day
|
|
1283
|
|
- f.write(html_header(2, 'Hour of Day'))
|
|
|
1301
|
+ lines_content.append(html_header(2, 'Hour of Day'))
|
|
1284
|
1302
|
hour_of_day = data.getLineActivityByHourOfDay()
|
|
1285
|
|
- f.write('<table><tr><th>Hour</th>')
|
|
|
1303
|
+ lines_content.append('<table><tr><th>Hour</th>')
|
|
1286
|
1304
|
for i in range(0, 24):
|
|
1287
|
|
- f.write('<th>%d</th>' % i)
|
|
1288
|
|
- f.write('</tr>\n<tr><th>Lines</th>')
|
|
|
1305
|
+ lines_content.append('<th>%d</th>' % i)
|
|
|
1306
|
+ lines_content.append('</tr>\n<tr><th>Lines</th>')
|
|
1289
|
1307
|
fp = open(path + '/line_hour_of_day.dat', 'w')
|
|
1290
|
1308
|
for i in range(0, 24):
|
|
1291
|
1309
|
if i in hour_of_day:
|
|
1292
|
1310
|
r = 127 + int((float(hour_of_day[i]) / data.lineactivity_by_hour_of_day_busiest) * 128)
|
|
1293
|
|
- f.write('<td style="background-color: rgb(%d, 0, 0)">%d</td>' % (r, hour_of_day[i]))
|
|
|
1311
|
+ lines_content.append('<td style="background-color: rgb(%d, 0, 0)">%d</td>' % (r, hour_of_day[i]))
|
|
1294
|
1312
|
fp.write('%d %d\n' % (i, hour_of_day[i]))
|
|
1295
|
1313
|
else:
|
|
1296
|
|
- f.write('<td>0</td>')
|
|
|
1314
|
+ lines_content.append('<td>0</td>')
|
|
1297
|
1315
|
fp.write('%d 0\n' % i)
|
|
1298
|
1316
|
fp.close()
|
|
1299
|
|
- f.write('</tr>\n<tr><th>%</th>')
|
|
|
1317
|
+ lines_content.append('</tr>\n<tr><th>%</th>')
|
|
1300
|
1318
|
totallines = data.getTotalLines()
|
|
1301
|
1319
|
for i in range(0, 24):
|
|
1302
|
1320
|
if i in hour_of_day:
|
|
1303
|
1321
|
r = 127 + int((float(hour_of_day[i]) / data.lineactivity_by_hour_of_day_busiest) * 128)
|
|
1304
|
|
- f.write('<td style="background-color: rgb(%d, 0, 0)">%.2f</td>' % (r, (100.0 * hour_of_day[i]) / totallines))
|
|
|
1322
|
+ lines_content.append('<td style="background-color: rgb(%d, 0, 0)">%.2f</td>' % (r, (100.0 * hour_of_day[i]) / totallines))
|
|
1305
|
1323
|
else:
|
|
1306
|
|
- f.write('<td>0.00</td>')
|
|
1307
|
|
- f.write('</tr></table>')
|
|
1308
|
|
- f.write('<img src="line_hour_of_day.png" alt="Hour of Day" />')
|
|
|
1324
|
+ lines_content.append('<td>0.00</td>')
|
|
|
1325
|
+ lines_content.append('</tr></table>')
|
|
|
1326
|
+ lines_content.append('<img src="line_hour_of_day.png" alt="Hour of Day" />')
|
|
1309
|
1327
|
fg = open(path + '/line_hour_of_day.dat', 'w')
|
|
1310
|
1328
|
for i in range(0, 24):
|
|
1311
|
1329
|
if i in hour_of_day:
|
|
|
@@ -1315,122 +1333,127 @@ class HTMLReportCreator(ReportCreator):
|
|
1315
|
1333
|
fg.close()
|
|
1316
|
1334
|
|
|
1317
|
1335
|
# Day of Week
|
|
1318
|
|
- f.write(html_header(2, 'Day of Week'))
|
|
|
1336
|
+ lines_content.append(html_header(2, 'Day of Week'))
|
|
1319
|
1337
|
day_of_week = data.getLineActivityByDayOfWeek()
|
|
1320
|
|
- f.write('<div class="vtable"><table>')
|
|
1321
|
|
- f.write('<tr><th>Day</th><th>Total (%)</th></tr>')
|
|
|
1338
|
+ lines_content.append('<div class="vtable"><table>')
|
|
|
1339
|
+ lines_content.append('<tr><th>Day</th><th>Total (%)</th></tr>')
|
|
1322
|
1340
|
fp = open(path + '/line_day_of_week.dat', 'w')
|
|
1323
|
1341
|
for d in range(0, 7):
|
|
1324
|
1342
|
commits = 0
|
|
1325
|
1343
|
if d in day_of_week:
|
|
1326
|
1344
|
commits = day_of_week[d]
|
|
1327
|
1345
|
fp.write('%d %s %d\n' % (d + 1, WEEKDAYS[d], commits))
|
|
1328
|
|
- f.write('<tr>')
|
|
1329
|
|
- f.write('<th>%s</th>' % (WEEKDAYS[d]))
|
|
|
1346
|
+ lines_content.append('<tr>')
|
|
|
1347
|
+ lines_content.append('<th>%s</th>' % (WEEKDAYS[d]))
|
|
1330
|
1348
|
if d in day_of_week:
|
|
1331
|
|
- f.write('<td>%d (%.2f%%)</td>' % (day_of_week[d], (100.0 * day_of_week[d]) / totallines))
|
|
|
1349
|
+ lines_content.append('<td>%d (%.2f%%)</td>' % (day_of_week[d], (100.0 * day_of_week[d]) / totallines))
|
|
1332
|
1350
|
else:
|
|
1333
|
|
- f.write('<td>0</td>')
|
|
1334
|
|
- f.write('</tr>')
|
|
1335
|
|
- f.write('</table></div>')
|
|
1336
|
|
- f.write('<img src="line_day_of_week.png" alt="Day of Week" />')
|
|
|
1351
|
+ lines_content.append('<td>0</td>')
|
|
|
1352
|
+ lines_content.append('</tr>')
|
|
|
1353
|
+ lines_content.append('</table></div>')
|
|
|
1354
|
+ lines_content.append('<img src="line_day_of_week.png" alt="Day of Week" />')
|
|
1337
|
1355
|
fp.close()
|
|
1338
|
1356
|
|
|
1339
|
1357
|
# Hour of Week
|
|
1340
|
|
- f.write(html_header(2, 'Hour of Week'))
|
|
1341
|
|
- f.write('<table>')
|
|
|
1358
|
+ lines_content.append(html_header(2, 'Hour of Week'))
|
|
|
1359
|
+ lines_content.append('<table>')
|
|
1342
|
1360
|
|
|
1343
|
|
- f.write('<tr><th>Weekday</th>')
|
|
|
1361
|
+ lines_content.append('<tr><th>Weekday</th>')
|
|
1344
|
1362
|
for hour in range(0, 24):
|
|
1345
|
|
- f.write('<th>%d</th>' % (hour))
|
|
1346
|
|
- f.write('</tr>')
|
|
|
1363
|
+ lines_content.append('<th>%d</th>' % (hour))
|
|
|
1364
|
+ lines_content.append('</tr>')
|
|
1347
|
1365
|
|
|
1348
|
1366
|
for weekday in range(0, 7):
|
|
1349
|
|
- f.write('<tr><th>%s</th>' % (WEEKDAYS[weekday]))
|
|
|
1367
|
+ lines_content.append('<tr><th>%s</th>' % (WEEKDAYS[weekday]))
|
|
1350
|
1368
|
for hour in range(0, 24):
|
|
1351
|
1369
|
try:
|
|
1352
|
1370
|
commits = data.lineactivity_by_hour_of_week[weekday][hour]
|
|
1353
|
1371
|
except KeyError:
|
|
1354
|
1372
|
commits = 0
|
|
1355
|
1373
|
if commits != 0:
|
|
1356
|
|
- f.write('<td')
|
|
|
1374
|
+ lines_content.append('<td')
|
|
1357
|
1375
|
r = 127 + int((float(commits) / data.lineactivity_by_hour_of_week_busiest) * 128)
|
|
1358
|
|
- f.write(' style="background-color: rgb(%d, 0, 0)"' % r)
|
|
1359
|
|
- f.write('>%d</td>' % commits)
|
|
|
1376
|
+ lines_content.append(' style="background-color: rgb(%d, 0, 0)"' % r)
|
|
|
1377
|
+ lines_content.append('>%d</td>' % commits)
|
|
1360
|
1378
|
else:
|
|
1361
|
|
- f.write('<td></td>')
|
|
1362
|
|
- f.write('</tr>')
|
|
|
1379
|
+ lines_content.append('<td></td>')
|
|
|
1380
|
+ lines_content.append('</tr>')
|
|
1363
|
1381
|
|
|
1364
|
|
- f.write('</table>')
|
|
|
1382
|
+ lines_content.append('</table>')
|
|
1365
|
1383
|
|
|
1366
|
1384
|
# Month of Year
|
|
1367
|
|
- f.write(html_header(2, 'Month of Year'))
|
|
1368
|
|
- f.write('<div class="vtable"><table>')
|
|
1369
|
|
- f.write('<tr><th>Month</th><th>Lines (%)</th></tr>')
|
|
|
1385
|
+ lines_content.append(html_header(2, 'Month of Year'))
|
|
|
1386
|
+ lines_content.append('<div class="vtable"><table>')
|
|
|
1387
|
+ lines_content.append('<tr><th>Month</th><th>Lines (%)</th></tr>')
|
|
1370
|
1388
|
fp = open (path + '/line_month_of_year.dat', 'w')
|
|
1371
|
1389
|
for mm in range(1, 13):
|
|
1372
|
1390
|
commits = 0
|
|
1373
|
1391
|
if mm in data.lineactivity_by_month_of_year:
|
|
1374
|
1392
|
commits = data.lineactivity_by_month_of_year[mm]
|
|
1375
|
|
- f.write('<tr><td>%d</td><td>%d (%.2f %%)</td></tr>' % (mm, commits, (100.0 * commits) / data.getTotalLines()))
|
|
|
1393
|
+ lines_content.append('<tr><td>%d</td><td>%d (%.2f %%)</td></tr>' % (mm, commits, (100.0 * commits) / data.getTotalLines()))
|
|
1376
|
1394
|
fp.write('%d %d\n' % (mm, commits))
|
|
1377
|
1395
|
fp.close()
|
|
1378
|
|
- f.write('</table></div>')
|
|
1379
|
|
- f.write('<img src="line_month_of_year.png" alt="Month of Year" />')
|
|
|
1396
|
+ lines_content.append('</table></div>')
|
|
|
1397
|
+ lines_content.append('<img src="line_month_of_year.png" alt="Month of Year" />')
|
|
1380
|
1398
|
|
|
1381
|
1399
|
# Lines by year/month
|
|
1382
|
|
- f.write(html_header(2, 'Lines by year/month'))
|
|
1383
|
|
- f.write('<div class="vtable"><table><tr><th>Month</th><th>Commits</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
|
1400
|
+ lines_content.append(html_header(2, 'Lines by year/month'))
|
|
|
1401
|
+ lines_content.append('<div class="vtable"><table><tr><th>Month</th><th>Commits</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
1384
|
1402
|
for yymm in reversed(sorted(data.commits_by_month.keys())):
|
|
1385
|
|
- f.write('<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td></tr>' % (yymm, data.commits_by_month.get(yymm,0), data.lines_added_by_month.get(yymm,0), data.lines_removed_by_month.get(yymm,0)))
|
|
1386
|
|
- f.write('</table></div>')
|
|
1387
|
|
- f.write('<img src="line_commits_by_year_month.png" alt="Commits by year/month" />')
|
|
|
1403
|
+ lines_content.append('<tr><td>%s</td><td>%d</td><td>%d</td><td>%d</td></tr>' % (yymm, data.commits_by_month.get(yymm,0), data.lines_added_by_month.get(yymm,0), data.lines_removed_by_month.get(yymm,0)))
|
|
|
1404
|
+ lines_content.append('</table></div>')
|
|
|
1405
|
+ lines_content.append('<img src="line_commits_by_year_month.png" alt="Commits by year/month" />')
|
|
1388
|
1406
|
fg = open(path + '/line_commits_by_year_month.dat', 'w')
|
|
1389
|
1407
|
for yymm in sorted(data.commits_by_month.keys()):
|
|
1390
|
1408
|
fg.write('%s %s\n' % (yymm, data.lines_added_by_month.get(yymm, 0) + data.lines_removed_by_month.get(yymm, 0)))
|
|
1391
|
1409
|
fg.close()
|
|
1392
|
1410
|
|
|
1393
|
1411
|
# Lines by year
|
|
1394
|
|
- f.write(html_header(2, 'Lines by Year'))
|
|
1395
|
|
- f.write('<div class="vtable"><table><tr><th>Year</th><th>Commits (% of all)</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
|
1412
|
+ lines_content.append(html_header(2, 'Lines by Year'))
|
|
|
1413
|
+ lines_content.append('<div class="vtable"><table><tr><th>Year</th><th>Commits (% of all)</th><th>Lines added</th><th>Lines removed</th></tr>')
|
|
1396
|
1414
|
for yy in reversed(sorted(data.commits_by_year.keys())):
|
|
1397
|
|
- f.write('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td></tr>' % (yy, data.commits_by_year.get(yy,0), (100.0 * data.commits_by_year.get(yy,0)) / data.getTotalCommits(), data.lines_added_by_year.get(yy,0), data.lines_removed_by_year.get(yy,0)))
|
|
1398
|
|
- f.write('</table></div>')
|
|
1399
|
|
- f.write('<img src="line_commits_by_year.png" alt="Commits by Year" />')
|
|
|
1415
|
+ lines_content.append('<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d</td><td>%d</td></tr>' % (yy, data.commits_by_year.get(yy,0), (100.0 * data.commits_by_year.get(yy,0)) / data.getTotalCommits(), data.lines_added_by_year.get(yy,0), data.lines_removed_by_year.get(yy,0)))
|
|
|
1416
|
+ lines_content.append('</table></div>')
|
|
|
1417
|
+ lines_content.append('<img src="line_commits_by_year.png" alt="Commits by Year" />')
|
|
1400
|
1418
|
fg = open(path + '/line_commits_by_year.dat', 'w')
|
|
1401
|
1419
|
for yy in sorted(data.commits_by_year.keys()):
|
|
1402
|
1420
|
fg.write('%d %d\n' % (yy, data.lines_added_by_year.get(yy,0) + data.lines_removed_by_year.get(yy,0)))
|
|
1403
|
1421
|
fg.close()
|
|
1404
|
1422
|
|
|
1405
|
1423
|
# Commits by timezone
|
|
1406
|
|
- f.write(html_header(2, 'Commits by Timezone'))
|
|
1407
|
|
- f.write('<table><tr>')
|
|
1408
|
|
- f.write('<th>Timezone</th><th>Commits</th>')
|
|
|
1424
|
+ lines_content.append(html_header(2, 'Commits by Timezone'))
|
|
|
1425
|
+ lines_content.append('<table><tr>')
|
|
|
1426
|
+ lines_content.append('<th>Timezone</th><th>Commits</th>')
|
|
1409
|
1427
|
max_commits_on_tz = max(data.commits_by_timezone.values())
|
|
1410
|
1428
|
for i in sorted(data.commits_by_timezone.keys(), key = lambda n : int(n)):
|
|
1411
|
1429
|
commits = data.commits_by_timezone[i]
|
|
1412
|
1430
|
r = 127 + int((float(commits) / max_commits_on_tz) * 128)
|
|
1413
|
|
- f.write('<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>' % (i, r, commits))
|
|
1414
|
|
- f.write('</tr></table>')
|
|
|
1431
|
+ lines_content.append('<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>' % (i, r, commits))
|
|
|
1432
|
+ lines_content.append('</tr></table>')
|
|
|
1433
|
+
|
|
|
1434
|
+ self.printContent(f, lines_content, 'Lines')
|
|
1415
|
1435
|
|
|
1416
|
|
- f.write('</body></html>')
|
|
|
1436
|
+ f.write('</div></body></html>')
|
|
1417
|
1437
|
f.close()
|
|
1418
|
1438
|
|
|
1419
|
1439
|
###
|
|
1420
|
1440
|
# tags.html
|
|
1421
|
1441
|
f = open(path + '/tags.html', 'w')
|
|
1422
|
1442
|
self.printHeader(f)
|
|
1423
|
|
- f.write('<h1>Tags</h1>')
|
|
|
1443
|
+
|
|
1424
|
1444
|
self.printNav(f)
|
|
1425
|
1445
|
|
|
1426
|
|
- f.write('<dl>')
|
|
1427
|
|
- f.write('<dt>Total tags</dt><dd>%d</dd>' % len(data.tags))
|
|
|
1446
|
+ tags_content = []
|
|
|
1447
|
+
|
|
|
1448
|
+
|
|
|
1449
|
+ tags_content.append('<dl>')
|
|
|
1450
|
+ tags_content.append('<dt>Total tags</dt><dd>%d</dd>' % len(data.tags))
|
|
1428
|
1451
|
if len(data.tags) > 0:
|
|
1429
|
|
- f.write('<dt>Average commits per tag</dt><dd>%.2f</dd>' % (1.0 * data.getTotalCommits() / len(data.tags)))
|
|
1430
|
|
- f.write('</dl>')
|
|
|
1452
|
+ tags_content.append('<dt>Average commits per tag</dt><dd>%.2f</dd>' % (1.0 * data.getTotalCommits() / len(data.tags)))
|
|
|
1453
|
+ tags_content.append('</dl>')
|
|
1431
|
1454
|
|
|
1432
|
|
- f.write('<table class="tags">')
|
|
1433
|
|
- f.write('<tr><th>Name</th><th>Date</th><th>Commits</th><th>Authors</th></tr>')
|
|
|
1455
|
+ tags_content.append('<table class="tags">')
|
|
|
1456
|
+ tags_content.append('<tr><th>Name</th><th>Date</th><th>Commits</th><th>Authors</th></tr>')
|
|
1434
|
1457
|
# sort the tags by date desc
|
|
1435
|
1458
|
tags_sorted_by_date_desc = [el[1] for el in reversed(sorted([(el[1]['date'], el[0]) for el in list(data.tags.items())]))]
|
|
1436
|
1459
|
for tag in tags_sorted_by_date_desc:
|
|
|
@@ -1438,10 +1461,12 @@ class HTMLReportCreator(ReportCreator):
|
|
1438
|
1461
|
self.authors_by_commits = getkeyssortedbyvalues(data.tags[tag]['authors'])
|
|
1439
|
1462
|
for i in reversed(self.authors_by_commits):
|
|
1440
|
1463
|
authorinfo.append('%s (%d)' % (i, data.tags[tag]['authors'][i]))
|
|
1441
|
|
- f.write('<tr><td>%s</td><td>%s</td><td>%d</td><td>%s</td></tr>' % (tag, data.tags[tag]['date'], data.tags[tag]['commits'], ', '.join(authorinfo)))
|
|
1442
|
|
- f.write('</table>')
|
|
|
1464
|
+ tags_content.append('<tr><td>%s</td><td>%s</td><td>%d</td><td>%s</td></tr>' % (tag, data.tags[tag]['date'], data.tags[tag]['commits'], ', '.join(authorinfo)))
|
|
|
1465
|
+ tags_content.append('</table>')
|
|
1443
|
1466
|
|
|
1444
|
|
- f.write('</body></html>')
|
|
|
1467
|
+ self.printContent(f, tags_content, 'Tags')
|
|
|
1468
|
+
|
|
|
1469
|
+ f.write('</div></body></html>')
|
|
1445
|
1470
|
f.close()
|
|
1446
|
1471
|
|
|
1447
|
1472
|
self.createGraphs(path)
|
|
|
@@ -1754,11 +1779,85 @@ plot """
|
|
1754
|
1779
|
<link rel="stylesheet" href="%s" type="text/css" />
|
|
1755
|
1780
|
<meta name="generator" content="GitStats %s" />
|
|
1756
|
1781
|
<script type="text/javascript" src="sortable.js"></script>
|
|
|
1782
|
+ <script src="https://cdn.tailwindcss.com"></script>
|
|
1757
|
1783
|
</head>
|
|
1758
|
1784
|
<body>
|
|
|
1785
|
+<div class="flex h-screen antialiased text-gray-900 bg-gray-100 dark:bg-dark dark:text-light">
|
|
1759
|
1786
|
""" % (self.title, conf['style'], getversion()))
|
|
|
1787
|
+
|
|
1760
|
1788
|
|
|
|
1789
|
+ def printContent(self, f, content, title="Front's Stats"):
|
|
|
1790
|
+ content = '\n'.join(content)
|
|
|
1791
|
+ f.write(f'''
|
|
|
1792
|
+ <div class="flex-1 h-full overflow-x-hidden overflow-y-auto">
|
|
|
1793
|
+ <!-- Navbar -->
|
|
|
1794
|
+ <header class="relative flex-shrink-0 bg-white dark:bg-darker">
|
|
|
1795
|
+ <div class="flex items-center justify-between p-2 border-b dark:border-primary-darker">
|
|
|
1796
|
+ <a href="#" class="inline-block text-2xl font-bold tracking-wider uppercase text-primary-dark dark:text-light">
|
|
|
1797
|
+ {title}
|
|
|
1798
|
+ </a>
|
|
|
1799
|
+ </div>
|
|
|
1800
|
+ </header>
|
|
|
1801
|
+
|
|
|
1802
|
+ <!-- Main content -->
|
|
|
1803
|
+ <main class="p-2">{content}</main>
|
|
|
1804
|
+ </div>
|
|
|
1805
|
+ ''')
|
|
|
1806
|
+
|
|
1761
|
1807
|
def printNav(self, f):
|
|
|
1808
|
+
|
|
|
1809
|
+ menu = ''.join([f'''
|
|
|
1810
|
+<a
|
|
|
1811
|
+ href="{href}"
|
|
|
1812
|
+ class="flex items-center p-2 text-gray-500 transition-colors rounded-md dark:text-light hover:bg-primary-100 dark:hover:bg-primary"
|
|
|
1813
|
+ role="button"
|
|
|
1814
|
+ >
|
|
|
1815
|
+ <span aria-hidden="true">
|
|
|
1816
|
+ <svg
|
|
|
1817
|
+ class="w-5 h-5"
|
|
|
1818
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
1819
|
+ fill="none"
|
|
|
1820
|
+ viewBox="0 0 24 24"
|
|
|
1821
|
+ stroke="currentColor"
|
|
|
1822
|
+ >
|
|
|
1823
|
+ <path
|
|
|
1824
|
+ stroke-linecap="round"
|
|
|
1825
|
+ stroke-linejoin="round"
|
|
|
1826
|
+ stroke-width="2"
|
|
|
1827
|
+ d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
|
|
1828
|
+ />
|
|
|
1829
|
+ </svg>
|
|
|
1830
|
+ </span>
|
|
|
1831
|
+ <span class="ml-2 text-sm">{label}</span>
|
|
|
1832
|
+ </a>
|
|
|
1833
|
+''' for (label, href) in [
|
|
|
1834
|
+ ("General", "index.html"),
|
|
|
1835
|
+ ("Activity", "activity.html"),
|
|
|
1836
|
+ ("Authors","authors.html"),
|
|
|
1837
|
+ ("Files","files.html"),
|
|
|
1838
|
+ ("Lines", "lines.html"),
|
|
|
1839
|
+ ("Tags", "tags.html")
|
|
|
1840
|
+ ]])
|
|
|
1841
|
+
|
|
|
1842
|
+ f.write(f'''
|
|
|
1843
|
+<!-- Sidebar -->
|
|
|
1844
|
+<aside class="flex-shrink-0 hidden w-64 bg-white border-r dark:border-primary-darker dark:bg-darker md:block">
|
|
|
1845
|
+ <div class="flex flex-col h-full">
|
|
|
1846
|
+ <!-- Sidebar links -->
|
|
|
1847
|
+ <nav aria-label="Main" class="flex-1 px-2 py-4 space-y-2 overflow-y-hidden hover:overflow-y-auto">
|
|
|
1848
|
+ {menu}
|
|
|
1849
|
+ </nav>
|
|
|
1850
|
+ <!-- Sidebar footer -->
|
|
|
1851
|
+ <div class="flex-shrink-0 px-2 py-4 space-y-2">
|
|
|
1852
|
+ <button type="button" class="flex items-center justify-center w-full px-4 py-2 text-sm text-white rounded-md bg-primary hover:bg-primary-dark focus:outline-none focus:ring focus:ring-primary-dark focus:ring-offset-1 focus:ring-offset-white dark:focus:ring-offset-dark">
|
|
|
1853
|
+ <span>Customize</span>
|
|
|
1854
|
+ </button>
|
|
|
1855
|
+ </div>
|
|
|
1856
|
+ </div>
|
|
|
1857
|
+</aside>
|
|
|
1858
|
+''')
|
|
|
1859
|
+
|
|
|
1860
|
+ def printNav_old(self, f):
|
|
1762
|
1861
|
f.write("""
|
|
1763
|
1862
|
<div class="nav">
|
|
1764
|
1863
|
<ul>
|