Skip to content

Commit 839288f

Browse files
committed
Support year selection, output repo language.
1 parent 5eea9a4 commit 839288f

File tree

5 files changed

+70
-55
lines changed

5 files changed

+70
-55
lines changed

main.py

+9-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# coding: utf8
22
"""Generate annual report for programmers."""
33
import os
4-
import time
54
import traceback
65
from datetime import datetime
76
from typing import Dict, Any
@@ -18,11 +17,18 @@ def get_user_info() -> Dict[str, Any]:
1817
""" Get information from user's inputs. """
1918
print('Sometimes it is the people no one imagines anything of, who do the things no one can '
2019
'imagine.\n' + ' ' * 70 + '-- Alan Turing')
21-
print('欢迎使用程序员年度总结生成器')
20+
print('欢迎使用程序员年度报告生成器')
2221
while True:
2322
name = input('请输入你的名字,按回车继续\n').strip()
2423
if name:
2524
break
25+
now = datetime.now()
26+
recent_year = now.year
27+
if now.month == 1:
28+
recent_year -= 1
29+
year = input('请输入你想要生成报告的年份,不输入则使用 {0}\n'.format(recent_year)).strip()
30+
if not year:
31+
year = recent_year
2632
print('请输入你近一年使用过的 git 邮箱,不输入则使用 ${0} 的结果'.format(const.GIT_EMAIL_CMD))
2733
emails = []
2834
while True:
@@ -59,34 +65,18 @@ def get_user_info() -> Dict[str, Any]:
5965
encrypt = True
6066
info = {
6167
'name': name,
68+
'year': int(year),
6269
'emails': list(set(emails)),
6370
'git_inputs': list(set(git_inputs)),
6471
'encrypt': encrypt,
6572
}
6673
return info
6774

6875

69-
def get_time_info() -> Dict[str, int]:
70-
""" Get recent year's info. """
71-
now = datetime.now()
72-
recent_year = now.year
73-
if now.month == 1:
74-
recent_year -= 1
75-
begin_ts = time.mktime(datetime(year=recent_year, month=1, day=1).timetuple())
76-
end_ts = time.mktime(datetime(year=recent_year + 1, month=1, day=1).timetuple())
77-
year_ends = {
78-
'year': recent_year,
79-
'begin': int(begin_ts),
80-
'end': int(end_ts),
81-
}
82-
return year_ends
83-
84-
8576
def main():
8677
ctx = util.DotDict()
8778
ctx.run_dir = RUN_DIR
8879
ctx.update(get_user_info())
89-
ctx.update(get_time_info())
9080
ctx.update(check_linguist(ctx))
9181
print('\nContext:')
9282
for key, val in ctx.items():

report.py

+27-22
Original file line numberDiff line numberDiff line change
@@ -79,34 +79,36 @@ def write_stat(self):
7979
self.report.writerow(title)
8080
self.report.writerow('')
8181

82-
summary = self.repos.get_commit_summary()
8382
self.report.writerow(['Summary'])
83+
summary = self.repos.get_commit_summary()
8484
names = ['projects', 'commits', 'merges', 'changes']
8585
for name in names[:3]:
8686
self.report.writerow([name, summary[name]])
8787
self.report.writerow([names[-1], summary['insert'] + summary['delete']])
8888
self.report.writerow('')
8989

90-
lang_stat = self.repos.get_language_stat()
91-
self.report.writerow(['Coding stat by language'])
92-
headers = ['language', 'commits', 'insertions', 'deletions', 'changes']
90+
self.report.writerow(['Coding stat by repo'])
91+
headers = ['name', 'language', 'commits', 'merges', 'insertions', 'deletions', 'changes']
9392
self.report.writerow(headers)
94-
for lang, stat in lang_stat.items():
93+
for repo in self.repos.repos:
94+
repo_stat = repo.get_commit_summary()
9595
row = [
96-
lang, stat['commits'], stat['insert'], stat['delete'],
97-
stat['insert'] + stat['delete'],
96+
repo.name, repo.language, repo_stat['commits'], repo_stat['merges'],
97+
repo_stat['insert'], repo_stat['delete'], repo_stat['insert'] + repo_stat['delete'],
9898
]
9999
self.report.writerow(row)
100100
self.report.writerow('')
101101

