| Server IP : 164.138.27.172 / Your IP : 216.73.216.143 Web Server : nginx/1.27.4 System : Linux cookingdream 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : www-adm ( 1001) PHP Version : 8.3.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /home/jonasn/new-gitstats/gitstats/ |
Upload File : |
# Copyright (c) 2007-2014 Heikki Hokkanen <hoxu@users.sf.net> & others contributors
# GPLv2 / GPLv3
# Copyright (c) 2024-present Xianpeng Shen <xianpeng.shen@gmail.com>.
# GPLv2 / GPLv3
import os
import glob
import shutil
import datetime
import time
from gitstats import load_config, GNUPLOT_COMMON, WEEKDAYS
from gitstats.utils import (
get_version,
get_git_version,
get_gnuplot_version,
get_pipe_output,
gnuplot_cmd,
)
conf = load_config()
class ReportCreator:
"""Creates the actual report based on given data."""
def __init__(self):
self.data = None
self.path = None
def create(self, data, path):
self.data = data
self.path = path
class HTMLReportCreator(ReportCreator):
def create(self, data, path):
ReportCreator.create(self, data, path)
self.title = data.project_name
# copy static files to the report directory
basedir = os.path.dirname(os.path.abspath(__file__))
for file in (
conf["style"],
"sortable.js",
"arrow-up.gif",
"arrow-down.gif",
"arrow-none.gif",
):
src = basedir + "/" + file
if os.path.exists(src):
shutil.copyfile(src, path + "/" + file)
break
self.create_index_html(data, path)
self.create_activity_html(data, path)
self.create_authors_html(data, path)
self.create_files_html(data, path)
self.create_lines_html(data, path)
self.create_tags_html(data, path)
self.create_graphs(path)
def create_index_html(self, data, path):
f = open(path + "/index.html", "w")
format = "%Y-%m-%d %H:%M:%S"
self.print_header(f)
f.write("<h1>GitStats - %s</h1>" % data.project_name)
self.print_nav(f)
f.write(html_header(2, "Git Overview"))
f.write("<table border='1' cellspacing='0' cellpadding='4'>")
f.write("<tr><td><b>Project name</b></td><td>%s</td></tr>" % data.project_name)
f.write(
"<tr><td><b>Generated</b></td><td>%s (in %d seconds)</td></tr>"
% (
datetime.datetime.now().strftime(format),
time.time() - data.get_stamp_created(),
)
)
f.write(
'<tr><td><b>Generator</b></td><td><a href="https://github.com/shenxianpeng/gitstats">gitstats</a> %s, %s, %s</td></tr>'
% (get_version(), get_git_version(), get_gnuplot_version())
)
f.write(
"<tr><td><b>Report Period</b></td><td>%s to %s</td></tr>"
% (
data.get_first_commit_date().strftime(format),
data.get_last_commit_date().strftime(format),
)
)
f.write(
"<tr><td><b>Age</b></td><td>%d days, %d active days (%3.2f%%)</td></tr>"
% (
data.get_commit_delta_days(),
len(data.get_active_days()),
(100.0 * len(data.get_active_days()) / data.get_commit_delta_days()),
)
)
f.write(
"<tr><td><b>Total Files</b></td><td>%s</td></tr>" % data.get_total_files()
)
f.write(
"<tr><td><b>Total Lines of Code</b></td><td>%s (%d added, %d removed)</td></tr>"
% (data.get_total_loc(), data.total_lines_added, data.total_lines_removed)
)
f.write(
"<tr><td><b>Total Commits</b></td><td>%s (average %.1f commits per active day, %.1f per all days)</td></tr>"
% (
data.get_total_commits(),
float(data.get_total_commits()) / len(data.get_active_days()),
float(data.get_total_commits()) / data.get_commit_delta_days(),
)
)
f.write(
"<tr><td><b>Authors</b></td><td>%s (average %.1f commits per author)</td></tr>"
% (
data.get_total_authors(),
(1.0 * data.get_total_commits()) / data.get_total_authors(),
)
)
f.write("</table>")
f.write("</body>\n</html>")
f.close()
def create_activity_html(self, data, path):
###
# Activity
f = open(path + "/activity.html", "w")
self.print_header(f)
f.write("<h1>Activity</h1>")
self.print_nav(f)
# f.write('<h2>Last 30 days</h2>')
# f.write('<h2>Last 12 months</h2>')
# Weekly activity
WEEKS = 32
f.write(html_header(2, "Weekly activity"))
f.write("<p>Last %d weeks</p>" % WEEKS)
# generate weeks to show (previous N weeks from now)
now = datetime.datetime.now()
deltaweek = datetime.timedelta(7)
weeks = []
stampcur = now
for i in range(0, WEEKS):
weeks.insert(0, stampcur.strftime("%Y-%W"))
stampcur -= deltaweek
# top row: commits & bar
f.write('<table class="noborders"><tr>')
for i in range(0, WEEKS):
commits = 0
if weeks[i] in data.activity_by_year_week:
commits = data.activity_by_year_week[weeks[i]]
percentage = 0
if weeks[i] in data.activity_by_year_week:
percentage = (
float(data.activity_by_year_week[weeks[i]])
/ data.activity_by_year_week_peak
)
height = max(1, int(200 * percentage))
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)
)
# bottom row: year/week
f.write("</tr><tr>")
for i in range(0, WEEKS):
f.write("<td>%s</td>" % (WEEKS - i))
f.write("</tr></table>")
# Hour of Day
f.write(html_header(2, "Hour of Day"))
hour_of_day = data.get_activity_by_hour_of_day()
f.write("<table><tr><th>Hour</th>")
for i in range(0, 24):
f.write("<th>%d</th>" % i)
f.write("</tr>\n<tr><th>Commits</th>")
fp = open(path + "/hour_of_day.dat", "w")
for i in range(0, 24):
if i in hour_of_day:
r = 127 + int(
(float(hour_of_day[i]) / data.activity_by_hour_of_day_busiest) * 128
)
f.write(
'<td style="background-color: rgb(%d, 0, 0)">%d</td>'
% (r, hour_of_day[i])
)
fp.write("%d %d\n" % (i, hour_of_day[i]))
else:
f.write("<td>0</td>")
fp.write("%d 0\n" % i)
fp.close()
f.write("</tr>\n<tr><th>%</th>")
totalcommits = data.get_total_commits()
for i in range(0, 24):
if i in hour_of_day:
r = 127 + int(
(float(hour_of_day[i]) / data.activity_by_hour_of_day_busiest) * 128
)
f.write(
'<td style="background-color: rgb(%d, 0, 0)">%.2f</td>'
% (r, (100.0 * hour_of_day[i]) / totalcommits)
)
else:
f.write("<td>0.00</td>")
f.write("</tr></table>")
f.write('<img src="hour_of_day.png" alt="Hour of Day">')
fg = open(path + "/hour_of_day.dat", "w")
for i in range(0, 24):
if i in hour_of_day:
fg.write("%d %d\n" % (i + 1, hour_of_day[i]))
else:
fg.write("%d 0\n" % (i + 1))
fg.close()
# Day of Week
f.write(html_header(2, "Day of Week"))
day_of_week = data.get_activity_by_day_of_week()
f.write('<div class="vtable"><table>')
f.write("<tr><th>Day</th><th>Total (%)</th></tr>")
fp = open(path + "/day_of_week.dat", "w")
for d in range(0, 7):
commits = 0
if d in day_of_week:
commits = day_of_week[d]
fp.write("%d %s %d\n" % (d + 1, WEEKDAYS[d], commits))
f.write("<tr>")
f.write("<th>%s</th>" % (WEEKDAYS[d]))
if d in day_of_week:
f.write(
"<td>%d (%.2f%%)</td>"
% (day_of_week[d], (100.0 * day_of_week[d]) / totalcommits)
)
else:
f.write("<td>0</td>")
f.write("</tr>")
f.write("</table></div>")
f.write('<img src="day_of_week.png" alt="Day of Week">')
fp.close()
# Hour of Week
f.write(html_header(2, "Hour of Week"))
f.write("<table>")
f.write("<tr><th>Weekday</th>")
for hour in range(0, 24):
f.write("<th>%d</th>" % (hour))
f.write("</tr>")
for weekday in range(0, 7):
f.write("<tr><th>%s</th>" % (WEEKDAYS[weekday]))
for hour in range(0, 24):
try:
commits = data.activity_by_hour_of_week[weekday][hour]
except KeyError:
commits = 0
if commits != 0:
f.write("<td")
r = 127 + int(
(float(commits) / data.activity_by_hour_of_week_busiest) * 128
)
f.write(' style="background-color: rgb(%d, 0, 0)"' % r)
f.write(">%d</td>" % commits)
else:
f.write("<td></td>")
f.write("</tr>")
f.write("</table>")
# Month of Year
f.write(html_header(2, "Month of Year"))
f.write('<div class="vtable"><table>')
f.write("<tr><th>Month</th><th>Commits (%)</th></tr>")
fp = open(path + "/month_of_year.dat", "w")
for mm in range(1, 13):
commits = 0
if mm in data.activity_by_month_of_year:
commits = data.activity_by_month_of_year[mm]
f.write(
"<tr><td>%d</td><td>%d (%.2f %%)</td></tr>"
% (mm, commits, (100.0 * commits) / data.get_total_commits())
)
fp.write("%d %d\n" % (mm, commits))
fp.close()
f.write("</table></div>")
f.write('<img src="month_of_year.png" alt="Month of Year">')
# Commits by year/month
f.write(html_header(2, "Commits by year/month"))
f.write(
'<div class="vtable"><table><tr><th>Month</th><th>Commits</th><th>Lines added</th><th>Lines removed</th></tr>'
)
for yymm in reversed(sorted(data.commits_by_month.keys())):
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),
)
)
f.write("</table></div>")
f.write('<img src="commits_by_year_month.png" alt="Commits by year/month">')
fg = open(path + "/commits_by_year_month.dat", "w")
for yymm in sorted(data.commits_by_month.keys()):
fg.write("%s %s\n" % (yymm, data.commits_by_month[yymm]))
fg.close()
# Commits by year
f.write(html_header(2, "Commits by Year"))
f.write(
'<div class="vtable"><table><tr><th>Year</th><th>Commits (% of all)</th><th>Lines added</th><th>Lines removed</th></tr>'
)
for yy in reversed(sorted(data.commits_by_year.keys())):
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.get_total_commits(),
data.lines_added_by_year.get(yy, 0),
data.lines_removed_by_year.get(yy, 0),
)
)
f.write("</table></div>")
f.write('<img src="commits_by_year.png" alt="Commits by Year">')
fg = open(path + "/commits_by_year.dat", "w")
for yy in sorted(data.commits_by_year.keys()):
fg.write("%d %d\n" % (yy, data.commits_by_year[yy]))
fg.close()
# Commits by timezone
f.write(html_header(2, "Commits by Timezone"))
f.write("<table><tr>")
f.write("<th>Timezone</th><th>Commits</th>")
f.write("</tr>")
max_commits_on_tz = max(data.commits_by_timezone.values())
for i in sorted(list(data.commits_by_timezone.keys()), key=lambda n: int(n)):
commits = data.commits_by_timezone[i]
r = 127 + int((float(commits) / max_commits_on_tz) * 128)
f.write(
'<tr><th>%s</th><td style="background-color: rgb(%d, 0, 0)">%d</td></tr>'
% (i, r, commits)
)
f.write("</table>")
f.write("</body></html>")
f.close()
def create_authors_html(self, data, path):
###
# Authors
f = open(path + "/authors.html", "w")
self.print_header(f)
f.write("<h1>Authors</h1>")
self.print_nav(f)
# Authors :: List of authors
f.write(html_header(2, "List of Authors"))
f.write('<table class="authors sortable" id="authors">')
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>'
)
for author in data.get_authors(conf["max_authors"]):
info = data.get_author_info(author)
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"],
)
)
f.write("</table>")
allauthors = data.get_authors()
if len(allauthors) > conf["max_authors"]:
rest = allauthors[conf["max_authors"] :]
f.write(
'<p class="moreauthors">These didn\'t make it to the top: %s</p>'
% ", ".join(rest)
)
f.write(html_header(2, "Cumulated Added Lines of Code per Author"))
f.write(
'<img src="lines_of_code_by_author.png" alt="Lines of code per Author">'
)
if len(allauthors) > conf["max_authors"]:
f.write(
'<p class="moreauthors">Only top %d authors shown</p>'
% conf["max_authors"]
)
f.write(html_header(2, "Commits per Author"))
f.write('<img src="commits_by_author.png" alt="Commits per Author">')
if len(allauthors) > conf["max_authors"]:
f.write(
'<p class="moreauthors">Only top %d authors shown</p>'
% conf["max_authors"]
)
fgl = open(path + "/lines_of_code_by_author.dat", "w")
fgc = open(path + "/commits_by_author.dat", "w")
lines_by_authors = {} # cumulated added lines by
# author. to save memory,
# changes_by_date_by_author[stamp][author] is defined
# only at points where author commits.
# lines_by_authors allows us to generate all the
# points in the .dat file.
# Don't rely on get_authors to give the same order each
# time. Be robust and keep the list in a variable.
commits_by_authors = {} # cumulated added lines by
self.authors_to_plot = data.get_authors(conf["max_authors"])
for author in self.authors_to_plot:
lines_by_authors[author] = 0
commits_by_authors[author] = 0
for stamp in sorted(data.changes_by_date_by_author.keys()):
fgl.write("%d" % stamp)
fgc.write("%d" % stamp)
for author in self.authors_to_plot:
if author in list(data.changes_by_date_by_author[stamp].keys()):
lines_by_authors[author] = data.changes_by_date_by_author[stamp][
author
]["lines_added"]
commits_by_authors[author] = data.changes_by_date_by_author[stamp][
author
]["commits"]
fgl.write(" %d" % lines_by_authors[author])
fgc.write(" %d" % commits_by_authors[author])
fgl.write("\n")
fgc.write("\n")
fgl.close()
fgc.close()
# Authors :: Author of Month
f.write(html_header(2, "Author of Month"))
f.write('<table class="sortable" id="aom">')
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"]
)
for yymm in reversed(sorted(data.author_of_month.keys())):
author_dict = data.author_of_month[yymm]
authors = get_keys_sorted_by_values(author_dict)
authors.reverse()
commits = data.author_of_month[yymm][authors[0]]
authors_str = ", ".join(authors[1 : conf["authors_top"] + 1])
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],
authors_str,
len(authors),
)
)
f.write("</table>")
f.write(html_header(2, "Author of Year"))
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"]
)
for yy in reversed(sorted(data.author_of_year.keys())):
author_dict = data.author_of_year[yy]
authors = get_keys_sorted_by_values(author_dict)
authors.reverse()
commits = data.author_of_year[yy][authors[0]]
authors_str = ", ".join(authors[1 : conf["authors_top"] + 1])
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],
authors_str,
len(authors),
)
)
f.write("</table>")
# Domains
f.write(html_header(2, "Commits by Domains"))
domains_by_commits = get_keys_sorted_by_value_key(data.domains, "commits")
domains_by_commits.reverse() # most first
f.write('<div class="vtable"><table>')
f.write("<tr><th>Domains</th><th>Total (%)</th></tr>")
fp = open(path + "/domains.dat", "w")
n = 0
for domain in domains_by_commits:
if n == conf["max_domains"]:
break
commits = 0
n += 1
info = data.get_domain_info(domain)
fp.write("%s %d %d\n" % (domain, n, info["commits"]))
f.write(
"<tr><th>%s</th><td>%d (%.2f%%)</td></tr>"
% (
domain,
info["commits"],
(100.0 * info["commits"] / data.get_total_commits()),
)
)
f.write("</table></div>")
f.write('<img src="domains.png" alt="Commits by Domains">')
fp.close()
f.write("</body></html>")
f.close()
def create_files_html(self, data, path):
###
# Files
f = open(path + "/files.html", "w")
self.print_header(f)
f.write("<h1>Files</h1>")
self.print_nav(f)
f.write("<dl>\n")
f.write("<dt>Total files</dt><dd>%d</dd>" % data.get_total_files())
f.write("<dt>Total lines</dt><dd>%d</dd>" % data.get_total_loc())
try:
f.write(
"<dt>Average file size</dt><dd>%.2f bytes</dd>"
% (float(data.get_total_size()) / data.get_total_files())
)
except ZeroDivisionError:
pass
f.write("</dl>\n")
# Files :: File count by date
f.write(html_header(2, "File count by date"))
# use set to get rid of duplicate/unnecessary entries
files_by_date = set()
for stamp in sorted(data.files_by_stamp.keys()):
files_by_date.add(
"%s %d"
% (
datetime.datetime.fromtimestamp(stamp).strftime("%Y-%m-%d"),
data.files_by_stamp[stamp],
)
)
fg = open(path + "/files_by_date.dat", "w")
for line in sorted(list(files_by_date)):
fg.write("%s\n" % line)
# for stamp in sorted(data.files_by_stamp.keys()):
# fg.write('%s %d\n' % (datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d'), data.files_by_stamp[stamp]))
fg.close()
f.write('<img src="files_by_date.png" alt="Files by Date">')
# f.write('<h2>Average file size by date</h2>')
# Files :: Extensions
f.write(html_header(2, "Extensions"))
f.write(
'<table class="sortable" id="ext"><tr><th>Extension</th><th>Files (%)</th><th>Lines (%)</th><th>Lines/file</th></tr>'
)
for ext in sorted(data.extensions.keys()):
files = data.extensions[ext]["files"]
lines = data.extensions[ext]["lines"]
try:
loc_percentage = (100.0 * lines) / data.get_total_loc()
except ZeroDivisionError:
loc_percentage = 0
f.write(
"<tr><td>%s</td><td>%d (%.2f%%)</td><td>%d (%.2f%%)</td><td>%d</td></tr>"
% (
ext,
files,
(100.0 * files) / data.get_total_files(),
lines,
loc_percentage,
lines / files,
)
)
f.write("</table>")
f.write("</body></html>")
f.close()
def create_lines_html(self, data, path):
###
# Lines
f = open(path + "/lines.html", "w")
self.print_header(f)
f.write("<h1>Lines</h1>")
self.print_nav(f)
f.write("<dl>\n")
f.write("<dt>Total lines</dt><dd>%d</dd>" % data.get_total_loc())
f.write("</dl>\n")
f.write(html_header(2, "Lines of Code"))
f.write('<img src="lines_of_code.png" alt="Lines of Code">')
fg = open(path + "/lines_of_code.dat", "w")
for stamp in sorted(data.changes_by_date.keys()):
fg.write("%d %d\n" % (stamp, data.changes_by_date[stamp]["lines"]))
fg.close()
f.write("</body></html>")
f.close()
def create_tags_html(self, data, path):
###
# tags.html
f = open(path + "/tags.html", "w")
self.print_header(f)
f.write("<h1>Tags</h1>")
self.print_nav(f)
f.write("<dl>")
f.write("<dt>Total tags</dt><dd>%d</dd>" % len(data.tags))
if len(data.tags) > 0:
f.write(
"<dt>Average commits per tag</dt><dd>%.2f</dd>"
% (1.0 * data.get_total_commits() / len(data.tags))
)
f.write("</dl>")
f.write('<table class="tags">')
f.write("<tr><th>Name</th><th>Date</th><th>Commits</th><th>Authors</th></tr>")
# sort the tags by date desc
tags_sorted_by_date_desc = [
el[1]
for el in reversed(
sorted([(el[1]["date"], el[0]) for el in list(data.tags.items())])
)
]
for tag in tags_sorted_by_date_desc:
authorinfo = []
self.authors_by_commits = get_keys_sorted_by_values(
data.tags[tag]["authors"]
)
for i in reversed(self.authors_by_commits):
authorinfo.append("%s (%d)" % (i, data.tags[tag]["authors"][i]))
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),
)
)
f.write("</table>")
f.write("</body></html>")
f.close()
def create_graphs(self, path):
print("Generating graphs...")
self.create_graph_hour_of_day(path)
self.create_graph_day_of_week(path)
self.create_graph_domains(path)
self.create_graph_month_of_year(path)
self.create_graph_commits_by_year_month(path)
self.create_graph_commits_by_year(path)
self.create_graph_files_by_date(path)
self.create_graph_lines_of_code(path)
self.create_graph_lines_of_code_by_author(path)
self.create_graph_commits_by_author(path)
self.create_graph_by_gnuplot(path)
def create_graph_hour_of_day(self, path):
# hour of day
f = open(path + "/hour_of_day.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'hour_of_day.png'
unset key
set xrange [0.5:24.5]
set yrange [0:]
set xtics 4
set grid y
set ylabel "Commits"
plot 'hour_of_day.dat' using 1:2:(0.5) w boxes fs solid
"""
)
f.close()
def create_graph_day_of_week(self, path):
# day of week
f = open(path + "/day_of_week.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'day_of_week.png'
unset key
set xrange [0.5:7.5]
set yrange [0:]
set xtics 1
set grid y
set ylabel "Commits"
plot 'day_of_week.dat' using 1:3:(0.5):xtic(2) w boxes fs solid
"""
)
f.close()
def create_graph_domains(self, path):
# Domains
f = open(path + "/domains.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'domains.png'
unset key
unset xtics
set yrange [0:]
set grid y
set ylabel "Commits"
plot 'domains.dat' using 2:3:(0.5) with boxes fs solid, '' using 2:3:1 with labels rotate by 45 offset 0,1
"""
)
f.close()
def create_graph_month_of_year(self, path):
# Month of Year
f = open(path + "/month_of_year.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'month_of_year.png'
unset key
set xrange [0.5:12.5]
set yrange [0:]
set xtics 1
set grid y
set ylabel "Commits"
plot 'month_of_year.dat' using 1:2:(0.5) w boxes fs solid
"""
)
f.close()
def create_graph_commits_by_year_month(self, path):
# commits_by_year_month
f = open(path + "/commits_by_year_month.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'commits_by_year_month.png'
unset key
set yrange [0:]
set xdata time
set timefmt "%Y-%m"
set format x "%Y-%m"
set xtics rotate
set bmargin 5
set grid y
set ylabel "Commits"
plot 'commits_by_year_month.dat' using 1:2:(0.5) w boxes fs solid
"""
)
f.close()
def create_graph_commits_by_year(self, path):
# commits_by_year
f = open(path + "/commits_by_year.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'commits_by_year.png'
unset key
set yrange [0:]
set xtics 1 rotate
set grid y
set ylabel "Commits"
set yrange [0:]
plot 'commits_by_year.dat' using 1:2:(0.5) w boxes fs solid
"""
)
f.close()
def create_graph_files_by_date(self, path):
# Files by date
f = open(path + "/files_by_date.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'files_by_date.png'
unset key
set yrange [0:]
set xdata time
set timefmt "%Y-%m-%d"
set format x "%Y-%m-%d"
set grid y
set ylabel "Files"
set xtics rotate
set ytics autofreq
set bmargin 6
plot 'files_by_date.dat' using 1:2 w steps
"""
)
f.close()
def create_graph_lines_of_code(self, path):
# Lines of Code
f = open(path + "/lines_of_code.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set output 'lines_of_code.png'
unset key
set yrange [0:]
set xdata time
set timefmt "%s"
set format x "%Y-%m-%d"
set grid y
set ylabel "Lines"
set xtics rotate
set bmargin 6
plot 'lines_of_code.dat' using 1:2 w lines
"""
)
f.close()
def create_graph_lines_of_code_by_author(self, path):
# Lines of Code Added per author
f = open(path + "/lines_of_code_by_author.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set terminal png transparent size 640,480
set output 'lines_of_code_by_author.png'
set key left top
set yrange [0:]
set xdata time
set timefmt "%s"
set format x "%Y-%m-%d"
set grid y
set ylabel "Lines"
set xtics rotate
set bmargin 6
plot """
)
i = 1
plots = []
for a in self.authors_to_plot:
i = i + 1
author = a.replace('"', '\\"').replace("`", "")
plots.append(
"""'lines_of_code_by_author.dat' using 1:%d title "%s" w lines"""
% (i, author)
)
f.write(", ".join(plots))
f.write("\n")
f.close()
def create_graph_commits_by_author(self, path):
# Commits per author
f = open(path + "/commits_by_author.plot", "w")
f.write(GNUPLOT_COMMON)
f.write(
"""
set terminal png transparent size 640,480
set output 'commits_by_author.png'
set key left top
set yrange [0:]
set xdata time
set timefmt "%s"
set format x "%Y-%m-%d"
set grid y
set ylabel "Commits"
set xtics rotate
set bmargin 6
plot """
)
i = 1
plots = []
for a in self.authors_to_plot:
i = i + 1
author = a.replace('"', '\\"').replace("`", "")
plots.append(
"""'commits_by_author.dat' using 1:%d title "%s" w lines"""
% (i, author)
)
f.write(", ".join(plots))
f.write("\n")
f.close()
def create_graph_by_gnuplot(self, path):
os.chdir(path)
files = glob.glob(path + "/*.plot")
for f in files:
out = get_pipe_output([gnuplot_cmd + ' "%s"' % f])
if len(out) > 0:
print(out)
def print_header(self, file) -> None:
file.write(
"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>GitStats - %s</title>
<link rel="stylesheet" href="%s" type="text/css">
<meta name="generator" content="GitStats %s">
<script type="text/javascript" src="sortable.js"></script>
</head>
<body>
"""
% (self.title, conf["style"], get_version)
)
def print_nav(self, file) -> None:
"""Print navigation menu to file."""
file.write(
"""
<div class="nav">
<ul>
<li><a href="index.html">General</a></li>
<li><a href="activity.html">Activity</a></li>
<li><a href="authors.html">Authors</a></li>
<li><a href="files.html">Files</a></li>
<li><a href="lines.html">Lines</a></li>
<li><a href="tags.html">Tags</a></li>
</ul>
</div>
"""
)
def html_header(level, text):
name = html_linkify(text)
return '\n<h%d id="%s"><a href="#%s">%s</a></h%d>\n\n' % (
level,
name,
name,
text,
level,
)
def html_linkify(text):
return text.lower().replace(" ", "_")
def get_keys_sorted_by_values(dict):
return [el[1] for el in sorted([(el[1], el[0]) for el in list(dict.items())])]
# dict['author'] = { 'commits': 512 } - ...key(dict, 'commits')
def get_keys_sorted_by_value_key(d, key):
return [el[1] for el in sorted([(d[el][key], el) for el in list(d.keys())])]