GithubTotal 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,333 @@
1
+ import argparse
2
+ from datetime import datetime
3
+ from zoneinfo import ZoneInfo
4
+
5
+ import requests
6
+ from openpyxl import Workbook
7
+
8
+ __version__ = "1.0.0"
9
+
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser(description="GithubTotal")
13
+ parser.add_argument("command", nargs="?", help="Github account name")
14
+ parser.add_argument(
15
+ "-o",
16
+ "--output",
17
+ metavar="FILENAME.xlsx",
18
+ help="Output file path",
19
+ )
20
+
21
+ args = parser.parse_args()
22
+
23
+ if args.command is None:
24
+ print(f"Github Total v{__version__}")
25
+ print("Usage: ght/github-total <username> [--output FILENAME.xlsx]")
26
+ exit()
27
+
28
+ now = datetime.now(ZoneInfo("Asia/Shanghai"))
29
+ uname = args.command
30
+
31
+ try:
32
+ json = requests.get(
33
+ f"https://api.github.com/users/{uname}/repos?per_page=100"
34
+ ).json()
35
+ if "status" in json:
36
+ print("无法获取仓库数据:", json["message"])
37
+ exit()
38
+ except Exception as e:
39
+ print("无法获取仓库数据:", e)
40
+ exit()
41
+
42
+ info = {
43
+ "name": None,
44
+ "id": None,
45
+ "created_at": None,
46
+ "followers": None,
47
+ }
48
+
49
+ try:
50
+ info = requests.get(f"https://api.github.com/users/{uname}").json()
51
+ if "status" in info:
52
+ print("无法获取用户数据:", json["message"])
53
+ except Exception as e:
54
+ print("无法获取用户数据:", e)
55
+
56
+ wb = Workbook()
57
+
58
+ ws = wb.active
59
+
60
+ ws.title = "仓库数据"
61
+
62
+ ws["A1"] = "仓库名"
63
+ ws["B1"] = "Fork 状态"
64
+ ws["C1"] = "协议"
65
+ ws["D1"] = "发布日期"
66
+ ws["E1"] = "状态 (按发布)"
67
+ ws["F1"] = "更新日期"
68
+ ws["G1"] = "状态 (按更新)"
69
+ ws["H1"] = "Star"
70
+ ws["I1"] = "Watcher"
71
+ ws["J1"] = "Fork"
72
+ ws["K1"] = "语言"
73
+
74
+ total = {
75
+ "total": 0,
76
+ "star": 0,
77
+ "watcher": 0,
78
+ "fork": 0,
79
+ "push": {"new": 0, "behind": 0, "old": 0},
80
+ "upd": {"new": 0, "behind": 0, "old": 0},
81
+ "is_fork": 0,
82
+ "not_fork": 0,
83
+ "license": {},
84
+ "create_at_year": {},
85
+ "create_at_weekday": {},
86
+ "langs": {},
87
+ "stars": {},
88
+ }
89
+
90
+ for i in json:
91
+ total["total"] += 1
92
+
93
+ name = i["name"]
94
+
95
+ is_fork = i["fork"]
96
+
97
+ if is_fork:
98
+ total["is_fork"] += 1
99
+ is_fork = "是"
100
+ else:
101
+ total["not_fork"] += 1
102
+ is_fork = "否"
103
+
104
+ license = i["license"]
105
+
106
+ if not license:
107
+ license = "无"
108
+ else:
109
+ license = license["name"]
110
+
111
+ if license in total["license"]:
112
+ total["license"][license] += 1
113
+ else:
114
+ total["license"][license] = 1
115
+
116
+ created_at = datetime.fromisoformat(
117
+ i["created_at"].replace("Z", "+00:00")
118
+ ).astimezone(ZoneInfo("Asia/Shanghai"))
119
+
120
+ updated_at = datetime.fromisoformat(
121
+ i["pushed_at"].replace("Z", "+00:00")
122
+ ).astimezone(ZoneInfo("Asia/Shanghai"))
123
+
124
+ if created_at.year == now.year:
125
+ if created_at.month == now.month:
126
+ total["push"]["new"] += 1
127
+ created_status = "新"
128
+ else:
129
+ total["push"]["behind"] += 1
130
+ created_status = "中"
131
+ else:
132
+ total["push"]["old"] += 1
133
+ created_status = "旧"
134
+
135
+ if updated_at.year == now.year:
136
+ if updated_at.month == now.month:
137
+ total["upd"]["new"] += 1
138
+ updated_status = "新"
139
+ else:
140
+ total["upd"]["behind"] += 1
141
+ updated_status = "中"
142
+ else:
143
+ total["upd"]["old"] += 1
144
+ updated_status = "旧"
145
+
146
+ created_at_year = created_at.year
147
+
148
+ if created_at_year in total["create_at_year"]:
149
+ total["create_at_year"][created_at_year] += 1
150
+ else:
151
+ total["create_at_year"][created_at_year] = 1
152
+
153
+ created_at_weekday = created_at.weekday()
154
+
155
+ if created_at_weekday in total["create_at_weekday"]:
156
+ total["create_at_weekday"][created_at_weekday] += 1
157
+ else:
158
+ total["create_at_weekday"][created_at_weekday] = 1
159
+
160
+ star = i["stargazers_count"]
161
+
162
+ total["star"] += star
163
+
164
+ total["stars"][name] = star
165
+
166
+ watcher = i["watchers_count"]
167
+
168
+ total["watcher"] += watcher
169
+
170
+ fork = i["forks_count"]
171
+
172
+ total["fork"] += fork
173
+
174
+ lang = i["language"]
175
+
176
+ if lang in total["langs"]:
177
+ total["langs"][lang] += 1
178
+ else:
179
+ total["langs"][lang] = 1
180
+
181
+ ws.append(
182
+ [
183
+ name,
184
+ is_fork,
185
+ license,
186
+ created_at.strftime("%Y年%m月%d日"),
187
+ created_status,
188
+ updated_at.strftime("%Y年%m月%d日"),
189
+ updated_status,
190
+ star,
191
+ watcher,
192
+ fork,
193
+ lang,
194
+ ]
195
+ )
196
+
197
+ ts = wb.create_sheet("统计数据")
198
+
199
+ ts["A1"] = "最多显示"
200
+ ts["B1"] = "仓库数量"
201
+ ts["C1"] = "新数量 (按发布)"
202
+ ts["D1"] = "中数量 (按发布)"
203
+ ts["E1"] = "旧数量 (按发布)"
204
+ ts["F1"] = "新数量 (按更新)"
205
+ ts["G1"] = "中数量 (按更新)"
206
+ ts["H1"] = "旧数量 (按更新)"
207
+ ts["I1"] = "Fork 数量"
208
+ ts["J1"] = "非 Fork 数量"
209
+ ts["K1"] = "使用最多的协议"
210
+ ts["L1"] = "创建仓库最多的年"
211
+ ts["M1"] = "创建仓库最多的星期"
212
+ ts["N1"] = "Star 数量"
213
+ ts["O1"] = "Watcher 数量"
214
+ ts["P1"] = "Fork 数量"
215
+ ts["Q1"] = "使用最多的语言"
216
+ ts["R1"] = "Star 最多的仓库"
217
+ ts["S1"] = "今日日期"
218
+
219
+ license = total["license"]
220
+
221
+ liceense_value = list(license.values())
222
+
223
+ max_license_value = max(liceense_value)
224
+ max_license = [i for i, j in license.items() if j == max_license_value]
225
+
226
+ created_at_year = total["create_at_year"]
227
+
228
+ created_at_year_value = list(created_at_year.values())
229
+
230
+ max_created_at_year_value = max(created_at_year_value)
231
+ max_created_at_year = [
232
+ str(i) for i, j in created_at_year.items() if j == max_created_at_year_value
233
+ ]
234
+
235
+ weekday_map = {
236
+ 0: "星期一",
237
+ 1: "星期二",
238
+ 2: "星期三",
239
+ 3: "星期四",
240
+ 4: "星期五",
241
+ 5: "星期六",
242
+ 6: "星期日",
243
+ }
244
+
245
+ created_at_weekday = total["create_at_weekday"]
246
+
247
+ created_at_weekday_value = list(created_at_weekday.values())
248
+
249
+ max_created_at_weekday_value = max(created_at_weekday_value)
250
+ max_created_at_weekday = [
251
+ weekday_map[i]
252
+ for i, j in created_at_weekday.items()
253
+ if j == max_created_at_weekday_value
254
+ ]
255
+
256
+ langs = total["langs"]
257
+
258
+ langs_value = list(langs.values())
259
+
260
+ max_langs_value = max(langs_value)
261
+ max_langs = [i for i, j in langs.items() if j == max_langs_value]
262
+
263
+ stars = total["stars"]
264
+
265
+ stars_value = list(stars.values())
266
+
267
+ max_stars_value = max(stars_value)
268
+ max_stars = [i for i, j in stars.items() if j == max_stars_value]
269
+
270
+ ts.append(
271
+ [
272
+ 100,
273
+ total["total"],
274
+ total["push"]["new"],
275
+ total["push"]["behind"],
276
+ total["push"]["old"],
277
+ total["upd"]["new"],
278
+ total["upd"]["behind"],
279
+ total["upd"]["old"],
280
+ total["is_fork"],
281
+ total["not_fork"],
282
+ ", ".join(max_license),
283
+ ", ".join(max_created_at_year),
284
+ ", ".join(max_created_at_weekday),
285
+ total["star"],
286
+ total["watcher"],
287
+ total["fork"],
288
+ ", ".join(max_langs),
289
+ ", ".join(max_stars),
290
+ now.strftime("%Y年%m月%d日"),
291
+ ]
292
+ )
293
+
294
+ us = wb.create_sheet("用户数据")
295
+
296
+ us["A1"] = "用户名"
297
+ us["B1"] = "ID"
298
+ us["C1"] = "创建日期"
299
+ us["D1"] = "粉丝数量"
300
+
301
+ name = info["name"] or uname
302
+
303
+ uid = info["id"]
304
+ if uid == None:
305
+ uid = "未知"
306
+ else:
307
+ uid = str(uid)
308
+
309
+ ucreated_at = (
310
+ datetime.fromisoformat(info["created_at"].replace("Z", "+00:00"))
311
+ .astimezone(ZoneInfo("Asia/Shanghai"))
312
+ .strftime("%Y年%m月%d日")
313
+ )
314
+
315
+ followers = info["followers"]
316
+ if followers == None:
317
+ followers = "未知"
318
+ else:
319
+ followers = str(followers)
320
+
321
+ us.append(
322
+ [
323
+ name,
324
+ uid,
325
+ ucreated_at,
326
+ followers,
327
+ ]
328
+ )
329
+
330
+ if hasattr(parser, "output"):
331
+ wb.save(parser.output)
332
+ else:
333
+ wb.save("GithubTotal.xlsx")
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 qiufengcute
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: GithubTotal
3
+ Version: 1.0.0
4
+ Summary: Generate personal GitHub stats report with multi-dimensional analysis.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: github,stats,report,excel,cli,analytics,github-api,repository,insights,developer-tools,productivity
8
+ Author: qiufeng
9
+ Author-email: appleidqiufeng@outlook.com
10
+ Requires-Python: >=3.8,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: System :: Filesystems
25
+ Requires-Dist: openpyxl (>=3.0.0,<4.0.0)
26
+ Requires-Dist: requests (>=2.28.0,<3.0.0)
27
+ Project-URL: Changelog, https://github.com/qiufengcute/GithubTotal/blob/main/CHANGELOG.md
28
+ Project-URL: Homepage, https://github.com/qiufengcute/GithubTotal
29
+ Project-URL: Repository, https://github.com/qiufengcute/GithubTotal
30
+ Description-Content-Type: text/markdown
31
+
32
+ # GitHub Total
33
+
34
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
35
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
36
+ [![Version](https://img.shields.io/pypi/v/GithubTotal.svg)]()
37
+ [![Download](https://img.shields.io/pypi/dm/GithubTotal.svg)]()
38
+
39
+ Generate personal GitHub stats report with multi-dimensional analysis.
40
+
41
+ ## Features
42
+
43
+ - 📊 Generate Excel report with 3 sheets:
44
+ - **Repository Data**: All repos with metadata (creation date, update date, language, stars, etc.)
45
+ - **Statistics**: Summary stats (active repos by period, top language, star leaders, etc.)
46
+ - **User Info**: Basic profile info
47
+ - 🚀 One command, no config needed
48
+ - 📈 Auto-categorize repos by creation/update time (New(This month)/Mid(This year)/Old(Before this year))
49
+ - ⭐ Track your stars (including self-stars)
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install GithubTotal
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ```bash
60
+ ght <username> -o <output.xlsx>
61
+ ```
62
+
@@ -0,0 +1,30 @@
1
+ # GitHub Total
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+ [![Version](https://img.shields.io/pypi/v/GithubTotal.svg)]()
6
+ [![Download](https://img.shields.io/pypi/dm/GithubTotal.svg)]()
7
+
8
+ Generate personal GitHub stats report with multi-dimensional analysis.
9
+
10
+ ## Features
11
+
12
+ - 📊 Generate Excel report with 3 sheets:
13
+ - **Repository Data**: All repos with metadata (creation date, update date, language, stars, etc.)
14
+ - **Statistics**: Summary stats (active repos by period, top language, star leaders, etc.)
15
+ - **User Info**: Basic profile info
16
+ - 🚀 One command, no config needed
17
+ - 📈 Auto-categorize repos by creation/update time (New(This month)/Mid(This year)/Old(Before this year))
18
+ - ⭐ Track your stars (including self-stars)
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install GithubTotal
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```bash
29
+ ght <username> -o <output.xlsx>
30
+ ```
@@ -0,0 +1,40 @@
1
+ [tool.poetry]
2
+ name = "GithubTotal"
3
+ version = "1.0.0"
4
+ description = "Generate personal GitHub stats report with multi-dimensional analysis."
5
+ authors = ["qiufeng <appleidqiufeng@outlook.com>"]
6
+ readme = "README.md"
7
+ license = "MIT"
8
+ keywords = ["github", "stats", "report", "excel", "cli", "analytics", "github-api", "repository", "insights", "developer-tools", "productivity"]
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.8",
12
+ "Programming Language :: Python :: 3.9",
13
+ "Programming Language :: Python :: 3.10",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Development Status :: 5 - Production/Stable",
19
+ "Intended Audience :: Developers",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ "Topic :: System :: Filesystems",
22
+ ]
23
+
24
+ [tool.poetry.scripts]
25
+ ght = "GithubTotal.cli:main"
26
+ github-total = "GithubTotal.cli:main"
27
+
28
+ [tool.poetry.urls]
29
+ "Homepage" = "https://github.com/qiufengcute/GithubTotal"
30
+ "Repository" = "https://github.com/qiufengcute/GithubTotal"
31
+ "Changelog" = "https://github.com/qiufengcute/GithubTotal/blob/main/CHANGELOG.md"
32
+
33
+ [tool.poetry.dependencies]
34
+ python = "^3.8"
35
+ requests = "^2.28.0"
36
+ openpyxl = "^3.0.0"
37
+
38
+ [build-system]
39
+ requires = ["poetry-core"]
40
+ build-backend = "poetry.core.masonry.api"