102-
self.report.writerow(['Coding stat by repo'])
103-
headers = ['name', 'commits', 'merges', 'insertions', 'deletions', 'changes']
102+
self.report.writerow(['Coding stat by language'])
103+
lang_stat = self.repos.get_language_stat()
104+
headers = ['language', 'commits', 'insertions', 'deletions', 'changes']
104105
self.report.writerow(headers)
105-
for repo in self.repos.repos:
106-
repo_stat = repo.get_commit_summary()
106+
sorted_lang = sorted(lang_stat.keys(), key=lambda x: lang_stat[x]['weight'], reverse=True)
107+
for lang in sorted_lang:
108+
stat = lang_stat[lang]
107109
row = [
108-
repo.name, repo_stat['commits'], repo_stat['merges'], repo_stat['insert'],
109-
repo_stat['delete'], repo_stat['insert'] + repo_stat['delete'],
110+
lang, stat['commits'], stat['insert'], stat['delete'],
111+
stat['insert'] + stat['delete'],
110112
]
111113
self.report.writerow(row)
112114
self.report.writerow('')
@@ -115,10 +117,13 @@ def write_stat(self):
115117
self.report.writerow(['Merge stat by name'])
116118
headers = ['name', 'merge', 'merged_by', 'merges']
117119
self.report.writerow(headers)
118-
for name, stat in merge_stat.items():
120+
sorted_names = sorted(merge_stat,
121+
key=lambda x: merge_stat[x]['merge'] + merge_stat[x]['merged_by'],
122+
reverse=True)
123+
for name in sorted_names:
124+
stat = merge_stat[name]
119125
row = [
120-
name, stat.get('merge', 0), stat.get('merged_by', 0),
121-
stat.get('merge', 0) + stat.get('merged_by', 0),
126+
name, stat['merge'], stat['merged_by'], stat['merge'] + stat['merged_by']
122127
]
123128
self.report.writerow(row)
124129
self.report.writerow('')
@@ -225,16 +230,16 @@ def draw_language_stat(self):
225230
weights = [lang_stat[key]['weight'] for key in labels]
226231
percents = util.get_percents(weights)
227232
weighted = [(labels[i], percents[i]) for i in range(len(labels))]
228-
weighted.sort(key=lambda x: x[1], reverse=True)
229233
res = []
230-
other_index = 0
231-
for i, item in enumerate(weighted):
234+
other_percent = 0
235+
for item in weighted:
232236
if item[1] > 2:
233237
res.append(item)
234238
else:
235-
other_index = i
236-
other_percent = sum(list(item[1] for item in weighted[other_index:])) + 0.01
237-
res.append(('other', other_percent))
239+
other_percent += item[1]
240+
if other_percent > 0:
241+
other_percent += 0.001
242+
res.append(('other', other_percent))
238243
labels, weights = [item[0] for item in res], [item[1] for item in res]
239244
plt.pie(weights, labels=labels, autopct='%1.1f%%', startangle=90)
240245
plt.title('Programming languages by weight')

repository.py

+26-11
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ def __init__(self, git_url_or_path: str, ctx: util.DotDict):
7272
self.name = util.encrypt_string(repo_name, ctx.encrypt)
7373
self.git_url = util.run(const.GIT_REMOTE_URL_CMD, check=False)
7474
self.ctx = ctx
75+
self.language = ''
7576
self.linguist_enabled = False
7677
self.linguist_res = {}
77-
self.analyze_by_linguist()
7878
self.commit_list = []
7979
self.commit_dict = {}
8080
self.user_commits = []
81+
self.analyze_by_linguist()
8182
self.parse_git_commits()
83+
self.get_repo_language()
8284
print('{0} loaded successfully!'.format(repo_name))
8385

8486
def parse_git_commits(self):
@@ -91,7 +93,8 @@ def parse_git_commits(self):
9193
if line.strip() == 'master':
9294
branch = 'master'
9395
break
94-
git_log_cmd = git_log_tmpl.format(branch=branch, begin=self.ctx.begin, end=self.ctx.end,
96+
begin, end = util.get_year_ends(self.ctx.year)
97+
git_log_cmd = git_log_tmpl.format(branch=branch, begin=begin, end=end,
9598
fmt=const.GIT_LOG_FORMAT)
9699
git_log = util.run(git_log_cmd)
97100
commit_logs = git_log.split(const.GIT_COMMIT_SEPARATOR)
@@ -116,8 +119,7 @@ def parse_git_log(self, commit_log: str) -> Any:
116119
author=lines[2], email=lines[3], timestamp=int(lines[4]))
117120
commit.subject = lines[5]
118121
commit.num_stat = [line.strip() for line in lines[6:] if line.strip()]
119-
if commit.email in self.ctx.emails:
120-
self.parse_commit_stat(commit)
122+
self.parse_commit_stat(commit)
121123
return commit
122124

123125
def analyze_by_linguist(self):
@@ -136,6 +138,12 @@ def analyze_by_linguist(self):
136138
self.linguist_res[file] = lang
137139
self.linguist_enabled = True
138140

141+
def get_repo_language(self):
142+
stat = self.get_language_stat(only_user=False)
143+
if not stat:
144+
return
145+
self.language = max(stat.keys(), key=lambda x: stat[x]['weight'])
146+
139147
def get_commit_summary(self) -> util.DotDict:
140148
summary = {
141149
'commits': 0,
@@ -151,10 +159,12 @@ def get_commit_summary(self) -> util.DotDict:
151159
summary['delete'] += commit.code_del
152160
return util.DotDict(summary)
153161

154-
def get_language_stat(self) -> Dict[str, Any]:
162+
def get_language_stat(self, only_user=True) -> Dict[str, Any]:
155163
""" Get each used language's commit stat. """
156164
res = {}
157-
for commit in self.user_commits:
165+
for commit in self.commit_list:
166+
if only_user and commit.email not in self.ctx.emails:
167+
continue
158168
for lang, stat in commit.lang_stat.items():
159169
if lang not in res:
160170
res[lang] = {
@@ -382,19 +392,24 @@ def get_merge_stat(self) -> Dict[str, Dict[str, Any]]:
382392
# user merges others' commit
383393
elif commit.email in self.ctx.emails:
384394
if merged.email not in merges:
385-
merges[merged.email] = {}
395+
merges[merged.email] = {
396+
'merge': 1,
397+
'merged_by': 0,
398+
}
386399
authors[merged.email] = {merged.author}
387400
else:
388-
merges[merged.email]['merge'] = merges[merged.email].get('merge', 0) + 1
401+
merges[merged.email]['merge'] += 1
389402
authors[merged.email].add(merged.author)
390403
# user's commit merged by others
391404
elif merged.email in self.ctx.emails:
392405
if commit.email not in merges:
393-
merges[commit.email] = {}
406+
merges[commit.email] = {
407+
'merge': 0,
408+
'merged_by': 1
409+
}
394410
authors[commit.email] = {commit.author}
395411
else:
396-
merges[commit.email]['merged_by'] = merges[commit.email].get('merged_by',
397-
0) + 1
412+
merges[commit.email]['merged_by'] += 1
398413
authors[commit.email].add(commit.author)
399414
result = {}
400415
for email, stat in merges.items():

tests.py

-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ def setUp(self):
1616
'git_inputs': [run_dir],
1717
'encrypt': True,
1818
'year': 2018,
19-
'begin': 1514736000,
20-
'end': 1546272000,
2119
'linguist_enabled': False
2220
})
2321

util.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# coding: utf8
22
import os
33
import subprocess
4+
import time
45
from datetime import datetime
5-
from typing import List, Any
6+
from typing import List, Any, Tuple
67

78
import const
89

@@ -46,6 +47,12 @@ def timestamp_to_fixed_day(timestamp: int) -> datetime:
4647
return dt.replace(year=2018, month=1, day=1)
4748

4849

50+
def get_year_ends(year: int) -> Tuple[int, int]:
51+
begin_ts = time.mktime(datetime(year=year, month=1, day=1).timetuple())
52+
end_ts = time.mktime(datetime(year=year + 1, month=1, day=1).timetuple())
53+
return int(begin_ts), int(end_ts)
54+
55+
4956
def is_ascii(s: str) -> bool:
5057
return all(ord(c) < 128 for c in s)
5158

0 commit comments

Comments
 (0)